Mistralys /
application-utils
| 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
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 |