<?php 
 
namespace League\Flysystem; 
 
use InvalidArgumentException; 
use League\Flysystem\FilesystemNotFoundException; 
use League\Flysystem\Plugin\PluggableTrait; 
use League\Flysystem\Plugin\PluginNotFoundException; 
 
/** 
 * Class MountManager. 
 * 
 * Proxies methods to Filesystem (@see __call): 
 * 
 * @method AdapterInterface getAdapter($prefix) 
 * @method Config getConfig($prefix) 
 * @method bool has($path) 
 * @method bool write($path, $contents, array $config = []) 
 * @method bool writeStream($path, $resource, array $config = []) 
 * @method bool put($path, $contents, $config = []) 
 * @method bool putStream($path, $contents, $config = []) 
 * @method string readAndDelete($path) 
 * @method bool update($path, $contents, $config = []) 
 * @method bool updateStream($path, $resource, $config = []) 
 * @method string|false read($path) 
 * @method resource|false readStream($path) 
 * @method bool rename($path, $newpath) 
 * @method bool delete($path) 
 * @method bool deleteDir($dirname) 
 * @method bool createDir($dirname, $config = []) 
 * @method array listFiles($directory = '', $recursive = false) 
 * @method array listPaths($directory = '', $recursive = false) 
 * @method array getWithMetadata($path, array $metadata) 
 * @method string|false getMimetype($path) 
 * @method string|false getTimestamp($path) 
 * @method string|false getVisibility($path) 
 * @method int|false getSize($path); 
 * @method bool setVisibility($path, $visibility) 
 * @method array|false getMetadata($path) 
 * @method Handler get($path, Handler $handler = null) 
 * @method Filesystem flushCache() 
 * @method void assertPresent($path) 
 * @method void assertAbsent($path) 
 * @method Filesystem addPlugin(PluginInterface $plugin) 
 */ 
class MountManager 
{ 
    use PluggableTrait; 
 
    /** 
     * @var FilesystemInterface[] 
     */ 
    protected $filesystems = []; 
 
    /** 
     * Constructor. 
     * 
     * @param FilesystemInterface[] $filesystems [:prefix => Filesystem,] 
     * 
     * @throws InvalidArgumentException 
     */ 
    public function __construct(array $filesystems = []) 
    { 
        $this->mountFilesystems($filesystems); 
    } 
 
    /** 
     * Mount filesystems. 
     * 
     * @param FilesystemInterface[] $filesystems [:prefix => Filesystem,] 
     * 
     * @throws InvalidArgumentException 
     * 
     * @return $this 
     */ 
    public function mountFilesystems(array $filesystems) 
    { 
        foreach ($filesystems as $prefix => $filesystem) { 
            $this->mountFilesystem($prefix, $filesystem); 
        } 
 
        return $this; 
    } 
 
    /** 
     * Mount filesystems. 
     * 
     * @param string              $prefix 
     * @param FilesystemInterface $filesystem 
     * 
     * @throws InvalidArgumentException 
     * 
     * @return $this 
     */ 
    public function mountFilesystem($prefix, FilesystemInterface $filesystem) 
    { 
        if ( ! is_string($prefix)) { 
            throw new InvalidArgumentException(__METHOD__ . ' expects argument #1 to be a string.'); 
        } 
 
        $this->filesystems[$prefix] = $filesystem; 
 
        return $this; 
    } 
 
    /** 
     * Get the filesystem with the corresponding prefix. 
     * 
     * @param string $prefix 
     * 
     * @throws FilesystemNotFoundException 
     * 
     * @return FilesystemInterface 
     */ 
    public function getFilesystem($prefix) 
    { 
        if ( ! isset($this->filesystems[$prefix])) { 
            throw new FilesystemNotFoundException('No filesystem mounted with prefix ' . $prefix); 
        } 
 
        return $this->filesystems[$prefix]; 
    } 
 
    /** 
     * Retrieve the prefix from an arguments array. 
     * 
     * @param array $arguments 
     * 
     * @throws InvalidArgumentException 
     * 
     * @return array [:prefix, :arguments] 
     */ 
    public function filterPrefix(array $arguments) 
    { 
        if (empty($arguments)) { 
            throw new InvalidArgumentException('At least one argument needed'); 
        } 
 
        $path = array_shift($arguments); 
 
        if ( ! is_string($path)) { 
            throw new InvalidArgumentException('First argument should be a string'); 
        } 
 
        list($prefix, $path) = $this->getPrefixAndPath($path); 
        array_unshift($arguments, $path); 
 
        return [$prefix, $arguments]; 
    } 
 
