摘要:說明本文主要學習的模塊的源碼邏輯,把自己的一點點研究心得分享出來,希望對別人有所幫助。實際上,使用了的重載學習筆記之重載,通過魔術方法調用里的,而這個實際上就是,該中有方法,可以調用。
說明:本文主要學習Laravel的Filesystem模塊的源碼邏輯,把自己的一點點研究心得分享出來,希望對別人有所幫助??偟膩碚f,Filesystem模塊的源碼也比較簡單,Laravel的IlluminateFilesystem模塊主要依賴于LeagueFlysystem這個Filesystem Abstractor Layer,類似于是LeagueFlysystem的Laravel Bridge。而不同的Filesystem SDK有著各自的具體增刪改查邏輯,如AWS S3 SDK,Dropbox SDK,這些SDK都是通過Adapter Pattern裝載入這個Filesystem Abstractor Layer。Filesystem模塊的整體架構如下兩張圖:
開發環境:Laravel5.2+MAMP+PHP7+MySQL5.6
1. IlluminateFilesystemFilesystemServiceProviderLaravel中每一個Service模塊都有對應的ServiceProvider,主要幫助把該Service注冊到Container中,方便在應用程序中利用Facade調用該Service。同樣,Filesystem Service有對應的FilesystemServiceProvider,幫助注冊files和filesystem等Service:
// IlluminateFilesystem $this->app->singleton("files", function () { return new Filesystem; }); $this->app->singleton("filesystem", function () { return new FilesystemManager($this->app); });
使用Container的singleton單例注冊,同時還注冊了filesystem.disk(config/filesystems.php的default配置選項)和filesystem.cloud(config/filesystems.php的cloud配置選項)。其中,files的Facade為IlluminateSupportFacadesFile,filesystem的Facade為IlluminateSupportFacadesFilesystem。
2. IlluminateFilesystemFilesystemManagerLaravel官網上有類似這樣代碼:
// Recursively List下AWS S3上路徑為dir/to的所有文件,迭代所有的文件和文件夾下的文件 $s3AllFiles = Storage::disk("s3")->allFiles("dir/to"); // Check S3 上dir/to/filesystem.png該文件是否存在 $s3AllFiles = Storage::disk("s3")->exists("dir/to/filesystem.png");
那這樣的代碼內部實現邏輯是怎樣的呢?
翻一下IlluminateFilesystemFilesystemManager代碼就很容易知道了。首先Storage::disk()是利用了Facade模式,Storage是名為filesystem的Facade,而filesystem從上文知道實際是FilesystemManager的對象,所以可以看做(new FilesystemManager)->disk(),看disk()方法源碼:
// IlluminateFilesystemFilesystemManager /** * Get a filesystem instance. * * @param string $name * @return IlluminateContractsFilesystemFilesystem */ public function disk($name = null) { // 如果不傳參,就默認filesystems.default的配置 $name = $name ?: $this->getDefaultDriver(); // 這里傳s3,$this->get("s3")取S3 driver return $this->disks[$name] = $this->get($name); } /** * Get the default driver name. * * @return string */ public function getDefaultDriver() { return $this->app["config"]["filesystems.default"]; } /** * Attempt to get the disk from the local cache. * * @param string $name * @return IlluminateContractsFilesystemFilesystem */ protected function get($name) { // PHP7里可以這樣簡潔的寫 $this->disks[$name] ?? $this->resolve($name); return isset($this->disks[$name]) ? $this->disks[$name] : $this->resolve($name); } /** * Resolve the given disk. * * @param string $name * @return IlluminateContractsFilesystemFilesystem * * @throws InvalidArgumentException */ protected function resolve($name) { // 取出S3的配置 $config = $this->getConfig($name); // 檢查自定義驅動中是否已經提前定義了,自定義是通過extend($driver, Closure $callback)定制化driver, // 如果已經定義則取出定制化driver,下文介紹 if (isset($this->customCreators[$config["driver"]])) { return $this->callCustomCreator($config); } // 這里有個巧妙的技巧,檢查IlluminateFilesystemFilesystemManager中是否有createS3Driver這個方法, // 有的話代入$config參數執行該方法,看createS3Driver()方法 $driverMethod = "create".ucfirst($config["driver"])."Driver"; if (method_exists($this, $driverMethod)) { return $this->{$driverMethod}($config); } else { throw new InvalidArgumentException("Driver [{$config["driver"]}] is not supported."); } } /** * Get the filesystem connection configuration. * * @param string $name * @return array */ protected function getConfig($name) { return $this->app["config"]["filesystems.disks.{$name}"]; } /** * Create an instance of the Amazon S3 driver. * * @param array $config * @return IlluminateContractsFilesystemCloud */ public function createS3Driver(array $config) { $s3Config = $this->formatS3Config($config); $root = isset($s3Config["root"]) ? $s3Config["root"] : null; $options = isset($config["options"]) ? $config["options"] : []; // use LeagueFlysystemAwsS3v3AwsS3Adapter as S3Adapter,這里用了LeagueFlysystemFilesystem, // 上文說過Laravel的Filesystem只是個Filesystem Bridge,實際上用的是LeagueFlysystem這個依賴。 // LeagueFlysystem源碼解析會在下篇中講述, // 主要使用了Adapter Pattern把各個Filesystem SDK 整合到一個LeagueFlysystemFilesystemInterface實例中, // 有幾個核心概念:Adapters, Relative Path, Files First, Plugin, MountManager(File Shortcut), Cache。 // 下面代碼類似于 // (new IlluminateFilesystemFilesystemAdapter( // new LeagueFlysystemFilesystem( // new S3Adapter(new S3Client(), $options), $config) // ) // )) return $this->adapt($this->createFlysystem( new S3Adapter(new S3Client($s3Config), $s3Config["bucket"], $root, $options), $config )); } /** * Create a Flysystem instance with the given adapter. * * @param LeagueFlysystemAdapterInterface $adapter * @param array $config * @return LeagueFlysystemFlysystemInterface */ protected function createFlysystem(AdapterInterface $adapter, array $config) { $config = Arr::only($config, ["visibility", "disable_asserts"]); // use LeagueFlysystemFilesystem as Flysystem return new Flysystem($adapter, count($config) > 0 ? $config : null); } /** * Adapt the filesystem implementation. * * @param LeagueFlysystemFilesystemInterface $filesystem * @return IlluminateContractsFilesystemFilesystem */ protected function adapt(FilesystemInterface $filesystem) { return new FilesystemAdapter($filesystem); }
通過代碼里注釋,可以看出Storage::disk("s3")實際上返回的是這樣一段類似代碼:
(new IlluminateFilesystemFilesystemAdapter(new LeagueFlysystemFilesystem(new S3Adapter(new S3Client(), $options), $config))))
所以,Storage::disk("s3")->allFiles($parameters)或者Storage::disk("s3")->exists($parameters),實際上調用的是IlluminateFilesystemFilesystemAdapter這個對象的allFiles($parameters)和exists($parameters)方法。
3. IlluminateFilesystemFilesystemAdapter查看FilesystemAdapter的源碼,提供了關于filesystem的增刪改查的一系列方法:
/** * Determine if a file exists. * * @param string $path * @return bool */ public function exists($path) { // 實際上又是調用的driver的has()方法,$driver又是LeagueFlysystemFilesystem對象, // 查看LeagueFlysystemFilesystem對象的has()方法, // 實際上是通過LeagueFlysystemAwsS3v3AwsS3Adapter的has()方法, // 當然最后調用的是AWS S3 SDK包的(new S3Client())->doesObjectExist($parameters)檢查S3上該文件是否存在 return $this->driver->has($path); } /** * Get all of the files from the given directory (recursive). * * @param string|null $directory * @return array */ public function allFiles($directory = null) { return $this->files($directory, true); } /** * Get an array of all files in a directory. * * @param string|null $directory * @param bool $recursive * @return array */ public function files($directory = null, $recursive = false) { $contents = $this->driver->listContents($directory, $recursive); return $this->filterContentsByType($contents, "file"); } /** * Pass dynamic methods call onto Flysystem. * * @param string $method * @param array $parameters * @return mixed * * @throws BadMethodCallException */ public function __call($method, array $parameters) { return call_user_func_array([$this->driver, $method], $parameters); }
通過代碼注釋知道,Storage::disk("s3")->exists($parameters)實際上最后通過調用S3 SDK的(new S3Client())->doesObjectExist($parameters)檢查S3上有沒有該文件,Storage::disk("s3")->allFiles($parameters)也是同理,通過調用(new LeagueFlysystemAwsS3v3AwsS3Adapter(new S3Client(), $config))->listContents()來list contents of a dir.
根據上文的解釋,那Storage::disk("s3")->readStream($path)可以調用不?
可以的。實際上,IlluminateFilesystemFilesystemAdapter使用了PHP的重載(Laravel學習筆記之PHP重載(overloading)),通過__call($method, $parameters)魔術方法調用$driver里的$method,而這個$driver實際上就是(new LeagueFlysystemFilesystem),該Filesystem Abstract Layer中有readStream方法,可以調用。
Laravelgu官網中介紹通過Storage::extend($driver, Closure $callback)來自定義driver,這里我們知道實際上調用的是(new IlluminateFilesystemFilesystemManager($parameters))->extend($driver, Closure $callback),上文中提到該對象的resolve($name)代碼時會先檢查自定義驅動有沒有,有的話調用自定義驅動,再看下resolve()代碼:
/** * Resolve the given disk. * * @param string $name * @return IlluminateContractsFilesystemFilesystem * * @throws InvalidArgumentException */ protected function resolve($name) { $config = $this->getConfig($name); // 檢查自動以驅動是否存在,存在的話,調用callCustomCreator來解析出$driver if (isset($this->customCreators[$config["driver"]])) { return $this->callCustomCreator($config); } $driverMethod = "create".ucfirst($config["driver"])."Driver"; if (method_exists($this, $driverMethod)) { return $this->{$driverMethod}($config); } else { throw new InvalidArgumentException("Driver [{$config["driver"]}] is not supported."); } } /** * Call a custom driver creator. * * @param array $config * @return IlluminateContractsFilesystemFilesystem */ protected function callCustomCreator(array $config) { $driver = $this->customCreators[$config["driver"]]($this->app, $config); if ($driver instanceof FilesystemInterface) { return $this->adapt($driver); } return $driver; }
extend()方法就是把自定義的驅動注冊進$customCreators里:
/** * 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; }
總結:上篇主要講述了Laravel Filesystem Bridge,該Bridge只是把League/Flysystem這個package簡單做了橋接和封裝,便于在Laravel中使用。明天再寫下篇,主要學習下League/Flysystem這個package的源碼,League/Flysystem作為一個Filesystem Abstractor Layer,利用了Adapter Pattern來封裝各個filesystem的SDK,如AWS S3 SDK或Dropbox SDK。到時見。
歡迎關注Laravel-China。
RightCapital招聘Laravel DevOps
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/30431.html
摘要:源碼解析這個類的源碼主要就是文件的操作和文件屬性的操作,而具體的操作是通過每一個實現的,看其構造函數看以上代碼知道對于操作,實際上是通過的實例來實現的??梢钥聪碌氖褂蒙衔囊呀浾f了,使得對各種的操作變得更方便了,不管是還是得。 說明:本文主要學習下LeagueFlysystem這個Filesystem Abstract Layer,學習下這個package的設計思想和編碼技巧,把自己的一...
摘要:說明本文主要學習容器的實例化過程,主要包括等四個過程??聪碌脑创a如果是數組,抽取別名并且注冊到中,上文已經討論實際上就是的。 說明:本文主要學習Laravel容器的實例化過程,主要包括Register Base Bindings, Register Base Service Providers , Register Core Container Aliases and Set the ...
摘要:說明本文主要講述了的文件系統的小,邏輯不復雜,主要就是把上的一個文件下載到本地,和下載到中。寫驅動由于沒有驅動,需要自定義下在中寫上名為的驅動同時在注冊下該就行。執行命令后,顯示上文件從上下載到上的文件該邏輯簡單,但很好玩。 說明:本文主要講述了Laravel的文件系統Filesystem的小Demo,邏輯不復雜,主要就是把Dropbox上的一個文件下載到本地local,和下載到AWS...
摘要:總結本文主要學習了啟動時做的七步準備工作環境檢測配置加載日志配置異常處理注冊注冊啟動。 說明:Laravel在把Request通過管道Pipeline送入中間件Middleware和路由Router之前,還做了程序的啟動Bootstrap工作,本文主要學習相關源碼,看看Laravel啟動程序做了哪些具體工作,并將個人的研究心得分享出來,希望對別人有所幫助。Laravel在入口index...
摘要:而函數作用是加載延遲服務,與容器解析關系不大,我們放在以后再說。在構造之前,服務容器會先把放入中,繼而再去解析。利用服務容器解析依賴的參數。 make解析 首先歡迎關注我的博客: www.leoyang90.cn 服務容器對對象的自動解析是服務容器的核心功能,make 函數、build 函數是實例化對象重要的核心,先大致看一下代碼: public function make($abst...
閱讀 3705·2021-11-22 13:52
閱讀 3603·2019-12-27 12:20
閱讀 2385·2019-08-30 15:55
閱讀 2144·2019-08-30 15:44
閱讀 2262·2019-08-30 13:16
閱讀 574·2019-08-28 18:19
閱讀 1881·2019-08-26 11:58
閱讀 3436·2019-08-26 11:47