咱们常常这样使用一些类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);