diff --git a/src/Illuminate/Contracts/Filesystem/Filesystem.php b/src/Illuminate/Contracts/Filesystem/Filesystem.php index 00488a2c2367..1b0f18400677 100644 --- a/src/Illuminate/Contracts/Filesystem/Filesystem.php +++ b/src/Illuminate/Contracts/Filesystem/Filesystem.php @@ -2,6 +2,8 @@ namespace Illuminate\Contracts\Filesystem; +use Illuminate\Filesystem\StoragePath; + interface Filesystem { /** @@ -217,4 +219,9 @@ public function makeDirectory($path); * @return bool */ public function deleteDirectory($directory); + + /** + * Get a storage path instance for the given path. + */ + public function at(string $path): StoragePath; } diff --git a/src/Illuminate/Filesystem/FilesystemAdapter.php b/src/Illuminate/Filesystem/FilesystemAdapter.php index 588f8c66f973..4fcd16f59582 100644 --- a/src/Illuminate/Filesystem/FilesystemAdapter.php +++ b/src/Illuminate/Filesystem/FilesystemAdapter.php @@ -1075,6 +1075,14 @@ protected function shouldReport(): bool return (bool) ($this->config['report'] ?? false); } + /** + * Get a storage path instance for the given path. + */ + public function at(string $path): StoragePath + { + return new StoragePath($path, $this); + } + /** * Pass dynamic methods call onto Flysystem. * diff --git a/src/Illuminate/Filesystem/FilesystemManager.php b/src/Illuminate/Filesystem/FilesystemManager.php index 57056e670cb6..42e6439a8cb8 100644 --- a/src/Illuminate/Filesystem/FilesystemManager.php +++ b/src/Illuminate/Filesystem/FilesystemManager.php @@ -96,6 +96,14 @@ public function cloud() return $this->disks[$name] = $this->get($name); } + /** + * Get a storage path instance for the default disk. + */ + public function at(string $path): StoragePath + { + return $this->disk()->at($path); + } + /** * Build an on-demand disk. * diff --git a/src/Illuminate/Filesystem/StoragePath.php b/src/Illuminate/Filesystem/StoragePath.php new file mode 100644 index 000000000000..24ed03b720f6 --- /dev/null +++ b/src/Illuminate/Filesystem/StoragePath.php @@ -0,0 +1,392 @@ +path = $path; + $this->filesystem = $filesystem; + } + + /** + * Determine if a file or directory exists. + */ + public function exists(): bool + { + return $this->filesystem->exists($this->path); + } + + /** + * Determine if a file or directory is missing. + */ + public function missing(): bool + { + if ($this->filesystem instanceof FilesystemAdapter) { + return $this->filesystem->missing($this->path); + } else { + return ! $this->filesystem->exists($this->path); + } + } + + /** + * Get the full path to the file. + */ + public function path(): string + { + return $this->filesystem->path($this->path); + } + + /** + * Get the contents of a file. + */ + public function get(): ?string + { + return $this->filesystem->get($this->path); + } + + /** + * Get a resource to read the file. + * + * @return resource|null The path resource or null on failure. + */ + public function readStream() + { + return $this->filesystem->readStream($this->path); + } + + /** + * Get the contents of a file as decoded JSON. + */ + public function json(int $flags = 0): ?array + { + if ($this->filesystem instanceof FilesystemAdapter) { + return $this->filesystem->json($this->path, $flags); + } + + // NOTE: Copied from FilesystemAdapter to support this method even on + // instanced of FilesystemContract|CloudFilesystemContract + $content = $this->get(); + + return is_null($content) ? null : json_decode($content, true, 512, $flags); + } + + /** + * Get the file size. + */ + public function size(): int + { + return $this->filesystem->size($this->path); + } + + /** + * Get the file's last modification time. + */ + public function lastModified(): int + { + return $this->filesystem->lastModified($this->path); + } + + /** + * Get the checksum for the file. + */ + public function checksum(array $options = []): string|false + { + if ($this->filesystem instanceof FilesystemAdapter) { + return $this->filesystem->checksum($this->path, $options); + } + + return false; + } + + /** + * Get the mime-type of the file. + */ + public function mimeType(): string|false + { + if ($this->filesystem instanceof FilesystemAdapter) { + return $this->filesystem->mimeType($this->path); + } + + return false; + } + + /** + * Write the contents of a file. + * + * @param \Psr\Http\Message\StreamInterface|\Illuminate\Http\File|\Illuminate\Http\UploadedFile|string|resource $contents + * @param mixed $options + */ + public function put($contents, $options = []): bool + { + return $this->filesystem->put($this->path, $contents, $options); + } + + /** + * Write a new file using a stream. + * + * @param resource $resource + */ + public function writeStream($resource, array $options = []): bool + { + return $this->filesystem->writeStream($this->path, $resource, $options); + } + + /** + * Prepend to a file. + */ + public function prepend(string $data, string $separator = PHP_EOL): bool + { + if ($this->filesystem instanceof FilesystemAdapter) { + return $this->filesystem->prepend($this->path, $data, $separator); + } else { + return $this->filesystem->prepend($this->path, $data); + } + } + + /** + * Append to a file. + */ + public function append(string $data, string $separator = PHP_EOL): bool + { + if ($this->filesystem instanceof FilesystemAdapter) { + return $this->filesystem->append($this->path, $data, $separator); + } else { + return $this->filesystem->append($this->path, $data); + } + } + + /** + * Get the visibility for the path. + */ + public function getVisibility(): string + { + return $this->filesystem->getVisibility($this->path); + } + + /** + * Set the visibility for the path. + */ + public function setVisibility(string $visibility): bool + { + return $this->filesystem->setVisibility($this->path, $visibility); + } + + /** + * Delete the file at the path. + */ + public function delete(): bool + { + return $this->filesystem->delete($this->path); + } + + /** + * Copy a file to a new location. + */ + public function copy(string $destination): bool + { + return $this->filesystem->copy($this->path, $destination); + } + + /** + * Copy a file to a new location and return the new path instance. + */ + public function copyTo(string $destination): ?static + { + if ($this->copy($destination)) { + return new static($destination, $this->filesystem); + } + + return null; + } + + /** + * Move a file to a new location. + */ + public function move(string $destination): bool + { + return $this->filesystem->move($this->path, $destination); + } + + /** + * Move a file to a new location and return the new path instance. + */ + public function moveTo(string $destination): ?static + { + if ($this->move($destination)) { + return new static($destination, $this->filesystem); + } + + return null; + } + + /** + * Get an array of all files in the directory. + * + * @return array + */ + public function files(bool $recursive = false): array + { + return $this->filesystem->files($this->path, $recursive); + } + + /** + * Get all of the files from the directory (recursive). + * + * @return array + */ + public function allFiles(): array + { + return $this->filesystem->allFiles($this->path); + } + + /** + * Get all of the directories within the directory. + * + * @return array + */ + public function directories(bool $recursive = false): array + { + return $this->filesystem->directories($this->path, $recursive); + } + + /** + * Get all (recursive) of the directories within the directory. + * + * @return array + */ + public function allDirectories(): array + { + return $this->filesystem->allDirectories($this->path); + } + + /** + * Create a directory. + */ + public function makeDirectory(): bool + { + return $this->filesystem->makeDirectory($this->path); + } + + /** + * Recursively delete a directory. + */ + public function deleteDirectory(): bool + { + return $this->filesystem->deleteDirectory($this->path); + } + + /** + * Get the URL for the file. + */ + public function url(): string + { + if ($this->filesystem instanceof CloudFilesystemContract) { + return $this->filesystem->url($this->path); + } + + throw new RuntimeException('This driver does not support retrieving URLs.'); + } + + /** + * Get a temporary URL for the file. + * + * @param \DateTimeInterface $expiration + */ + public function temporaryUrl($expiration, array $options = []): string + { + if ($this->filesystem instanceof FilesystemAdapter) { + return $this->filesystem->temporaryUrl($this->path, $expiration, $options); + } + + throw new RuntimeException('This driver does not support creating temporary URLs.'); + } + + /** + * Create a streamed response for the file. + */ + public function response(?string $name = null, array $headers = [], ?string $disposition = 'inline'): StreamedResponse + { + if ($this->filesystem instanceof FilesystemAdapter) { + return $this->filesystem->response($this->path, $name, $headers, $disposition); + } + + throw new RuntimeException('This driver does not support responses.'); + } + + /** + * Create a streamed download response for the file. + */ + public function download(?string $name = null, array $headers = []): StreamedResponse + { + if ($this->filesystem instanceof FilesystemAdapter) { + return $this->filesystem->download($this->path, $name, $headers); + } + + throw new RuntimeException('This driver does not support downloads.'); + } + + /** + * Get a new storage path instance for a nested path. + */ + public function at(string $path): static + { + return new static( + $this->path.'/'.ltrim($path, '/'), + $this->filesystem + ); + } + + /** + * Get the path string. + */ + public function getPath(): string + { + return $this->path; + } + + /** + * Get the filesystem instance. + */ + public function getFilesystem(): FilesystemContract|CloudFilesystemContract|FilesystemAdapter + { + return $this->filesystem; + } + + /** + * Dynamically call the filesystem instance. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public function __call($method, $parameters) + { + if (static::hasMacro($method)) { + return $this->macroCall($method, $parameters); + } + + return $this->filesystem->$method($this->path, ...$parameters); + } +} diff --git a/src/Illuminate/Support/Facades/Storage.php b/src/Illuminate/Support/Facades/Storage.php index e4ff1e64fc5b..998293a49f26 100644 --- a/src/Illuminate/Support/Facades/Storage.php +++ b/src/Illuminate/Support/Facades/Storage.php @@ -28,6 +28,7 @@ * @method static string|null get(string $path) * @method static resource|null readStream(string $path) * @method static bool put(string $path, \Psr\Http\Message\StreamInterface|\Illuminate\Http\File|\Illuminate\Http\UploadedFile|string|resource $contents, mixed $options = []) + * @method static \Illuminate\Filesystem\StoragePath at(string $path) * @method static string|false putFile(\Illuminate\Http\File|\Illuminate\Http\UploadedFile|string $path, \Illuminate\Http\File|\Illuminate\Http\UploadedFile|string|array|null $file = null, mixed $options = []) * @method static string|false putFileAs(\Illuminate\Http\File|\Illuminate\Http\UploadedFile|string $path, \Illuminate\Http\File|\Illuminate\Http\UploadedFile|string|array|null $file, string|array|null $name = null, mixed $options = []) * @method static bool writeStream(string $path, resource $resource, array $options = [])