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

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