vendor/league/flysystem/src/Local/LocalFilesystemAdapter.php line 373

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace League\Flysystem\Local;
  4. use function file_put_contents;
  5. use const DIRECTORY_SEPARATOR;
  6. use const LOCK_EX;
  7. use DirectoryIterator;
  8. use FilesystemIterator;
  9. use Generator;
  10. use League\Flysystem\Config;
  11. use League\Flysystem\DirectoryAttributes;
  12. use League\Flysystem\FileAttributes;
  13. use League\Flysystem\FilesystemAdapter;
  14. use League\Flysystem\PathPrefixer;
  15. use League\Flysystem\SymbolicLinkEncountered;
  16. use League\Flysystem\UnableToCopyFile;
  17. use League\Flysystem\UnableToCreateDirectory;
  18. use League\Flysystem\UnableToDeleteDirectory;
  19. use League\Flysystem\UnableToDeleteFile;
  20. use League\Flysystem\UnableToMoveFile;
  21. use League\Flysystem\UnableToReadFile;
  22. use League\Flysystem\UnableToRetrieveMetadata;
  23. use League\Flysystem\UnableToSetVisibility;
  24. use League\Flysystem\UnableToWriteFile;
  25. use League\Flysystem\UnixVisibility\PortableVisibilityConverter;
  26. use League\Flysystem\UnixVisibility\VisibilityConverter;
  27. use League\MimeTypeDetection\FinfoMimeTypeDetector;
  28. use League\MimeTypeDetection\MimeTypeDetector;
  29. use RecursiveDirectoryIterator;
  30. use RecursiveIteratorIterator;
  31. use SplFileInfo;
  32. use function chmod;
  33. use function clearstatcache;
  34. use function dirname;
  35. use function error_clear_last;
  36. use function error_get_last;
  37. use function file_exists;
  38. use function is_dir;
  39. use function is_file;
  40. use function mkdir;
  41. use function rename;
  42. use function stream_copy_to_stream;
  43. class LocalFilesystemAdapter implements FilesystemAdapter
  44. {
  45.     /**
  46.      * @var int
  47.      */
  48.     public const SKIP_LINKS 0001;
  49.     /**
  50.      * @var int
  51.      */
  52.     public const DISALLOW_LINKS 0002;
  53.     /**
  54.      * @var PathPrefixer
  55.      */
  56.     private $prefixer;
  57.     /**
  58.      * @var int
  59.      */
  60.     private $writeFlags;
  61.     /**
  62.      * @var int
  63.      */
  64.     private $linkHandling;
  65.     /**
  66.      * @var VisibilityConverter
  67.      */
  68.     private $visibility;
  69.     /**
  70.      * @var MimeTypeDetector
  71.      */
  72.     private $mimeTypeDetector;
  73.     public function __construct(
  74.         string $location,
  75.         VisibilityConverter $visibility null,
  76.         int $writeFlags LOCK_EX,
  77.         int $linkHandling self::DISALLOW_LINKS,
  78.         MimeTypeDetector $mimeTypeDetector null
  79.     ) {
  80.         $this->prefixer = new PathPrefixer($locationDIRECTORY_SEPARATOR);
  81.         $this->writeFlags $writeFlags;
  82.         $this->linkHandling $linkHandling;
  83.         $this->visibility $visibility ?: new PortableVisibilityConverter();
  84.         $this->ensureDirectoryExists($location$this->visibility->defaultForDirectories());
  85.         $this->mimeTypeDetector $mimeTypeDetector ?: new FinfoMimeTypeDetector();
  86.     }
  87.     public function write(string $pathstring $contentsConfig $config): void
  88.     {
  89.         $this->writeToFile($path$contents$config);
  90.     }
  91.     public function writeStream(string $path$contentsConfig $config): void
  92.     {
  93.         $this->writeToFile($path$contents$config);
  94.     }
  95.     /**
  96.      * @param resource|string $contents
  97.      */
  98.     private function writeToFile(string $path$contentsConfig $config): void
  99.     {
  100.         $prefixedLocation $this->prefixer->prefixPath($path);
  101.         $this->ensureDirectoryExists(
  102.             dirname($prefixedLocation),
  103.             $this->resolveDirectoryVisibility($config->get(Config::OPTION_DIRECTORY_VISIBILITY))
  104.         );
  105.         error_clear_last();
  106.         if (@file_put_contents($prefixedLocation$contents$this->writeFlags) === false) {
  107.             throw UnableToWriteFile::atLocation($patherror_get_last()['message'] ?? '');
  108.         }
  109.         if ($visibility $config->get(Config::OPTION_VISIBILITY)) {
  110.             $this->setVisibility($path, (string) $visibility);
  111.         }
  112.     }
  113.     public function delete(string $path): void
  114.     {
  115.         $location $this->prefixer->prefixPath($path);
  116.         if ( ! file_exists($location)) {
  117.             return;
  118.         }
  119.         error_clear_last();
  120.         if ( ! @unlink($location)) {
  121.             throw UnableToDeleteFile::atLocation($locationerror_get_last()['message'] ?? '');
  122.         }
  123.     }
  124.     public function deleteDirectory(string $prefix): void
  125.     {
  126.         $location $this->prefixer->prefixPath($prefix);
  127.         if ( ! is_dir($location)) {
  128.             return;
  129.         }
  130.         $contents $this->listDirectoryRecursively($locationRecursiveIteratorIterator::CHILD_FIRST);
  131.         /** @var SplFileInfo $file */
  132.         foreach ($contents as $file) {
  133.             if ( ! $this->deleteFileInfoObject($file)) {
  134.                 throw UnableToDeleteDirectory::atLocation($prefix"Unable to delete file at " $file->getPathname());
  135.             }
  136.         }
  137.         unset($contents);
  138.         if ( ! @rmdir($location)) {
  139.             throw UnableToDeleteDirectory::atLocation($prefixerror_get_last()['message'] ?? '');
  140.         }
  141.     }
  142.     private function listDirectoryRecursively(
  143.         string $path,
  144.         int $mode RecursiveIteratorIterator::SELF_FIRST
  145.     ): Generator {
  146.         yield from new RecursiveIteratorIterator(
  147.             new RecursiveDirectoryIterator($pathFilesystemIterator::SKIP_DOTS),
  148.             $mode
  149.         );
  150.     }
  151.     protected function deleteFileInfoObject(SplFileInfo $file): bool
  152.     {
  153.         switch ($file->getType()) {
  154.             case 'dir':
  155.                 return @rmdir((string) $file->getRealPath());
  156.             case 'link':
  157.                 return @unlink((string) $file->getPathname());
  158.             default:
  159.                 return @unlink((string) $file->getRealPath());
  160.         }
  161.     }
  162.     public function listContents(string $pathbool $deep): iterable
  163.     {
  164.         $location $this->prefixer->prefixPath($path);
  165.         if ( ! is_dir($location)) {
  166.             return;
  167.         }
  168.         /** @var SplFileInfo[] $iterator */
  169.         $iterator $deep $this->listDirectoryRecursively($location) : $this->listDirectory($location);
  170.         foreach ($iterator as $fileInfo) {
  171.             if ($fileInfo->isLink()) {
  172.                 if ($this->linkHandling self::SKIP_LINKS) {
  173.                     continue;
  174.                 }
  175.                 throw SymbolicLinkEncountered::atLocation($fileInfo->getPathname());
  176.             }
  177.             $path $this->prefixer->stripPrefix($fileInfo->getPathname());
  178.             $lastModified $fileInfo->getMTime();
  179.             $isDirectory $fileInfo->isDir();
  180.             $permissions octdec(substr(sprintf('%o'$fileInfo->getPerms()), -4));
  181.             $visibility $isDirectory $this->visibility->inverseForDirectory($permissions) : $this->visibility->inverseForFile($permissions);
  182.             yield $isDirectory ? new DirectoryAttributes($path$visibility$lastModified) : new FileAttributes(
  183.                 str_replace('\\''/'$path),
  184.                 $fileInfo->getSize(),
  185.                 $visibility,
  186.                 $lastModified
  187.             );
  188.         }
  189.     }
  190.     public function move(string $sourcestring $destinationConfig $config): void
  191.     {
  192.         $sourcePath $this->prefixer->prefixPath($source);
  193.         $destinationPath $this->prefixer->prefixPath($destination);
  194.         $this->ensureDirectoryExists(
  195.             dirname($destinationPath),
  196.             $this->resolveDirectoryVisibility($config->get(Config::OPTION_DIRECTORY_VISIBILITY))
  197.         );
  198.         if ( ! @rename($sourcePath$destinationPath)) {
  199.             throw UnableToMoveFile::fromLocationTo($sourcePath$destinationPath);
  200.         }
  201.     }
  202.     public function copy(string $sourcestring $destinationConfig $config): void
  203.     {
  204.         $sourcePath $this->prefixer->prefixPath($source);
  205.         $destinationPath $this->prefixer->prefixPath($destination);
  206.         $this->ensureDirectoryExists(
  207.             dirname($destinationPath),
  208.             $this->resolveDirectoryVisibility($config->get(Config::OPTION_DIRECTORY_VISIBILITY))
  209.         );
  210.         if ( ! @copy($sourcePath$destinationPath)) {
  211.             throw UnableToCopyFile::fromLocationTo($sourcePath$destinationPath);
  212.         }
  213.     }
  214.     public function read(string $path): string
  215.     {
  216.         $location $this->prefixer->prefixPath($path);
  217.         error_clear_last();
  218.         $contents = @file_get_contents($location);
  219.         if ($contents === false) {
  220.             throw UnableToReadFile::fromLocation($patherror_get_last()['message'] ?? '');
  221.         }
  222.         return $contents;
  223.     }
  224.     public function readStream(string $path)
  225.     {
  226.         $location $this->prefixer->prefixPath($path);
  227.         error_clear_last();
  228.         $contents = @fopen($location'rb');
  229.         if ($contents === false) {
  230.             throw UnableToReadFile::fromLocation($patherror_get_last()['message'] ?? '');
  231.         }
  232.         return $contents;
  233.     }
  234.     protected function ensureDirectoryExists(string $dirnameint $visibility): void
  235.     {
  236.         if (is_dir($dirname)) {
  237.             return;
  238.         }
  239.         error_clear_last();
  240.         if ( ! @mkdir($dirname$visibilitytrue)) {
  241.             $mkdirError error_get_last();
  242.         }
  243.         clearstatcache(true$dirname);
  244.         if ( ! is_dir($dirname)) {
  245.             $errorMessage = isset($mkdirError['message']) ? $mkdirError['message'] : '';
  246.             throw UnableToCreateDirectory::atLocation($dirname$errorMessage);
  247.         }
  248.     }
  249.     public function fileExists(string $location): bool
  250.     {
  251.         $location $this->prefixer->prefixPath($location);
  252.         return is_file($location);
  253.     }
  254.     public function createDirectory(string $pathConfig $config): void
  255.     {
  256.         $location $this->prefixer->prefixPath($path);
  257.         $visibility $config->get(Config::OPTION_VISIBILITY$config->get(Config::OPTION_DIRECTORY_VISIBILITY));
  258.         $permissions $this->resolveDirectoryVisibility($visibility);
  259.         if (is_dir($location)) {
  260.             $this->setPermissions($location$permissions);
  261.             return;
  262.         }
  263.         error_clear_last();
  264.         if ( ! @mkdir($location$permissionstrue)) {
  265.             throw UnableToCreateDirectory::atLocation($patherror_get_last()['message'] ?? '');
  266.         }
  267.     }
  268.     public function setVisibility(string $pathstring $visibility): void
  269.     {
  270.         $path $this->prefixer->prefixPath($path);
  271.         $visibility is_dir($path) ? $this->visibility->forDirectory($visibility) : $this->visibility->forFile(
  272.             $visibility
  273.         );
  274.         $this->setPermissions($path$visibility);
  275.     }
  276.     public function visibility(string $path): FileAttributes
  277.     {
  278.         $location $this->prefixer->prefixPath($path);
  279.         clearstatcache(false$location);
  280.         error_clear_last();
  281.         $fileperms = @fileperms($location);
  282.         if ($fileperms === false) {
  283.             throw UnableToRetrieveMetadata::visibility($patherror_get_last()['message'] ?? '');
  284.         }
  285.         $permissions $fileperms 0777;
  286.         $visibility $this->visibility->inverseForFile($permissions);
  287.         return new FileAttributes($pathnull$visibility);
  288.     }
  289.     private function resolveDirectoryVisibility(?string $visibility): int
  290.     {
  291.         return $visibility === null $this->visibility->defaultForDirectories() : $this->visibility->forDirectory(
  292.             $visibility
  293.         );
  294.     }
  295.     public function mimeType(string $path): FileAttributes
  296.     {
  297.         $location $this->prefixer->prefixPath($path);
  298.         error_clear_last();
  299.         $mimeType $this->mimeTypeDetector->detectMimeTypeFromFile($location);
  300.         if ($mimeType === null) {
  301.             throw UnableToRetrieveMetadata::mimeType($patherror_get_last()['message'] ?? '');
  302.         }
  303.         return new FileAttributes($pathnullnullnull$mimeType);
  304.     }
  305.     public function lastModified(string $path): FileAttributes
  306.     {
  307.         $location $this->prefixer->prefixPath($path);
  308.         error_clear_last();
  309.         $lastModified = @filemtime($location);
  310.         if ($lastModified === false) {
  311.             throw UnableToRetrieveMetadata::lastModified($patherror_get_last()['message'] ?? '');
  312.         }
  313.         return new FileAttributes($pathnullnull$lastModified);
  314.     }
  315.     public function fileSize(string $path): FileAttributes
  316.     {
  317.         $location $this->prefixer->prefixPath($path);
  318.         error_clear_last();
  319.         if (is_file($location) && ($fileSize = @filesize($location)) !== false) {
  320.             return new FileAttributes($path$fileSize);
  321.         }
  322.         throw UnableToRetrieveMetadata::fileSize($patherror_get_last()['message'] ?? '');
  323.     }
  324.     private function listDirectory(string $location): Generator
  325.     {
  326.         $iterator = new DirectoryIterator($location);
  327.         foreach ($iterator as $item) {
  328.             if ($item->isDot()) {
  329.                 continue;
  330.             }
  331.             yield $item;
  332.         }
  333.     }
  334.     private function setPermissions(string $locationint $visibility): void
  335.     {
  336.         error_clear_last();
  337.         if ( ! @chmod($location$visibility)) {
  338.             $extraMessage error_get_last()['message'] ?? '';
  339.             throw UnableToSetVisibility::atLocation($this->prefixer->stripPrefix($location), $extraMessage);
  340.         }
  341.     }
  342. }