摘要:說(shuō)明本文主要學(xué)習(xí)的模塊的源碼邏輯,把自己的一點(diǎn)點(diǎn)研究心得分享出來(lái),希望對(duì)別人有所幫助。實(shí)際上,使用了的重載學(xué)習(xí)筆記之重載,通過(guò)魔術(shù)方法調(diào)用里的,而這個(gè)實(shí)際上就是,該中有方法,可以調(diào)用。
說(shuō)明:本文主要學(xué)習(xí)Laravel的Filesystem模塊的源碼邏輯,把自己的一點(diǎn)點(diǎn)研究心得分享出來(lái),希望對(duì)別人有所幫助。總的來(lái)說(shuō),F(xiàn)ilesystem模塊的源碼也比較簡(jiǎn)單,Laravel的IlluminateFilesystem模塊主要依賴于LeagueFlysystem這個(gè)Filesystem Abstractor Layer,類似于是LeagueFlysystem的Laravel Bridge。而不同的Filesystem SDK有著各自的具體增刪改查邏輯,如AWS S3 SDK,Dropbox SDK,這些SDK都是通過(guò)Adapter Pattern裝載入這個(gè)Filesystem Abstractor Layer。Filesystem模塊的整體架構(gòu)如下兩張圖:
開發(fā)環(huán)境:Laravel5.2+MAMP+PHP7+MySQL5.6
1. IlluminateFilesystemFilesystemServiceProviderLaravel中每一個(gè)Service模塊都有對(duì)應(yīng)的ServiceProvider,主要幫助把該Service注冊(cè)到Container中,方便在應(yīng)用程序中利用Facade調(diào)用該Service。同樣,F(xiàn)ilesystem Service有對(duì)應(yīng)的FilesystemServiceProvider,幫助注冊(cè)files和filesystem等Service:
// IlluminateFilesystem $this->app->singleton("files", function () { return new Filesystem; }); $this->app->singleton("filesystem", function () { return new FilesystemManager($this->app); });
使用Container的singleton單例注冊(cè),同時(shí)還注冊(cè)了filesystem.disk(config/filesystems.php的default配置選項(xiàng))和filesystem.cloud(config/filesystems.php的cloud配置選項(xiàng))。其中,files的Facade為IlluminateSupportFacadesFile,filesystem的Facade為IlluminateSupportFacadesFilesystem。
2. IlluminateFilesystemFilesystemManagerLaravel官網(wǎng)上有類似這樣代碼:
// 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");
那這樣的代碼內(nèi)部實(shí)現(xiàn)邏輯是怎樣的呢?
翻一下IlluminateFilesystemFilesystemManager代碼就很容易知道了。首先Storage::disk()是利用了Facade模式,Storage是名為filesystem的Facade,而filesystem從上文知道實(shí)際是FilesystemManager的對(duì)象,所以可以看做(new FilesystemManager)->disk(),看disk()方法源碼:
// IlluminateFilesystemFilesystemManager /** * Get a filesystem instance. * * @param string $name * @return IlluminateContractsFilesystemFilesystem */ public function disk($name = null) { // 如果不傳參,就默認(rèn)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里可以這樣簡(jiǎn)潔的寫 $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); // 檢查自定義驅(qū)動(dòng)中是否已經(jīng)提前定義了,自定義是通過(guò)extend($driver, Closure $callback)定制化driver, // 如果已經(jīng)定義則取出定制化driver,下文介紹 if (isset($this->customCreators[$config["driver"]])) { return $this->callCustomCreator($config); } // 這里有個(gè)巧妙的技巧,檢查IlluminateFilesystemFilesystemManager中是否有createS3Driver這個(gè)方法, // 有的話代入$config參數(shù)執(zhí)行該方法,看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, // 上文說(shuō)過(guò)Laravel的Filesystem只是個(gè)Filesystem Bridge,實(shí)際上用的是LeagueFlysystem這個(gè)依賴。 // LeagueFlysystem源碼解析會(huì)在下篇中講述, // 主要使用了Adapter Pattern把各個(gè)Filesystem SDK 整合到一個(gè)LeagueFlysystemFilesystemInterface實(shí)例中, // 有幾個(gè)核心概念: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); }
通過(guò)代碼里注釋,可以看出Storage::disk("s3")實(shí)際上返回的是這樣一段類似代碼:
(new IlluminateFilesystemFilesystemAdapter(new LeagueFlysystemFilesystem(new S3Adapter(new S3Client(), $options), $config))))
所以,Storage::disk("s3")->allFiles($parameters)或者Storage::disk("s3")->exists($parameters),實(shí)際上調(diào)用的是IlluminateFilesystemFilesystemAdapter這個(gè)對(duì)象的allFiles($parameters)和exists($parameters)方法。
3. IlluminateFilesystemFilesystemAdapter查看FilesystemAdapter的源碼,提供了關(guān)于filesystem的增刪改查的一系列方法:
/** * Determine if a file exists. * * @param string $path * @return bool */ public function exists($path) { // 實(shí)際上又是調(diào)用的driver的has()方法,$driver又是LeagueFlysystemFilesystem對(duì)象, // 查看LeagueFlysystemFilesystem對(duì)象的has()方法, // 實(shí)際上是通過(guò)LeagueFlysystemAwsS3v3AwsS3Adapter的has()方法, // 當(dāng)然最后調(diào)用的是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); }
通過(guò)代碼注釋知道,Storage::disk("s3")->exists($parameters)實(shí)際上最后通過(guò)調(diào)用S3 SDK的(new S3Client())->doesObjectExist($parameters)檢查S3上有沒(méi)有該文件,Storage::disk("s3")->allFiles($parameters)也是同理,通過(guò)調(diào)用(new LeagueFlysystemAwsS3v3AwsS3Adapter(new S3Client(), $config))->listContents()來(lái)list contents of a dir.
根據(jù)上文的解釋,那Storage::disk("s3")->readStream($path)可以調(diào)用不?
可以的。實(shí)際上,IlluminateFilesystemFilesystemAdapter使用了PHP的重載(Laravel學(xué)習(xí)筆記之PHP重載(overloading)),通過(guò)__call($method, $parameters)魔術(shù)方法調(diào)用$driver里的$method,而這個(gè)$driver實(shí)際上就是(new LeagueFlysystemFilesystem),該Filesystem Abstract Layer中有readStream方法,可以調(diào)用。
Laravelgu官網(wǎng)中介紹通過(guò)Storage::extend($driver, Closure $callback)來(lái)自定義driver,這里我們知道實(shí)際上調(diào)用的是(new IlluminateFilesystemFilesystemManager($parameters))->extend($driver, Closure $callback),上文中提到該對(duì)象的resolve($name)代碼時(shí)會(huì)先檢查自定義驅(qū)動(dòng)有沒(méi)有,有的話調(diào)用自定義驅(qū)動(dòng),再看下resolve()代碼:
/** * Resolve the given disk. * * @param string $name * @return IlluminateContractsFilesystemFilesystem * * @throws InvalidArgumentException */ protected function resolve($name) { $config = $this->getConfig($name); // 檢查自動(dòng)以驅(qū)動(dòng)是否存在,存在的話,調(diào)用callCustomCreator來(lái)解析出$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()方法就是把自定義的驅(qū)動(dòng)注冊(cè)進(jìn)$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; }
總結(jié):上篇主要講述了Laravel Filesystem Bridge,該Bridge只是把League/Flysystem這個(gè)package簡(jiǎn)單做了橋接和封裝,便于在Laravel中使用。明天再寫下篇,主要學(xué)習(xí)下League/Flysystem這個(gè)package的源碼,League/Flysystem作為一個(gè)Filesystem Abstractor Layer,利用了Adapter Pattern來(lái)封裝各個(gè)filesystem的SDK,如AWS S3 SDK或Dropbox SDK。到時(shí)見。
歡迎關(guān)注Laravel-China。
RightCapital招聘Laravel DevOps
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/30431.html
摘要:源碼解析這個(gè)類的源碼主要就是文件的操作和文件屬性的操作,而具體的操作是通過(guò)每一個(gè)實(shí)現(xiàn)的,看其構(gòu)造函數(shù)看以上代碼知道對(duì)于操作,實(shí)際上是通過(guò)的實(shí)例來(lái)實(shí)現(xiàn)的??梢钥聪碌氖褂蒙衔囊呀?jīng)說(shuō)了,使得對(duì)各種的操作變得更方便了,不管是還是得。 說(shuō)明:本文主要學(xué)習(xí)下LeagueFlysystem這個(gè)Filesystem Abstract Layer,學(xué)習(xí)下這個(gè)package的設(shè)計(jì)思想和編碼技巧,把自己的一...
摘要:說(shuō)明本文主要學(xué)習(xí)容器的實(shí)例化過(guò)程,主要包括等四個(gè)過(guò)程??聪碌脑创a如果是數(shù)組,抽取別名并且注冊(cè)到中,上文已經(jīng)討論實(shí)際上就是的。 說(shuō)明:本文主要學(xué)習(xí)Laravel容器的實(shí)例化過(guò)程,主要包括Register Base Bindings, Register Base Service Providers , Register Core Container Aliases and Set the ...
摘要:說(shuō)明本文主要講述了的文件系統(tǒng)的小,邏輯不復(fù)雜,主要就是把上的一個(gè)文件下載到本地,和下載到中。寫驅(qū)動(dòng)由于沒(méi)有驅(qū)動(dòng),需要自定義下在中寫上名為的驅(qū)動(dòng)同時(shí)在注冊(cè)下該就行。執(zhí)行命令后,顯示上文件從上下載到上的文件該邏輯簡(jiǎn)單,但很好玩。 說(shuō)明:本文主要講述了Laravel的文件系統(tǒng)Filesystem的小Demo,邏輯不復(fù)雜,主要就是把Dropbox上的一個(gè)文件下載到本地local,和下載到AWS...
摘要:總結(jié)本文主要學(xué)習(xí)了啟動(dòng)時(shí)做的七步準(zhǔn)備工作環(huán)境檢測(cè)配置加載日志配置異常處理注冊(cè)注冊(cè)啟動(dòng)。 說(shuō)明:Laravel在把Request通過(guò)管道Pipeline送入中間件Middleware和路由Router之前,還做了程序的啟動(dòng)Bootstrap工作,本文主要學(xué)習(xí)相關(guān)源碼,看看Laravel啟動(dòng)程序做了哪些具體工作,并將個(gè)人的研究心得分享出來(lái),希望對(duì)別人有所幫助。Laravel在入口index...
摘要:而函數(shù)作用是加載延遲服務(wù),與容器解析關(guān)系不大,我們放在以后再說(shuō)。在構(gòu)造之前,服務(wù)容器會(huì)先把放入中,繼而再去解析。利用服務(wù)容器解析依賴的參數(shù)。 make解析 首先歡迎關(guān)注我的博客: www.leoyang90.cn 服務(wù)容器對(duì)對(duì)象的自動(dòng)解析是服務(wù)容器的核心功能,make 函數(shù)、build 函數(shù)是實(shí)例化對(duì)象重要的核心,先大致看一下代碼: public function make($abst...
閱讀 3765·2021-11-22 13:52
閱讀 3633·2019-12-27 12:20
閱讀 2402·2019-08-30 15:55
閱讀 2156·2019-08-30 15:44
閱讀 2274·2019-08-30 13:16
閱讀 589·2019-08-28 18:19
閱讀 1903·2019-08-26 11:58
閱讀 3450·2019-08-26 11:47