    /** 
     * @param string $directory 
     * @param bool   $recursive 
     * 
     * @throws InvalidArgumentException 
     * @throws FilesystemNotFoundException 
     * 
     * @return array 
     */ 
    public function listContents($directory = '', $recursive = false) 
    { 
        list($prefix, $directory) = $this->getPrefixAndPath($directory); 
        $filesystem = $this->getFilesystem($prefix); 
        $result = $filesystem->listContents($directory, $recursive); 
 
        foreach ($result as &$file) { 
            $file['filesystem'] = $prefix; 
        } 
 
        return $result; 
    } 
 
    /** 
     * Call forwarder. 
     * 
     * @param string $method 
     * @param array  $arguments 
     * 
     * @throws InvalidArgumentException 
     * @throws FilesystemNotFoundException 
     * 
     * @return mixed 
     */ 
    public function __call($method, $arguments) 
    { 
        list($prefix, $arguments) = $this->filterPrefix($arguments); 
 
        return $this->invokePluginOnFilesystem($method, $arguments, $prefix); 
    } 
 
    /** 
     * @param string $from 
     * @param string $to 
     * @param array  $config 
     * 
     * @throws InvalidArgumentException 
     * @throws FilesystemNotFoundException 
     * 
     * @return bool 
     */ 
    public function copy($from, $to, array $config = []) 
    { 
        list($prefixFrom, $from) = $this->getPrefixAndPath($from); 
 
        $buffer = $this->getFilesystem($prefixFrom)->readStream($from); 
 
        if ($buffer === false) { 
            return false; 
        } 
 
        list($prefixTo, $to) = $this->getPrefixAndPath($to); 
 
        $result = $this->getFilesystem($prefixTo)->writeStream($to, $buffer, $config); 
 
        if (is_resource($buffer)) { 
            fclose($buffer); 
        } 
 
        return $result; 
    } 
 
    /** 
     * List with plugin adapter. 
     * 
     * @param array  $keys 
     * @param string $directory 
     * @param bool   $recursive 
     * 
     * @throws InvalidArgumentException 
     * @throws FilesystemNotFoundException 
     * 
     * @return array 
     */ 
    public function listWith(array $keys = [], $directory = '', $recursive = false) 
    { 
        list($prefix, $directory) = $this->getPrefixAndPath($directory); 
        $arguments = [$keys, $directory, $recursive]; 
 
        return $this->invokePluginOnFilesystem('listWith', $arguments, $prefix); 
    } 
 
    /** 
     * Move a file. 
     * 
     * @param string $from 
     * @param string $to 
     * @param array  $config 
     * 
     * @throws InvalidArgumentException 
     * @throws FilesystemNotFoundException 
     * 
     * @return bool 
     */ 
    public function move($from, $to, array $config = []) 
    { 
        $copied = $this->copy($from, $to, $config); 
 
        if ($copied) { 
            return $this->delete($from); 
        } 
 
        return false; 
    } 
 
    /** 
     * Invoke a plugin on a filesystem mounted on a given prefix. 
     * 
     * @param string $method 
     * @param array  $arguments 
     * @param string $prefix 
     * 
     * @throws FilesystemNotFoundException 
     * 
     * @return mixed 
     */ 
    public function invokePluginOnFilesystem($method, $arguments, $prefix) 
    { 
        $filesystem = $this->getFilesystem($prefix); 
 
        try { 
            return $this->invokePlugin($method, $arguments, $filesystem); 
        } catch (PluginNotFoundException $e) { 
            // Let it pass, it's ok, don't panic. 
        } 
 
        $callback = [$filesystem, $method]; 
 
        return call_user_func_array($callback, $arguments); 
    } 
 
    /** 
     * @param string $path 
     * 
     * @throws InvalidArgumentException 
     * 
     * @return string[] [:prefix, :path] 
     */ 
    protected function getPrefixAndPath($path) 
    { 
        if (strpos($path, '://') < 1) { 
            throw new InvalidArgumentException('No prefix detected in path: ' . $path); 
        } 
 
        return explode('://', $path, 2); 
    } 
} 
 
 |