Laravel 储存 (Storage) 源码分析
储存门面(Storage)
fake
方法
/**
* Replace the given disk with a local testing disk.
*
* @param string|null $disk
*
* @return void
*/
public static function fake($disk = null)
{
$disk = $disk ?: self::$app['config']->get('filesystems.default');
(new Filesystem)->cleanDirectory(
$root = storage_path('framework/testing/disks/'.$disk)
);
static::set($disk, self::createLocalDriver(['root' => $root]));
}
获取储存驱动名称和子路径
$disk = $disk ?: self:$app['config']->get('filesystems.default');
判断是否有传递测试路径 用于保存
删除存在的目录
(new Filesystem)->cleanDirectory(
$root = storage_path('framework/testing/disks/'.$disk)
);
创建文件对象(Filesystem)通过cleanDirectory
方法 来删除已经存在的目录
Filesystem 文件 cleanDirectory
删除代码
/**
* Empty the specified directory of all files and folders.
*
* @param string $directory
* @return bool
*/
public function cleanDirectory($directory)
{
return $this->deleteDirectory($directory, true);
}
在cleanDirectory
可以看到调用自身的deleteDirectory
方法
/**
* Recursively delete a directory.
*
* The directory itself may be optionally preserved.
*
* @param string $directory
* @param bool $preserve
* @return bool
*/
public function deleteDirectory($directory, $preserve = false)
{
if (! $this->isDirectory($directory)) {
return false;
}
$items = new FilesystemIterator($directory);
foreach ($items as $item) {
// If the item is a directory, we can just recurse into the function and
// delete that sub-directory otherwise we'll just delete the file and
// keep iterating through each file until the directory is cleaned.
if ($item->isDir() && ! $item->isLink()) {
$this->deleteDirectory($item->getPathname());
}
// If the item is just a file, we can go ahead and delete it since we're
// just looping through and waxing all of the files in this directory
// and calling directories recursively, so we delete the real path.
else {
$this->delete($item->getPathname());
}
}
if (! $preserve) {
@rmdir($directory);
}
return true;
}
deleteDirectory
方法中的第一行是验证方法是否存在
if (! $this->isDirectory($directory)) {
return false;
}
isDirectory
方法
/**
* Determine if the given path is a directory.
*
* @param string $directory
* @return bool
*/
public function isDirectory($directory)
{
return is_dir($directory);
}
deleteDirectory
第二行代码创建一个 FilesystemIterator
来获取当前目录下所有文件和删除所有文件和目录
$items = new FilesystemIterator($directory);
foreach ($items as $item) {
// If the item is a directory, we can just recurse into the function and
// delete that sub-directory otherwise we'll just delete the file and
// keep iterating through each file until the directory is cleaned.
if ($item->isDir() && ! $item->isLink()) {
$this->deleteDirectory($item->getPathname());
}
// If the item is just a file, we can go ahead and delete it since we're
// just looping through and waxing all of the files in this directory
// and calling directories recursively, so we delete the real path.
else {
$this->delete($item->getPathname());
}
}
- 第一个条件验证是否目录和是否有完整路径, 调用自身递归删除
- 如果不是目录调用自身
delete
方法删除文件和软连接
/**
* Delete the file at a given path.
*
* @param string|array $paths
* @return bool
*/
public function delete($paths)
{
$paths = is_array($paths) ? $paths : func_get_args();
$success = true;
foreach ($paths as $path) {
try {
if (! @unlink($path)) {
$success = false;
}
} catch (ErrorException $e) {
$success = false;
}
}
return $success;
}
创建目录
static::set($disk, self::createLocalDriver(['root' => $root]));
调用 Illuminate\Filesystem\FilesystemManager
类中的方法创建本地测试驱动
/**
* Create an instance of the local driver.
*
* @param array $config
* @return \Illuminate\Contracts\Filesystem\Filesystem
*/
public function createLocalDriver(array $config)
{
$permissions = $config['permissions'] ?? [];
$links = ($config['links'] ?? null) === 'skip'
? LocalAdapter::SKIP_LINKS
: LocalAdapter::DISALLOW_LINKS;
return $this->adapt($this->createFlysystem(new LocalAdapter(
$config['root'], LOCK_EX, $links, $permissions
), $config));
}
获取文件和目录写入的权限
$permissions = $config['permissions'] ?? [];
获取是否有软连接写入
$links = ($config['links'] ?? null) === 'skip'
? LocalAdapter::SKIP_LINKS
: LocalAdapter::DISALLOW_LINKS;
创建一个新的本地文件适配器 (League\Flysystem\Adapter\LocalAdapter
) 类对象
/**
* Constructor.
*
* @param string $root
* @param int $writeFlags
* @param int $linkHandling
* @param array $permissions
*
* @throws LogicException
*/
public function __construct($root, $writeFlags = LOCK_EX, $linkHandling = self::DISALLOW_LINKS, array $permissions = [])
{
$root = is_link($root) ? realpath($root) : $root;
$this->permissionMap = array_replace_recursive(static::$permissions, $permissions);
$this->ensureDirectory($root);
if ( ! is_dir($root) || ! is_readable($root)) {
throw new LogicException('The root path ' . $root . ' is not readable.');
}
$this->setPathPrefix($root);
$this->writeFlags = $writeFlags;
$this->linkHandling = $linkHandling;
}
获取文件绝对路径, 验证是否绝对路径如果不是转换为绝对路径
$root = is_link($root) ? realpath($root) : $root;
获取文件或者文件夹权限
$this->permissionMap = array_replace_recursive(static::$permissions, $permissions);
ensureDirectory
验证目录是否存在不存在重新创建
/**
* Ensure the root directory exists.
*
* @param string $root root directory path
*
* @return void
*
* @throws Exception in case the root directory can not be created
*/
protected function ensureDirectory($root)
{
if ( ! is_dir($root)) {
$umask = umask(0);
if ( ! @mkdir($root, $this->permissionMap['dir']['public'], true)) {
$mkdirError = error_get_last();
}
umask($umask);
clearstatcache(false, $root);
if ( ! is_dir($root)) {
$errorMessage = isset($mkdirError['message']) ? $mkdirError['message'] : '';
throw new Exception(sprintf('Impossible to create the root directory "%s". %s', $root, $errorMessage));
}
}
}
setPathPrefix
设置绝对目录
/**
* Set the path prefix.
*
* @param string $prefix
*
* @return void
*/
public function setPathPrefix($prefix)
{
$prefix = (string) $prefix;
if ($prefix === '') {
$this->pathPrefix = null;
return;
}
$this->pathPrefix = rtrim($prefix, '\\/') . $this->pathSeparator;
}
创建文件对象 createFlysystem
创建代码
$this->createFlysystem(
new LocalAdapter($config['root'], LOCK_EX, $links, $permissions),
$config);
/**
* Create a Flysystem instance with the given adapter.
*
* @param \League\Flysystem\AdapterInterface $adapter
* @param array $config
* @return \League\Flysystem\FilesystemInterface
*/
protected function createFlysystem(AdapterInterface $adapter, array $config)
{
$cache = Arr::pull($config, 'cache');
$config = Arr::only($config, ['visibility', 'disable_asserts', 'url']);
if ($cache) {
$adapter = new CachedAdapter($adapter, $this->createCacheStore($cache));
}
return new Flysystem($adapter, count($config) > 0 ? $config : null);
}
获取配置数据
$cache = Arr::pull($config, 'cache');
$config = Arr::only($config, ['visibility', 'disable_asserts', 'url']);
看是否需要缓存配置, 需要的话创建 缓存适配器
替换之前的 本地适配器
if ($cache) {
$adapter = new CachedAdapter($adapter, $this->createCacheStore($cache));
}
创建 League\Flysystem\Filesystem
return new Flysystem($adapter, count($config) > 0 ? $config : null);
保存使用纪录
static::set($disk, self::createLocalDriver(['root' => $root]));
/**
* Set the given disk instance.
*
* @param string $name
* @param mixed $disk
* @return $this
*/
public function set($name, $disk)
{
$this->disks[$name] = $disk;
return $this;
}
关于 Storage
门面如何映射 FilesystemManager
说明
通过 laravel框架提供 IOC
(控制反转) 来进行映射
由 Illuminate\Support\Facades\Storage
门面方法中 getFacadeAccessor
提供的名称来访问(映射)
其中 Storage
中的 getFacadeAccessor
返回了 filesystem
名称
<?php
namespace Illuminate\Support\Facades;
use Illuminate\Filesystem\Filesystem;
/**
* @method static \Illuminate\Contracts\Filesystem\Filesystem disk(string $name = null)
*
* @see \Illuminate\Filesystem\FilesystemManager
*/
class Storage extends Facade
{
...
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'filesystem';
}
}
然后在 Illuminate\Filesystem\FilesystemServiceProvider
文件服务提供者注册单例
<?php
namespace Illuminate\Filesystem;
use Illuminate\Support\ServiceProvider;
class FilesystemServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->registerNativeFilesystem();
$this->registerFlysystem();
}
/**
* Register the native filesystem implementation.
*
* @return void
*/
protected function registerNativeFilesystem()
{
$this->app->singleton('files', function () {
return new Filesystem;
});
}
/**
* Register the driver based filesystem.
*
* @return void
*/
protected function registerFlysystem()
{
$this->registerManager();
$this->app->singleton('filesystem.disk', function () {
return $this->app['filesystem']->disk($this->getDefaultDriver());
});
$this->app->singleton('filesystem.cloud', function () {
return $this->app['filesystem']->disk($this->getCloudDriver());
});
}
/**
* Register the filesystem manager.
*
* @return void
*/
protected function registerManager()
{
$this->app->singleton('filesystem', function () {
return new FilesystemManager($this->app);
});
}
...
}
说明
由于个人技术知识有限, 在部分说明中可能会出现错误解释, 已经说明不完整的地方, 可以指出帮忙改正, 谢谢
本作品采用《CC 协议》,转载必须注明作者和本文链接
你好:

请教一下,下面图片的这行代码是怎么调用到「
Illuminate\Filesystem\FilesystemManager
」这个类的方法的呢通过 IOC (
Illuminate\Support\Facades\Storage
) 中的返回访问名称来映射和服务提供者注册单例的服务
Illuminate\Filesystem\FilesystemServiceProvider
其中由
registerManager
注册文件储存