咱們常常這樣使用一些類php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Log;
class IndexController extends Controller
{
public function index()
{
Log::info('hahaha~');
}
}
複製代碼
先看對應的 Log
類在框架中註冊的部分,在 app.php
文件中的別名數組bootstrap
'aliases' => [
...
'Log' => Illuminate\Support\Facades\Log::class,
...
],
複製代碼
因此調用的時候實際容器會去解析設計模式
lluminate\Support\Facades\Log::class
這個類數組
咱們來看這個類bash
<?php
namespace Illuminate\Support\Facades;
class Log extends Facade
{
protected static function getFacadeAccessor()
{
return 'log';
}
}
複製代碼
最開始咱們的調用方法是 Log::info
這裏要追蹤到父類 Facade
裏面app
方便閱讀精簡一些方法框架
<?php
namespace Illuminate\Support\Facades;
use Closure;
use Mockery;
use RuntimeException;
use Mockery\MockInterface;
abstract class Facade
{
protected static $app;
protected static $resolvedInstance;
...
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();
if (! $instance) {
throw new RuntimeException('A facade root has not been set.');
}
return $instance->$method(...$args);
}
}
複製代碼
這裏的調用邏輯是經過 __callStatic
這個魔術方法來實現的ide
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();
if (! $instance) {
throw new RuntimeException('A facade root has not been set.');
}
return $instance->$method(...$args);
}
複製代碼
這裏也就解釋了爲何咱們能以靜態調用的方式調用對應的方法。post
開始分析ui
$instance = static::getFacadeRoot();
複製代碼
看樣子是要解析一個實例出來,這裏要注意的是
static
調用會指向調用的類,其次纔是繼承的類。
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
複製代碼
因此這裏的 static::getFacadeAccessor()
實際指向 Log
類
protected static function getFacadeAccessor()
{
return 'log';
}
複製代碼
原來是獲取一個別名,那麼推測後面就是經過別名從容器拿對象了
繼續看看如何拿對象
protected static function resolveFacadeInstance($name)
{
if (is_object($name)) {
return $name;
}
if (isset(static::$resolvedInstance[$name])) {
return static::$resolvedInstance[$name];
}
return static::$resolvedInstance[$name] = static::$app[$name];
}
複製代碼
看到這裏就知道最主要是 static::$app[$name]
來獲取對象
$app
來自哪裏?可是疑問來了,$app
若是是 Application
對象的話又是在什麼地方賦值?
回到內核 Kernel
來看看解答,如何啓動請回顧 【Laravel-海賊王系列】第四章,Kernel 類解析 的 handle
方法。
protected $bootstrappers = [
...
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
...
];
複製代碼
直接上代碼
<?php
namespace Illuminate\Foundation\Bootstrap;
use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\Facades\Facade;
use Illuminate\Foundation\PackageManifest;
use Illuminate\Contracts\Foundation\Application;
class RegisterFacades
{
public function bootstrap(Application $app)
{
Facade::clearResolvedInstances();
Facade::setFacadeApplication($app);
AliasLoader::getInstance(array_merge(
$app->make('config')->get('app.aliases', []),
$app->make(PackageManifest::class)->aliases()
))->register();
}
}
複製代碼
真香~, Facade::setFacadeApplication($app);
在這兒傳入的 Application
。
這裏又涉及到了一些問題,那我怎麼知道 $app['log']
裏面的對象是誰呢?
哈哈這裏去看【Laravel-海賊王系列】第三章,Container 類解析,
裏面的 bind()
方法就是在容器中綁定抽象和實現的功能,
這裏也是容器的知識。 這裏的 log
是 Application
在啓動的時候提早以及幫咱們搞定了~
位於 Application
的 __construct()
中
public function __construct($basePath = null)
{
...
$this->registerBaseServiceProviders();
...
}
複製代碼
protected function registerBaseServiceProviders()
{
...
$this->register(new LogServiceProvider($this));
...
}
複製代碼
<?php
namespace Illuminate\Log;
use Illuminate\Support\ServiceProvider;
class LogServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('log', function () {
return new LogManager($this->app);
});
}
}
複製代碼
最後就看到了真正的對象 return new LogManager($this->app);
對象在手,就能夠任意的調用存在的方法。
return $instance->$method(...$args);