FilesystemManager.php 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. <?php
  2. namespace Illuminate\Filesystem;
  3. use Closure;
  4. use Aws\S3\S3Client;
  5. use OpenCloud\Rackspace;
  6. use Illuminate\Support\Arr;
  7. use InvalidArgumentException;
  8. use League\Flysystem\AdapterInterface;
  9. use League\Flysystem\Sftp\SftpAdapter;
  10. use League\Flysystem\FilesystemInterface;
  11. use League\Flysystem\Cached\CachedAdapter;
  12. use League\Flysystem\Filesystem as Flysystem;
  13. use League\Flysystem\Adapter\Ftp as FtpAdapter;
  14. use League\Flysystem\Rackspace\RackspaceAdapter;
  15. use League\Flysystem\Adapter\Local as LocalAdapter;
  16. use League\Flysystem\AwsS3v3\AwsS3Adapter as S3Adapter;
  17. use League\Flysystem\Cached\Storage\Memory as MemoryStore;
  18. use Illuminate\Contracts\Filesystem\Factory as FactoryContract;
  19. /**
  20. * @mixin \Illuminate\Contracts\Filesystem\Filesystem
  21. */
  22. class FilesystemManager implements FactoryContract
  23. {
  24. /**
  25. * The application instance.
  26. *
  27. * @var \Illuminate\Contracts\Foundation\Application
  28. */
  29. protected $app;
  30. /**
  31. * The array of resolved filesystem drivers.
  32. *
  33. * @var array
  34. */
  35. protected $disks = [];
  36. /**
  37. * The registered custom driver creators.
  38. *
  39. * @var array
  40. */
  41. protected $customCreators = [];
  42. /**
  43. * Create a new filesystem manager instance.
  44. *
  45. * @param \Illuminate\Contracts\Foundation\Application $app
  46. * @return void
  47. */
  48. public function __construct($app)
  49. {
  50. $this->app = $app;
  51. }
  52. /**
  53. * Get a filesystem instance.
  54. *
  55. * @param string $name
  56. * @return \Illuminate\Contracts\Filesystem\Filesystem
  57. */
  58. public function drive($name = null)
  59. {
  60. return $this->disk($name);
  61. }
  62. /**
  63. * Get a filesystem instance.
  64. *
  65. * @param string $name
  66. * @return \Illuminate\Contracts\Filesystem\Filesystem
  67. */
  68. public function disk($name = null)
  69. {
  70. $name = $name ?: $this->getDefaultDriver();
  71. return $this->disks[$name] = $this->get($name);
  72. }
  73. /**
  74. * Get a default cloud filesystem instance.
  75. *
  76. * @return \Illuminate\Contracts\Filesystem\Filesystem
  77. */
  78. public function cloud()
  79. {
  80. $name = $this->getDefaultCloudDriver();
  81. return $this->disks[$name] = $this->get($name);
  82. }
  83. /**
  84. * Attempt to get the disk from the local cache.
  85. *
  86. * @param string $name
  87. * @return \Illuminate\Contracts\Filesystem\Filesystem
  88. */
  89. protected function get($name)
  90. {
  91. return $this->disks[$name] ?? $this->resolve($name);
  92. }
  93. /**
  94. * Resolve the given disk.
  95. *
  96. * @param string $name
  97. * @return \Illuminate\Contracts\Filesystem\Filesystem
  98. *
  99. * @throws \InvalidArgumentException
  100. */
  101. protected function resolve($name)
  102. {
  103. $config = $this->getConfig($name);
  104. if (isset($this->customCreators[$config['driver']])) {
  105. return $this->callCustomCreator($config);
  106. }
  107. $driverMethod = 'create'.ucfirst($config['driver']).'Driver';
  108. if (method_exists($this, $driverMethod)) {
  109. return $this->{$driverMethod}($config);
  110. } else {
  111. throw new InvalidArgumentException("Driver [{$config['driver']}] is not supported.");
  112. }
  113. }
  114. /**
  115. * Call a custom driver creator.
  116. *
  117. * @param array $config
  118. * @return \Illuminate\Contracts\Filesystem\Filesystem
  119. */
  120. protected function callCustomCreator(array $config)
  121. {
  122. $driver = $this->customCreators[$config['driver']]($this->app, $config);
  123. if ($driver instanceof FilesystemInterface) {
  124. return $this->adapt($driver);
  125. }
  126. return $driver;
  127. }
  128. /**
  129. * Create an instance of the local driver.
  130. *
  131. * @param array $config
  132. * @return \Illuminate\Contracts\Filesystem\Filesystem
  133. */
  134. public function createLocalDriver(array $config)
  135. {
  136. $permissions = $config['permissions'] ?? [];
  137. $links = ($config['links'] ?? null) === 'skip'
  138. ? LocalAdapter::SKIP_LINKS
  139. : LocalAdapter::DISALLOW_LINKS;
  140. return $this->adapt($this->createFlysystem(new LocalAdapter(
  141. $config['root'], LOCK_EX, $links, $permissions
  142. ), $config));
  143. }
  144. /**
  145. * Create an instance of the ftp driver.
  146. *
  147. * @param array $config
  148. * @return \Illuminate\Contracts\Filesystem\Filesystem
  149. */
  150. public function createFtpDriver(array $config)
  151. {
  152. return $this->adapt($this->createFlysystem(
  153. new FtpAdapter($config), $config
  154. ));
  155. }
  156. /**
  157. * Create an instance of the sftp driver.
  158. *
  159. * @param array $config
  160. * @return \Illuminate\Contracts\Filesystem\Filesystem
  161. */
  162. public function createSftpDriver(array $config)
  163. {
  164. return $this->adapt($this->createFlysystem(
  165. new SftpAdapter($config), $config
  166. ));
  167. }
  168. /**
  169. * Create an instance of the Amazon S3 driver.
  170. *
  171. * @param array $config
  172. * @return \Illuminate\Contracts\Filesystem\Cloud
  173. */
  174. public function createS3Driver(array $config)
  175. {
  176. $s3Config = $this->formatS3Config($config);
  177. $root = $s3Config['root'] ?? null;
  178. $options = $config['options'] ?? [];
  179. return $this->adapt($this->createFlysystem(
  180. new S3Adapter(new S3Client($s3Config), $s3Config['bucket'], $root, $options), $config
  181. ));
  182. }
  183. /**
  184. * Format the given S3 configuration with the default options.
  185. *
  186. * @param array $config
  187. * @return array
  188. */
  189. protected function formatS3Config(array $config)
  190. {
  191. $config += ['version' => 'latest'];
  192. if ($config['key'] && $config['secret']) {
  193. $config['credentials'] = Arr::only($config, ['key', 'secret']);
  194. }
  195. return $config;
  196. }
  197. /**
  198. * Create an instance of the Rackspace driver.
  199. *
  200. * @param array $config
  201. * @return \Illuminate\Contracts\Filesystem\Cloud
  202. */
  203. public function createRackspaceDriver(array $config)
  204. {
  205. $client = new Rackspace($config['endpoint'], [
  206. 'username' => $config['username'], 'apiKey' => $config['key'],
  207. ]);
  208. $root = $config['root'] ?? null;
  209. return $this->adapt($this->createFlysystem(
  210. new RackspaceAdapter($this->getRackspaceContainer($client, $config), $root), $config
  211. ));
  212. }
  213. /**
  214. * Get the Rackspace Cloud Files container.
  215. *
  216. * @param \OpenCloud\Rackspace $client
  217. * @param array $config
  218. * @return \OpenCloud\ObjectStore\Resource\Container
  219. */
  220. protected function getRackspaceContainer(Rackspace $client, array $config)
  221. {
  222. $urlType = $config['url_type'] ?? null;
  223. $store = $client->objectStoreService('cloudFiles', $config['region'], $urlType);
  224. return $store->getContainer($config['container']);
  225. }
  226. /**
  227. * Create a Flysystem instance with the given adapter.
  228. *
  229. * @param \League\Flysystem\AdapterInterface $adapter
  230. * @param array $config
  231. * @return \League\Flysystem\FilesystemInterface
  232. */
  233. protected function createFlysystem(AdapterInterface $adapter, array $config)
  234. {
  235. $cache = Arr::pull($config, 'cache');
  236. $config = Arr::only($config, ['visibility', 'disable_asserts', 'url']);
  237. if ($cache) {
  238. $adapter = new CachedAdapter($adapter, $this->createCacheStore($cache));
  239. }
  240. return new Flysystem($adapter, count($config) > 0 ? $config : null);
  241. }
  242. /**
  243. * Create a cache store instance.
  244. *
  245. * @param mixed $config
  246. * @return \League\Flysystem\Cached\CacheInterface
  247. *
  248. * @throws \InvalidArgumentException
  249. */
  250. protected function createCacheStore($config)
  251. {
  252. if ($config === true) {
  253. return new MemoryStore;
  254. }
  255. return new Cache(
  256. $this->app['cache']->store($config['store']),
  257. $config['prefix'] ?? 'flysystem',
  258. $config['expire'] ?? null
  259. );
  260. }
  261. /**
  262. * Adapt the filesystem implementation.
  263. *
  264. * @param \League\Flysystem\FilesystemInterface $filesystem
  265. * @return \Illuminate\Contracts\Filesystem\Filesystem
  266. */
  267. protected function adapt(FilesystemInterface $filesystem)
  268. {
  269. return new FilesystemAdapter($filesystem);
  270. }
  271. /**
  272. * Set the given disk instance.
  273. *
  274. * @param string $name
  275. * @param mixed $disk
  276. * @return $this
  277. */
  278. public function set($name, $disk)
  279. {
  280. $this->disks[$name] = $disk;
  281. return $this;
  282. }
  283. /**
  284. * Get the filesystem connection configuration.
  285. *
  286. * @param string $name
  287. * @return array
  288. */
  289. protected function getConfig($name)
  290. {
  291. return $this->app['config']["filesystems.disks.{$name}"];
  292. }
  293. /**
  294. * Get the default driver name.
  295. *
  296. * @return string
  297. */
  298. public function getDefaultDriver()
  299. {
  300. return $this->app['config']['filesystems.default'];
  301. }
  302. /**
  303. * Get the default cloud driver name.
  304. *
  305. * @return string
  306. */
  307. public function getDefaultCloudDriver()
  308. {
  309. return $this->app['config']['filesystems.cloud'];
  310. }
  311. /**
  312. * Unset the given disk instances.
  313. *
  314. * @param array|string $disk
  315. * @return $this
  316. */
  317. public function forgetDisk($disk)
  318. {
  319. foreach ((array) $disk as $diskName) {
  320. unset($this->disks[$diskName]);
  321. }
  322. return $this;
  323. }
  324. /**
  325. * Register a custom driver creator Closure.
  326. *
  327. * @param string $driver
  328. * @param \Closure $callback
  329. * @return $this
  330. */
  331. public function extend($driver, Closure $callback)
  332. {
  333. $this->customCreators[$driver] = $callback;
  334. return $this;
  335. }
  336. /**
  337. * Dynamically call the default driver instance.
  338. *
  339. * @param string $method
  340. * @param array $parameters
  341. * @return mixed
  342. */
  343. public function __call($method, $parameters)
  344. {
  345. return $this->disk()->$method(...$parameters);
  346. }
  347. }