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

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