| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 |
- <?php
- namespace Illuminate\Filesystem;
- use Aws\S3\S3Client;
- use Closure;
- use Illuminate\Contracts\Filesystem\Factory as FactoryContract;
- use Illuminate\Support\Arr;
- use InvalidArgumentException;
- use League\Flysystem\AwsS3V3\AwsS3V3Adapter as S3Adapter;
- use League\Flysystem\AwsS3V3\PortableVisibilityConverter as AwsS3PortableVisibilityConverter;
- use League\Flysystem\Filesystem as Flysystem;
- use League\Flysystem\FilesystemAdapter as FlysystemAdapter;
- use League\Flysystem\Ftp\FtpAdapter;
- use League\Flysystem\Ftp\FtpConnectionOptions;
- use League\Flysystem\Local\LocalFilesystemAdapter as LocalAdapter;
- use League\Flysystem\PathPrefixing\PathPrefixedAdapter;
- use League\Flysystem\PhpseclibV3\SftpAdapter;
- use League\Flysystem\PhpseclibV3\SftpConnectionProvider;
- use League\Flysystem\ReadOnly\ReadOnlyFilesystemAdapter;
- use League\Flysystem\UnixVisibility\PortableVisibilityConverter;
- use League\Flysystem\Visibility;
- use function Illuminate\Support\enum_value;
- /**
- * @mixin \Illuminate\Contracts\Filesystem\Filesystem
- * @mixin \Illuminate\Filesystem\FilesystemAdapter
- */
- class FilesystemManager implements FactoryContract
- {
- /**
- * The application instance.
- *
- * @var \Illuminate\Contracts\Foundation\Application
- */
- protected $app;
- /**
- * The array of resolved filesystem drivers.
- *
- * @var array
- */
- protected $disks = [];
- /**
- * The registered custom driver creators.
- *
- * @var array
- */
- protected $customCreators = [];
- /**
- * Create a new filesystem manager instance.
- *
- * @param \Illuminate\Contracts\Foundation\Application $app
- */
- public function __construct($app)
- {
- $this->app = $app;
- }
- /**
- * Get a filesystem instance.
- *
- * @param string|null $name
- * @return \Illuminate\Contracts\Filesystem\Filesystem
- */
- public function drive($name = null)
- {
- return $this->disk($name);
- }
- /**
- * Get a filesystem instance.
- *
- * @param \UnitEnum|string|null $name
- * @return \Illuminate\Contracts\Filesystem\Filesystem
- */
- public function disk($name = null)
- {
- $name = enum_value($name) ?: $this->getDefaultDriver();
- return $this->disks[$name] = $this->get($name);
- }
- /**
- * Get a default cloud filesystem instance.
- *
- * @return \Illuminate\Contracts\Filesystem\Cloud
- */
- public function cloud()
- {
- $name = $this->getDefaultCloudDriver();
- return $this->disks[$name] = $this->get($name);
- }
- /**
- * Build an on-demand disk.
- *
- * @param string|array $config
- * @return \Illuminate\Contracts\Filesystem\Filesystem
- */
- public function build($config)
- {
- return $this->resolve('ondemand', is_array($config) ? $config : [
- 'driver' => 'local',
- 'root' => $config,
- ]);
- }
- /**
- * Attempt to get the disk from the local cache.
- *
- * @param string $name
- * @return \Illuminate\Contracts\Filesystem\Filesystem
- */
- protected function get($name)
- {
- return $this->disks[$name] ?? $this->resolve($name);
- }
- /**
- * Resolve the given disk.
- *
- * @param string $name
- * @param array|null $config
- * @return \Illuminate\Contracts\Filesystem\Filesystem
- *
- * @throws \InvalidArgumentException
- */
- protected function resolve($name, $config = null)
- {
- $config ??= $this->getConfig($name);
- if (empty($config['driver'])) {
- throw new InvalidArgumentException("Disk [{$name}] does not have a configured driver.");
- }
- $driver = $config['driver'];
- if (isset($this->customCreators[$driver])) {
- return $this->callCustomCreator($config);
- }
- $driverMethod = 'create'.ucfirst($driver).'Driver';
- if (! method_exists($this, $driverMethod)) {
- throw new InvalidArgumentException("Driver [{$driver}] is not supported.");
- }
- return $this->{$driverMethod}($config, $name);
- }
- /**
- * Call a custom driver creator.
- *
- * @param array $config
- * @return \Illuminate\Contracts\Filesystem\Filesystem
- */
- protected function callCustomCreator(array $config)
- {
- return $this->customCreators[$config['driver']]($this->app, $config);
- }
- /**
- * Create an instance of the local driver.
- *
- * @param array $config
- * @param string $name
- * @return \Illuminate\Contracts\Filesystem\Filesystem
- */
- public function createLocalDriver(array $config, string $name = 'local')
- {
- $visibility = PortableVisibilityConverter::fromArray(
- $config['permissions'] ?? [],
- $config['directory_visibility'] ?? $config['visibility'] ?? Visibility::PRIVATE
- );
- $links = ($config['links'] ?? null) === 'skip'
- ? LocalAdapter::SKIP_LINKS
- : LocalAdapter::DISALLOW_LINKS;
- $adapter = new LocalAdapter(
- $config['root'], $visibility, $config['lock'] ?? LOCK_EX, $links
- );
- return (new LocalFilesystemAdapter(
- $this->createFlysystem($adapter, $config), $adapter, $config
- ))->diskName(
- $name
- )->shouldServeSignedUrls(
- $config['serve'] ?? false,
- fn () => $this->app['url'],
- );
- }
- /**
- * Create an instance of the ftp driver.
- *
- * @param array $config
- * @return \Illuminate\Contracts\Filesystem\Filesystem
- */
- public function createFtpDriver(array $config)
- {
- if (! isset($config['root'])) {
- $config['root'] = '';
- }
- $adapter = new FtpAdapter(FtpConnectionOptions::fromArray($config));
- return new FilesystemAdapter($this->createFlysystem($adapter, $config), $adapter, $config);
- }
- /**
- * Create an instance of the sftp driver.
- *
- * @param array $config
- * @return \Illuminate\Contracts\Filesystem\Filesystem
- */
- public function createSftpDriver(array $config)
- {
- $provider = SftpConnectionProvider::fromArray($config);
- $root = $config['root'] ?? '';
- $visibility = PortableVisibilityConverter::fromArray(
- $config['permissions'] ?? []
- );
- $adapter = new SftpAdapter($provider, $root, $visibility);
- return new FilesystemAdapter($this->createFlysystem($adapter, $config), $adapter, $config);
- }
- /**
- * Create an instance of the Amazon S3 driver.
- *
- * @param array $config
- * @return \Illuminate\Contracts\Filesystem\Cloud
- */
- public function createS3Driver(array $config)
- {
- $s3Config = $this->formatS3Config($config);
- $root = (string) ($s3Config['root'] ?? '');
- $visibility = new AwsS3PortableVisibilityConverter(
- $config['visibility'] ?? Visibility::PUBLIC
- );
- $streamReads = $s3Config['stream_reads'] ?? false;
- $client = new S3Client($s3Config);
- $adapter = new S3Adapter($client, $s3Config['bucket'], $root, $visibility, null, $config['options'] ?? [], $streamReads);
- return new AwsS3V3Adapter(
- $this->createFlysystem($adapter, $config), $adapter, $s3Config, $client
- );
- }
- /**
- * Format the given S3 configuration with the default options.
- *
- * @param array $config
- * @return array
- */
- protected function formatS3Config(array $config)
- {
- $config += ['version' => 'latest'];
- if (! empty($config['key']) && ! empty($config['secret'])) {
- $config['credentials'] = Arr::only($config, ['key', 'secret']);
- if (! empty($config['token'])) {
- $config['credentials']['token'] = $config['token'];
- }
- }
- return Arr::except($config, ['token']);
- }
- /**
- * Create a scoped driver.
- *
- * @param array $config
- * @return \Illuminate\Contracts\Filesystem\Filesystem
- *
- * @throws \InvalidArgumentException
- */
- public function createScopedDriver(array $config)
- {
- if (empty($config['disk'])) {
- throw new InvalidArgumentException('Scoped disk is missing "disk" configuration option.');
- } elseif (empty($config['prefix'])) {
- throw new InvalidArgumentException('Scoped disk is missing "prefix" configuration option.');
- }
- return $this->build(tap(
- is_string($config['disk']) ? $this->getConfig($config['disk']) : $config['disk'],
- function (&$parent) use ($config) {
- if (empty($parent['prefix'])) {
- $parent['prefix'] = $config['prefix'];
- } else {
- $separator = $parent['directory_separator'] ?? DIRECTORY_SEPARATOR;
- $parentPrefix = rtrim($parent['prefix'], $separator);
- $scopedPrefix = ltrim($config['prefix'], $separator);
- $parent['prefix'] = "{$parentPrefix}{$separator}{$scopedPrefix}";
- }
- if (isset($config['visibility'])) {
- $parent['visibility'] = $config['visibility'];
- }
- if (isset($config['throw'])) {
- $parent['throw'] = $config['throw'];
- }
- }
- ));
- }
- /**
- * Create a Flysystem instance with the given adapter.
- *
- * @param \League\Flysystem\FilesystemAdapter $adapter
- * @param array $config
- * @return \League\Flysystem\FilesystemOperator
- */
- protected function createFlysystem(FlysystemAdapter $adapter, array $config)
- {
- if ($config['read-only'] ?? false) {
- $adapter = new ReadOnlyFilesystemAdapter($adapter);
- }
- if (! empty($config['prefix'])) {
- $adapter = new PathPrefixedAdapter($adapter, $config['prefix']);
- }
- if (str_contains($config['endpoint'] ?? '', 'r2.cloudflarestorage.com')) {
- $config['retain_visibility'] = false;
- }
- return new Flysystem($adapter, Arr::only($config, [
- 'directory_visibility',
- 'disable_asserts',
- 'retain_visibility',
- 'temporary_url',
- 'url',
- 'visibility',
- ]));
- }
- /**
- * Set the given disk instance.
- *
- * @param string $name
- * @param mixed $disk
- * @return $this
- */
- public function set($name, $disk)
- {
- $this->disks[$name] = $disk;
- return $this;
- }
- /**
- * Get the filesystem connection configuration.
- *
- * @param string $name
- * @return array
- */
- protected function getConfig($name)
- {
- return $this->app['config']["filesystems.disks.{$name}"] ?: [];
- }
- /**
- * Get the default driver name.
- *
- * @return string
- */
- public function getDefaultDriver()
- {
- return $this->app['config']['filesystems.default'];
- }
- /**
- * Get the default cloud driver name.
- *
- * @return string
- */
- public function getDefaultCloudDriver()
- {
- return $this->app['config']['filesystems.cloud'] ?? 's3';
- }
- /**
- * Unset the given disk instances.
- *
- * @param array|string $disk
- * @return $this
- */
- public function forgetDisk($disk)
- {
- foreach ((array) $disk as $diskName) {
- unset($this->disks[$diskName]);
- }
- return $this;
- }
- /**
- * Disconnect the given disk and remove from local cache.
- *
- * @param string|null $name
- * @return void
- */
- public function purge($name = null)
- {
- $name ??= $this->getDefaultDriver();
- unset($this->disks[$name]);
- }
- /**
- * Register a custom driver creator Closure.
- *
- * @param string $driver
- * @param \Closure $callback
- * @return $this
- */
- public function extend($driver, Closure $callback)
- {
- $this->customCreators[$driver] = $callback;
- return $this;
- }
- /**
- * Set the application instance used by the manager.
- *
- * @param \Illuminate\Contracts\Foundation\Application $app
- * @return $this
- */
- public function setApplication($app)
- {
- $this->app = $app;
- return $this;
- }
- /**
- * Dynamically call the default driver instance.
- *
- * @param string $method
- * @param array $parameters
- * @return mixed
- */
- public function __call($method, $parameters)
- {
- return $this->disk()->$method(...$parameters);
- }
- }
|