Issues (84)

src/FileHelper.php (1 issue)

1
<?php
2
/**
3
 * File containing the {@see AppUtils\FileHelper} class.
4
 * 
5
 * @package Application Utils
6
 * @subpackage FileHelper
7
 * @see FileHelper
8
 */
9
10
declare(strict_types=1);
11
12
namespace AppUtils;
13
14
use AppUtils\FileHelper\AbstractPathInfo;
15
use AppUtils\FileHelper\CLICommandChecker;
16
use AppUtils\FileHelper\FileDownloader;
17
use AppUtils\FileHelper\FileFinder;
18
use AppUtils\FileHelper\FileInfo\NameFixer;
19
use AppUtils\FileHelper\PathRelativizer;
20
use AppUtils\FileHelper\PathsReducer;
21
use AppUtils\FileHelper\FolderInfo;
22
use AppUtils\FileHelper\FolderTree;
23
use AppUtils\FileHelper\FileInfo;
24
use AppUtils\FileHelper\JSONFile;
25
use AppUtils\FileHelper\PathInfoInterface;
26
use AppUtils\FileHelper\PHPFile;
27
use AppUtils\FileHelper\SerializedFile;
28
use AppUtils\FileHelper\UnicodeHandling;
29
use AppUtils\FileHelper\UploadFileSizeInfo;
30
use DateTime;
31
use JsonException;
32
use SplFileInfo;
33
34
/**
35
 * Collection of file system related methods.
36
 * 
37
 * @package Application Utils
38
 * @subpackage FileHelper
39
 * @author Sebastian Mordziol <[email protected]>
40
 */
41
class FileHelper
42
{
43
    public const ERROR_CANNOT_FIND_JSON_FILE = 340001;
44
    public const ERROR_CANNOT_DECODE_JSON_FILE = 340003;
45
    public const ERROR_JSON_ENCODE_ERROR = 340005;
46
    public const ERROR_CANNOT_OPEN_URL = 340008;
47
    public const ERROR_CANNOT_CREATE_FOLDER = 340009;
48
    public const ERROR_FILE_NOT_READABLE = 340010;
49
    public const ERROR_CANNOT_COPY_FILE = 340011;
50
    public const ERROR_CANNOT_DELETE_FILE = 340012;
51
    public const ERROR_FIND_SUBFOLDERS_FOLDER_DOES_NOT_EXIST = 340014;
52
    public const ERROR_UNKNOWN_FILE_MIME_TYPE = 340015;
53
    public const ERROR_SERIALIZED_FILE_CANNOT_BE_READ = 340017;
54
    public const ERROR_SERIALIZED_FILE_UNSERIALZE_FAILED = 340018;
55
    public const ERROR_UNSUPPORTED_OS_CLI_COMMAND = 340019;
56
    public const ERROR_SOURCE_FILE_NOT_FOUND = 340020;
57
    public const ERROR_SOURCE_FILE_NOT_READABLE = 340021;
58
    public const ERROR_TARGET_COPY_FOLDER_NOT_WRITABLE = 340022;
59
    public const ERROR_SAVE_FOLDER_NOT_WRITABLE = 340023;
60
    public const ERROR_SAVE_FILE_NOT_WRITABLE = 340024;
61
    public const ERROR_SAVE_FILE_WRITE_FAILED = 340025;
62
    public const ERROR_FILE_DOES_NOT_EXIST = 340026;
63
    public const ERROR_CANNOT_OPEN_FILE_TO_READ_LINES = 340027;
64
    public const ERROR_CANNOT_READ_FILE_CONTENTS = 340028;
65
    public const ERROR_CURL_OUTPUT_NOT_STRING = 340031;
66
    public const ERROR_CANNOT_OPEN_FILE_TO_DETECT_BOM = 340032;
67
    public const ERROR_FOLDER_DOES_NOT_EXIST = 340033;
68
    public const ERROR_PATH_IS_NOT_A_FOLDER = 340034;
69
    public const ERROR_CANNOT_DELETE_FOLDER = 340036;
70
    public const ERROR_REAL_PATH_NOT_FOUND = 340037;
71
    public const ERROR_PATH_IS_NOT_A_FILE = 340038;
72
    public const ERROR_PATH_NOT_WRITABLE = 340039;
73
    public const ERROR_PATH_INVALID = 340040;
74
    public const ERROR_CANNOT_COPY_FILE_TO_FOLDER = 340041;
75
76
   /**
77
    * Opens a serialized file and returns the unserialized data.
78
    *
79
    * @param string|PathInfoInterface|SplFileInfo $file
80
    * @throws FileHelper_Exception
81
    * @return array<int|string,mixed>
82
    * @see SerializedFile::parse()
83
    * 
84
    * @see FileHelper::ERROR_FILE_DOES_NOT_EXIST
85
    * @see FileHelper::ERROR_SERIALIZED_FILE_CANNOT_BE_READ
86
    * @see FileHelper::ERROR_SERIALIZED_FILE_UNSERIALZE_FAILED
87
    */
88
    public static function parseSerializedFile($file) : array
89
    {
90
        return SerializedFile::factory($file)->parse();
91
    }
92
93
    /**
94
     * Deletes a folder tree with all files therein, including
95
     * the specified folder itself.
96
     *
97
     * @param string|PathInfoInterface|SplFileInfo $rootFolder
98
     * @return bool
99
     * @throws FileHelper_Exception
100
     */
101
    public static function deleteTree($rootFolder) : bool
102
    {
103
        return FolderTree::delete($rootFolder);
104
    }
105
    
106
   /**
107
    * Create a folder, if it does not exist yet.
108
    *  
109
    * @param string|PathInfoInterface $path
110
    * @throws FileHelper_Exception
111
    * @see FileHelper::ERROR_CANNOT_CREATE_FOLDER
112
    */
113
    public static function createFolder($path) : FolderInfo
114
    {
115
        return self::getFolderInfo($path)->create();
116
    }
117
118
    /**
119
     * @param string|PathInfoInterface|SplFileInfo $path
120
     * @return FolderInfo
121
     * @throws FileHelper_Exception
122
     */
123
    public static function getFolderInfo($path) : FolderInfo
124
    {
125
        return FolderInfo::factory($path);
126
    }
127
128
    /**
129
     * Copies a folder tree to the target folder.
130
     *
131
     * @param string|PathInfoInterface|SplFileInfo $source
132
     * @param string|PathInfoInterface|SplFileInfo $target
133
     * @throws FileHelper_Exception
134
     * @see FolderTree
135
     */
136
    public static function copyTree($source, $target) : void
137
    {
138
        FolderTree::copy($source, $target);
139
    }
140
    
141
   /**
142
    * Copies a file to the target location. Includes checks
143
    * for most error sources, like the source file not being
144
    * readable. Automatically creates the target folder if it
145
    * does not exist yet.
146
    * 
147
    * @param string|PathInfoInterface|SplFileInfo $sourcePath
148
    * @param string|PathInfoInterface|SplFileInfo $targetPath
149
    * @throws FileHelper_Exception
150
    * 
151
    * @see FileHelper::ERROR_CANNOT_CREATE_FOLDER
152
    * @see FileHelper::ERROR_SOURCE_FILE_NOT_FOUND
153
    * @see FileHelper::ERROR_SOURCE_FILE_NOT_READABLE
154
    * @see FileHelper::ERROR_TARGET_COPY_FOLDER_NOT_WRITABLE
155
    * @see FileHelper::ERROR_CANNOT_COPY_FILE
156
    */
157
    public static function copyFile($sourcePath, $targetPath) : void
158
    {
159
        self::getFileInfo($sourcePath)->copyTo($targetPath);
160
    }
161
    
162
   /**
163
    * Deletes the target file. Ignored if it cannot be found,
164
    * and throws an exception if it fails.
165
    * 
166
    * @param string|PathInfoInterface|SplFileInfo $filePath
167
    * @throws FileHelper_Exception
168
    * 
169
    * @see FileHelper::ERROR_CANNOT_DELETE_FILE
170
    */
171
    public static function deleteFile($filePath) : void
172
    {
173
        self::getFileInfo($filePath)->delete();
174
    }
175
176
    /**
177
     * Retrieves an instance of the file info class, which
178
     * allows file operations and accessing information on
179
     * the file.
180
     *
181
     * @param string|PathInfoInterface|SplFileInfo $path
182
     * @return FileInfo
183
     * @throws FileHelper_Exception
184
     */
185
    public static function getFileInfo($path) : FileInfo
186
    {
187
        return FileInfo::factory($path);
188
    }
189
190
    /**
191
     * @param string|PathInfoInterface|SplFileInfo $path
192
     * @return PathInfoInterface
193
     * @throws FileHelper_Exception
194
     */
195
    public static function getPathInfo($path) : PathInfoInterface
196
    {
197
        return AbstractPathInfo::resolveType($path);
198
    }
199
200
    /**
201
     * Detects the mime type for the specified file name/path.
202
     * Returns null if it is not a known file extension.
203
     *
204
     * @param string|PathInfoInterface|SplFileInfo $fileName
205
     * @return string|NULL
206
     * @throws FileHelper_Exception
207
     */
208
    public static function detectMimeType($fileName) : ?string
209
    {
210
        $ext = self::getExtension($fileName);
211
        if(empty($ext)) {
212
            return null;
213
        }
214
215
        return FileHelper_MimeTypes::getMime($ext);
216
    }
217
218
    /**
219
     * Like `sendFile()`, but automatically determines whether
220
     * the browser can open the target file type, to either
221
     * send it directly to the browser, or force downloading
222
     * it instead.
223
     *
224
     * @param string|PathInfoInterface|SplFileInfo $filePath
225
     * @param string $fileName
226
     * @throws FileHelper_Exception
227
     */
228
    public static function sendFileAuto($filePath, string $fileName = '') : void
229
    {
230
        $file = FileInfo::factory($filePath)
231
            ->requireExists()
232
            ->requireReadable();
233
234
        self::sendFile(
235
            $file,
236
            $fileName,
237
            !FileHelper_MimeTypes::canBrowserDisplay($file->getExtension())
238
        );
239
    }
240
241
    /**
242
     * Detects the mime type of the target file automatically,
243
     * sends the required headers to trigger a download and
244
     * outputs the file. Returns false if the mime type could
245
     * not be determined.
246
     * 
247
     * @param string|PathInfoInterface|SplFileInfo $filePath
248
     * @param string|null $fileName The name of the file for the client.
249
     * @param bool $asAttachment Whether to force the client to download the file.
250
     * @throws FileHelper_Exception
251
     * 
252
     * @see FileHelper::ERROR_FILE_DOES_NOT_EXIST
253
     * @see FileHelper::ERROR_UNKNOWN_FILE_MIME_TYPE
254
     */
255
    public static function sendFile($filePath, ?string $fileName = null, bool $asAttachment=true) : void
256
    {
257
        self::getFileInfo($filePath)->getDownloader()->send($fileName, $asAttachment);
258
    }
259
260
    /**
261
     * Uses cURL to download the contents of the specified URL,
262
     * returns the content.
263
     *
264
     * @param string $url
265
     * @param int $timeout In seconds. Set to 0 to use the default.
266
     * @param bool $SSLEnabled Whether to enable HTTPs host verification.
267
     * @return string
268
     *
269
     * @throws FileHelper_Exception
270
     * @see FileHelper::ERROR_CANNOT_OPEN_URL
271
     */
272
    public static function downloadFile(string $url, int $timeout=0, bool $SSLEnabled=false) : string
273
    {
274
        return FileDownloader::factory($url)
275
            ->setTimeout($timeout)
276
            ->setSSLEnabled($SSLEnabled)
277
            ->download();
278
    }
279
280
    /**
281
     * Verifies whether the target file is a PHP file. The path
282
     * to the file can be a path to a file as a string, or a
283
     * {@see SplFileInfo} object instance.
284
     *
285
     * @param string|PathInfoInterface|SplFileInfo $filePath
286
     * @return boolean
287
     * @throws FileHelper_Exception
288
     */
289
    public static function isPHPFile($filePath) : bool
290
    {
291
    	return self::getExtension($filePath) === 'php';
292
    }
293
294
    /**
295
     * Retrieves the extension of the specified file. Can be a path
296
     * to a file as a string, or a {@see SplFileInfo} object instance.
297
     *
298
     * NOTE: A folder will return an empty string.
299
     *
300
     * @param string|PathInfoInterface|SplFileInfo $fileName
301
     * @param bool $lowercase
302
     * @return string
303
     * @throws FileHelper_Exception
304
     */
305
    public static function getExtension($fileName, bool $lowercase = true) : string
306
    {
307
        return self::getPathInfo($fileName)->getExtension($lowercase);
308
    }
309
310
    /**
311
     * Retrieves the file name from a path, with or without extension.
312
     * The path to the file can be a string, or a {@see SplFileInfo}
313
     * object instance.
314
     *
315
     * In case of folders, behaves like the "pathinfo" function: returns
316
     * the name of the folder.
317
     *
318
     * @param string|PathInfoInterface|SplFileInfo $pathOrDirIterator
319
     * @param bool $extension
320
     * @return string
321
     * @throws FileHelper_Exception
322
     */
323
    public static function getFilename($pathOrDirIterator, bool $extension = true) : string
324
    {
325
        $info = self::getPathInfo($pathOrDirIterator);
326
327
        if($extension === true || $info instanceof FolderInfo)
328
        {
329
            return $info->getName();
330
        }
331
332
        return $info->requireIsFile()->removeExtension();
333
    }
334
335
    /**
336
     * Tries to read the contents of the target file and
337
     * treat it as JSON to return the decoded JSON data.
338
     *
339
     * @param string|PathInfoInterface|SplFileInfo $file
340
     * @param string $targetEncoding
341
     * @param string|string[]|null $sourceEncoding
342
     * @return array<int|string,mixed>
343
     *
344
     * @throws FileHelper_Exception
345
     * @throws JsonException
346
     * @see FileHelper::ERROR_CANNOT_FIND_JSON_FILE
347
     * @see FileHelper::ERROR_CANNOT_DECODE_JSON_FILE
348
     */
349
    public static function parseJSONFile($file, string $targetEncoding='', $sourceEncoding=null) : array
350
    {
351
        return JSONFile::factory($file)
352
            ->setTargetEncoding($targetEncoding)
353
            ->setSourceEncodings($sourceEncoding)
354
            ->parse();
355
    }
356
    
357
   /**
358
    * Corrects common formatting mistakes when users enter
359
    * file names, like too many spaces, dots and the like.
360
    * 
361
    * NOTE: if the file name contains a path, the path is
362
    * stripped, leaving only the file name.
363
    * 
364
    * @param string $name
365
    * @return string
366
    */
367
    public static function fixFileName(string $name) : string
368
    {
369
        return NameFixer::fixName($name);
370
    }
371
372
    /**
373
     * Creates an instance of the file finder, which is an easier
374
     * alternative to the other manual findFile methods, since all
375
     * options can be set by chaining.
376
     *
377
     * @param string|AbstractPathInfo|SplFileInfo $path
378
     * @return FileFinder
379
     * @throws FileHelper_Exception
380
     *
381
     * @see FileFinder::ERROR_PATH_DOES_NOT_EXIST
382
     */
383
    public static function createFileFinder($path) : FileFinder
384
    {
385
        return new FileFinder($path);
386
    }
387
388
    /**
389
     * Searches for all HTML files in the target folder.
390
     *
391
     * NOTE: This method only exists for backwards compatibility.
392
     * Use the {@see FileHelper::createFileFinder()} method instead,
393
     * which offers an object-oriented interface that is much easier
394
     * to use.
395
     *
396
     * @param string|PathInfoInterface|SplFileInfo $targetFolder
397
     * @param array<string,mixed> $options
398
     * @return string[] An indexed array with files.
399
     * @throws FileHelper_Exception
400
     * @see FileHelper::createFileFinder()
401
     */
402
    public static function findHTMLFiles($targetFolder, array $options=array()) : array
403
    {
404
        return self::findFiles($targetFolder, array('html'), $options);
405
    }
406
407
    /**
408
     * Searches for all PHP files in the target folder.
409
     *
410
     * NOTE: This method only exists for backwards compatibility.
411
     * Use the {@see FileHelper::createFileFinder()} method instead,
412
     * which offers an object-oriented interface that is much easier
413
     * to use.
414
     *
415
     * @param string|PathInfoInterface|SplFileInfo $targetFolder
416
     * @param array<string,mixed> $options
417
     * @return string[] An indexed array of PHP files.
418
     * @throws FileHelper_Exception
419
     * @see FileHelper::createFileFinder()
420
     */
421
    public static function findPHPFiles($targetFolder, array $options=array()) : array
422
    {
423
        return self::findFiles($targetFolder, array('php'), $options);
424
    }
425
    
426
   /**
427
    * Finds files according to the specified options.
428
    * 
429
    * NOTE: This method only exists for backwards compatibility.
430
    * Use the {@see FileHelper::createFileFinder()} method instead,
431
    * which offers an object-oriented interface that is much easier
432
    * to use.
433
    *  
434
    * @param string|PathInfoInterface|SplFileInfo $targetFolder
435
    * @param string[] $extensions
436
    * @param array<string,mixed> $options
437
    * @throws FileHelper_Exception
438
    * @return string[]
439
    *
440
    * @see FileHelper::createFileFinder()
441
    * @deprecated Use the file finder instead.
442
    */
443
    public static function findFiles($targetFolder, array $extensions=array(), array $options=array()) : array
444
    {
445
        $finder = self::createFileFinder($targetFolder);
446
447
        foreach ($extensions as $extension) {
448
            $finder->includeExtension($extension);
449
        }
450
451
        $finder->setPathmodeStrip();
452
        
453
        if(isset($options['relative-path']) && $options['relative-path'] === true) 
454
        {
455
            $finder->setPathmodeRelative();
456
        } 
457
        else if(isset($options['absolute-path']) && $options['absolute-path'] === true)
458
        {
459
            $finder->setPathmodeAbsolute();
460
        }
461
        
462
        if(isset($options['strip-extension'])) 
463
        {
464
            $finder->stripExtensions();
465
        }
466
        
467
        $finder->setOptions($options);
468
        
469
        return $finder->getAll();
470
    }
471
472
    /**
473
     * Removes the extension from the specified path or file name,
474
     * if any, and returns the name without the extension.
475
     *
476
     * @param string|PathInfoInterface|SplFileInfo $filename
477
     * @param bool $keepPath Whether to keep the path component, if any. Default PHP pathinfo behavior is no.
478
     * @return string
479
     * @throws FileHelper_Exception
480
     */
481
    public static function removeExtension($filename, bool $keepPath=false) : string
482
    {
483
        $path = self::getPathInfo($filename);
484
485
        if($path instanceof FileInfo)
486
        {
487
            return $path->removeExtension($keepPath);
488
        }
489
490
        if($keepPath)
491
        {
492
            return $filename;
493
        }
494
495
        return basename($filename);
496
    }
497
498
    /**
499
     * @var UnicodeHandling|NULL
500
     */
501
    private static ?UnicodeHandling $unicodeHandling = null;
502
503
    public static function createUnicodeHandling() : UnicodeHandling
504
    {
505
        if(!isset(self::$unicodeHandling))
506
        {
507
            self::$unicodeHandling = new UnicodeHandling();
508
        }
509
510
        return self::$unicodeHandling;
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::unicodeHandling could return the type null which is incompatible with the type-hinted return AppUtils\FileHelper\UnicodeHandling. Consider adding an additional type-check to rule them out.
Loading history...
511
    }
512
    
513
   /**
514
    * Normalizes the slash style in a file or folder path,
515
    * by replacing any anti-slashes with forward slashes.
516
    * 
517
    * @param string $path
518
    * @return string
519
    */
520
    public static function normalizePath(string $path) : string
521
    {
522
        return str_replace(array('\\', '//'), array('/', '/'), $path);
523
    }
524
525
    /**
526
     * Saves the specified data to a file, JSON encoded.
527
     *
528
     * @param mixed $data
529
     * @param string|PathInfoInterface|SplFileInfo $file
530
     * @param bool $pretty
531
     * @return JSONFile
532
     *
533
     * @throws FileHelper_Exception
534
     * @see FileHelper::ERROR_JSON_ENCODE_ERROR
535
     * @see FileHelper::ERROR_SAVE_FOLDER_NOT_WRITABLE
536
     * @see FileHelper::ERROR_SAVE_FILE_NOT_WRITABLE
537
     * @see FileHelper::ERROR_SAVE_FILE_WRITE_FAILED
538
     */
539
    public static function saveAsJSON($data, $file, bool $pretty=false) : JSONFile
540
    {
541
        return JSONFile::factory($file)->putData($data, $pretty);
542
    }
543
544
    /**
545
     * Saves the specified content to the target file, creating
546
     * the file and the folder as necessary.
547
     *
548
     * @param string|PathInfoInterface|SplFileInfo $filePath
549
     * @param string $content
550
     * @return FileInfo
551
     *
552
     * @throws FileHelper_Exception
553
     * @see FileHelper::ERROR_SAVE_FOLDER_NOT_WRITABLE
554
     * @see FileHelper::ERROR_SAVE_FILE_NOT_WRITABLE
555
     * @see FileHelper::ERROR_SAVE_FILE_WRITE_FAILED
556
     */
557
    public static function saveFile($filePath, string $content='') : FileInfo
558
    {
559
        return self::getFileInfo($filePath)->putContents($content);
560
    }
561
562
    /**
563
     * Checks whether it is possible to run PHP command
564
     * line commands.
565
     *
566
     * @return boolean
567
     * @throws FileHelper_Exception
568
     */
569
    public static function canMakePHPCalls() : bool
570
    {
571
        return self::cliCommandExists('php');
572
    }
573
    
574
    /**
575
     * Determines if a command exists on the current environment's command line interface.
576
     *
577
     * @param string $command The name of the command to check, e.g. "php"
578
     * @return bool True if the command has been found, false otherwise.
579
     * @throws FileHelper_Exception
580
     * @see FileHelper::ERROR_UNSUPPORTED_OS_CLI_COMMAND
581
     */
582
    public static function cliCommandExists(string $command) : bool
583
    {
584
        return CLICommandChecker::factory()->exists($command);
585
    }
586
587
    /**
588
     * Validates a PHP file's syntax.
589
     *
590
     * NOTE: This will fail silently if the PHP command line
591
     * is not available. Use {@link FileHelper::canMakePHPCalls()}
592
     * to check this beforehand as needed.
593
     *
594
     * @param string|PathInfoInterface|SplFileInfo $path
595
     * @return boolean|string[] A boolean true if the file is valid, an array with validation messages otherwise.
596
     * @throws FileHelper_Exception
597
     * @deprecated Use {@see PHPFile::checkSyntax()} instead.
598
     */
599
    public static function checkPHPFileSyntax($path)
600
    {
601
        return PHPFile::factory($path)->checkSyntax();
602
    }
603
604
    /**
605
     * Retrieves the last modified date for the specified file or folder.
606
     *
607
     * Note: If the target does not exist, returns null.
608
     *
609
     * @param string|PathInfoInterface|SplFileInfo $path
610
     * @return DateTime|NULL
611
     * @throws FileHelper_Exception
612
     */
613
    public static function getModifiedDate($path) : ?DateTime
614
    {
615
        return self::getFileInfo($path)->getModifiedDate();
616
    }
617
618
    /**
619
     * Retrieves the names of all sub-folders in the specified path.
620
     *
621
     * Available options:
622
     *
623
     * - recursive: true/false
624
     *   Whether to search for sub-folders recursively.
625
     *
626
     * - absolute-paths: true/false
627
     *   Whether to return a list of absolute paths.
628
     *
629
     * @param string|PathInfoInterface|SplFileInfo $targetFolder
630
     * @param array<string,mixed> $options
631
     * @return string[]
632
     *
633
     * @throws FileHelper_Exception
634
     * @see FileHelper::ERROR_FIND_SUBFOLDERS_FOLDER_DOES_NOT_EXIST
635
     */
636
    public static function getSubfolders($targetFolder, array $options = array()) : array
637
    {
638
        return FolderInfo::factory($targetFolder)
639
            ->createFolderFinder()
640
            ->setOptions($options)
641
            ->getPaths();
642
    }
643
644
   /**
645
    * Retrieves the maximum allowed upload file size, in bytes.
646
    * Takes into account the PHP ini settings <code>post_max_size</code>
647
    * and <code>upload_max_filesize</code>. Since these cannot
648
    * be modified at runtime, they are the hard limits for uploads.
649
    * 
650
    * NOTE: Based on binary values, where 1KB = 1024 Bytes.
651
    * 
652
    * @return int Will return <code>-1</code> if no limit.
653
    */
654
    public static function getMaxUploadFilesize() : int
655
    {
656
        return UploadFileSizeInfo::getFileSize();
657
    }
658
   
659
   /**
660
    * Makes a path relative using a folder depth: will reduce the
661
    * length of the path so that only the amount of folders defined
662
    * in the <code>$depth</code> attribute are shown below the actual
663
    * folder or file in the path.
664
    *  
665
    * @param string  $path The absolute or relative path
666
    * @param int $depth The folder depth to reduce the path to
667
    * @return string
668
    */
669
    public static function relativizePathByDepth(string $path, int $depth=2) : string
670
    {
671
        return PathRelativizer::relativizeByDepth($path, $depth);
672
    }
673
    
674
   /**
675
    * Makes the specified path relative to another path,
676
    * by removing one from the other if found. Also 
677
    * normalizes the path to use forward slashes. 
678
    * 
679
    * Example:
680
    * 
681
    * <pre>
682
    * relativizePath('c:\some\folder\to\file.txt', 'c:\some\folder');
683
    * </pre>
684
    * 
685
    * Result: <code>to/file.txt</code>
686
    * 
687
    * @param string $path
688
    * @param string $relativeTo
689
    * @return string
690
    */
691
    public static function relativizePath(string $path, string $relativeTo) : string
692
    {
693
        return PathRelativizer::relativize($path, $relativeTo);
694
    }
695
    
696
   /**
697
    * Checks that the target file exists, and throws an exception
698
    * if it does not. 
699
    * 
700
    * @param string|SplFileInfo $path
701
    * @param int|NULL $errorCode Optional custom error code
702
    * @throws FileHelper_Exception
703
    * @return string The real path to the file
704
    * 
705
    * @see FileHelper::ERROR_FILE_DOES_NOT_EXIST
706
    * @see FileHelper::ERROR_REAL_PATH_NOT_FOUND
707
    */
708
    public static function requireFileExists($path, ?int $errorCode=null) : string
709
    {
710
        return self::getPathInfo($path)
711
            ->requireIsFile()
712
            ->requireExists($errorCode)
713
            ->getRealPath();
714
    }
715
716
    /**
717
     * @param string|PathInfoInterface|SplFileInfo $path
718
     * @param int|NULL $errorCode
719
     * @return string
720
     * @throws FileHelper_Exception
721
     */
722
    public static function requireFileReadable($path, ?int $errorCode=null) : string
723
    {
724
        return self::getPathInfo($path)
725
            ->requireIsFile()
726
            ->requireReadable($errorCode)
727
            ->getPath();
728
    }
729
    
730
   /**
731
    * Reads a specific line number from the target file and returns its
732
    * contents, if the file has such a line. Does so with little memory
733
    * usage, as the file is not read entirely into memory.
734
    * 
735
    * @param string|PathInfoInterface|SplFileInfo $path
736
    * @param int $lineNumber Note: 1-based; the first line is number 1.
737
    * @return string|NULL Will return null if the requested line does not exist.
738
    * @throws FileHelper_Exception
739
    * 
740
    * @see FileHelper::ERROR_FILE_DOES_NOT_EXIST
741
    */
742
    public static function getLineFromFile($path, int $lineNumber) : ?string
743
    {
744
        return self::getFileInfo($path)->getLine($lineNumber);
745
    }
746
747
    /**
748
     * Retrieves the total amount of lines in the file, without
749
     * reading the whole file into memory.
750
     *
751
     * @param string|PathInfoInterface|SplFileInfo $path
752
     * @return int
753
     * @throws FileHelper_Exception
754
     */
755
    public static function countFileLines($path) : int
756
    {
757
        return self::getFileInfo($path)->countLines();
758
    }
759
760
    /**
761
     * Parses the target file to detect any PHP classes contained
762
     * within, and retrieve information on them. Does not use the
763
     * PHP reflection API.
764
     *
765
     * @param string|PathInfoInterface|SplFileInfo $filePath
766
     * @return FileHelper_PHPClassInfo
767
     * @throws FileHelper_Exception
768
     */
769
    public static function findPHPClasses($filePath) : FileHelper_PHPClassInfo
770
    {
771
        return PHPFile::factory($filePath)->findClasses();
772
    }
773
774
    /**
775
     * Detects the end of line style used in the target file, if any.
776
     * Can be used with large files, because it only reads part of it.
777
     *
778
     * @param string|PathInfoInterface|SplFileInfo $filePath The path to the file.
779
     * @return NULL|ConvertHelper_EOL The end of line character information, or NULL if none is found.
780
     * @throws FileHelper_Exception
781
     */
782
    public static function detectEOLCharacter($filePath) : ?ConvertHelper_EOL
783
    {
784
        return self::getFileInfo($filePath)->detectEOLCharacter();
785
    }
786
787
    /**
788
     * Reads the specified amount of lines from the target file.
789
     * Unicode BOM compatible: any byte order marker is stripped
790
     * from the resulting lines.
791
     *
792
     * @param string|PathInfoInterface|SplFileInfo $filePath
793
     * @param int $amount Set to 0 to read all lines.
794
     * @return string[]
795
     *
796
     * @throws FileHelper_Exception
797
     * @see FileHelper::ERROR_FILE_DOES_NOT_EXIST
798
     * @see FileHelper::ERROR_CANNOT_OPEN_FILE_TO_READ_LINES
799
     */
800
    public static function readLines($filePath, int $amount=0) : array
801
    {
802
        return self::getFileInfo($filePath)
803
            ->getLineReader()
804
            ->getLines($amount);
805
    }
806
    
807
   /**
808
    * Reads all content from a file.
809
    * 
810
    * @param string|PathInfoInterface|SplFileInfo $filePath
811
    * @throws FileHelper_Exception
812
    * @return string
813
    * 
814
    * @see FileHelper::ERROR_FILE_DOES_NOT_EXIST
815
    * @see FileHelper::ERROR_CANNOT_READ_FILE_CONTENTS
816
    */
817
    public static function readContents($filePath) : string
818
    {
819
        return self::getFileInfo($filePath)->getContents();
820
    }
821
822
   /**
823
    * Ensures that the target path exists on disk, and is a folder.
824
    * 
825
    * @param string|PathInfoInterface|SplFileInfo $path
826
    * @return string The real path, with normalized slashes.
827
    * @throws FileHelper_Exception
828
    * 
829
    * @see FileHelper::normalizePath()
830
    * 
831
    * @see FileHelper::ERROR_FOLDER_DOES_NOT_EXIST
832
    * @see FileHelper::ERROR_PATH_IS_NOT_A_FOLDER
833
    */
834
    public static function requireFolderExists($path) : string
835
    {
836
        return self::getFolderInfo($path)
837
            ->requireExists(self::ERROR_FOLDER_DOES_NOT_EXIST)
838
            ->getRealPath();
839
    }
840
841
    /**
842
     * Creates an instance of the path reducer tool, which can reduce
843
     * a list of paths to the closest common root folder.
844
     *
845
     * @param string[] $paths
846
     * @return PathsReducer
847
     *
848
     * @throws FileHelper_Exception
849
     */
850
    public static function createPathsReducer(array $paths=array()) : PathsReducer
851
    {
852
        return new PathsReducer($paths);
853
    }
854
}
855