- Published on
Laravel-Facade实现原理
- Authors
- Name
- Xiangxuan Liu
在 Laravel 中,我们经常这样调用系统核心类
App::make('Test');
你可能会认为 make() 是 App 对象的静态方法,可当你翻完整个框架后,却连 \App 类的源文件都找不到。 实际上 make() 是属于 Illuminate\Foundation\Application 的方法。在 vendor/laravel/framework/src/Illuminate/Foundation/Application.php 文件 444 行左右你可以看到 make 的申明:
public function make($abstract, $parameters = array())
{
$abstract = $this->getAlias($abstract);
if (isset($this->deferredServices[$abstract]))
{
$this->loadDeferredProvider($abstract);
}
return parent::make($abstract, $parameters);
}
既然 App 类并不存在,为何调用其静态方法时会调用到 Application 对象同名的非静态方法呢? 下面我们来读一读源代码~
首先我们找到配置文件 app/config/app.php,在这个文件最后配置了 aliases 数组。
'aliases' => array(
'App' => 'Illuminate\Support\Facades\App',
'Artisan' => 'Illuminate\Support\Facades\Artisan',
'Auth' => 'Illuminate\Support\Facades\Auth',
'Blade' => 'Illuminate\Support\Facades\Blade',
'Cache' => 'Illuminate\Support\Facades\Cache',
'ClassLoader' => 'Illuminate\Support\ClassLoader',
'Config' => 'Illuminate\Support\Facades\Config',
'Controller' => 'Illuminate\Routing\Controller',
'Cookie' => 'Illuminate\Support\Facades\Cookie',
'Crypt' => 'Illuminate\Support\Facades\Crypt',
'DB' => 'Illuminate\Support\Facades\DB',
'Eloquent' => 'Illuminate\Database\Eloquent\Model',
'Event' => 'Illuminate\Support\Facades\Event',
'File' => 'Illuminate\Support\Facades\File',
'Form' => 'Illuminate\Support\Facades\Form',
'Hash' => 'Illuminate\Support\Facades\Hash',
'HTML' => 'Illuminate\Support\Facades\HTML',
'Input' => 'Illuminate\Support\Facades\Input',
'Lang' => 'Illuminate\Support\Facades\Lang',
'Log' => 'Illuminate\Support\Facades\Log',
'Mail' => 'Illuminate\Support\Facades\Mail',
'Paginator' => 'Illuminate\Support\Facades\Paginator',
'Password' => 'Illuminate\Support\Facades\Password',
'Queue' => 'Illuminate\Support\Facades\Queue',
'Redirect' => 'Illuminate\Support\Facades\Redirect',
'Redis' => 'Illuminate\Support\Facades\Redis',
'Request' => 'Illuminate\Support\Facades\Request',
'Response' => 'Illuminate\Support\Facades\Response',
'Route' => 'Illuminate\Support\Facades\Route',
'Schema' => 'Illuminate\Support\Facades\Schema',
'Seeder' => 'Illuminate\Database\Seeder',
'Session' => 'Illuminate\Support\Facades\Session',
'SoftDeletingTrait' => 'Illuminate\Database\Eloquent\SoftDeletingTrait',
'SSH' => 'Illuminate\Support\Facades\SSH',
'Str' => 'Illuminate\Support\Str',
'URL' => 'Illuminate\Support\Facades\URL',
'Validator' => 'Illuminate\Support\Facades\Validator',
'View' => 'Illuminate\Support\Facades\View',
),
我们可以清楚的看到类别名设置关系。
在 vendor/laravel/framework/src/Illuminate/Foundation/start.php 文件第 180 行左右开始类别名注册。
$aliases = $config['aliases'];
AliasLoader::getInstance($aliases)->register();
经过一堆调用之后。会调用到 Illuminate\Foundation\AliasLoader的load() 方法
public function load($alias)
{
if (isset($this->aliases[$alias]))
{
return class_alias($this->aliases[$alias], $alias);
}
}
最后调用的是系统的 class_alias() 方法,不清楚的自己查查手册咯~
好的,我们现在以 App 对象举例,经过类别名之后,我们调用到了 Illuminate\Support\Facades\App,根据 PSR-4 规范,我们找到 vendor/laravel/framework/src/Illuminate/Support/Facades/App.php 文件,下面是它的全部源码。
<?php namespace Illuminate\Support\Facades;
/**
* @see \Illuminate\Foundation\Application
*/
class App extends Facade {
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor() { return 'app'; }
}
它只定义了一个 getFacadeAccessor() 的静态方法,返回字符串 app。 我们再来看看这个类的父类 Illuminate\Support\Facades\Facade。
发现其中定义了一个 __callStatic() 的魔术方法,不清楚的童鞋再去查查手册咯~
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();
switch (count($args))
{
case 0:
return $instance->$method();
case 1:
return $instance->$method($args[0]);
case 2:
return $instance->$method($args[0], $args[1]);
case 3:
return $instance->$method($args[0], $args[1], $args[2]);
case 4:
return $instance->$method($args[0], $args[1], $args[2], $args[3]);
default:
return call_user_func_array(array($instance, $method), $args);
}
}
发现他获取了一个 $instance 实例变量,现在我们在看看 $instance 变量到底是什么~ 找到 getFacadeRoot() 方法
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
他调用了 getFacadeAccessor() 方法的返回值传给了 resolveFacadeInstance()。刚我们已经看了在 Illuminate\Support\Facades\App 中的 getFacadeAccessor() 返回字符串 app。
再继续看 resolveFacadeInstance() 方法。里面的代码相当于回从 IOC 容器中使用 app 这个 key 值取回对应的对象。 而 app 在 vendor/laravel/framework/src/Illuminate/Foundation/start.php 文件的 62 行左右已经注入到 IoC 容器
$app->instance('app', $app);
而 $app 是在更前面的加载中实例化的,就是 Application 的对象实例。
再回到 Facade 的 __callStatic 方法,现在我们确定 $instance 就是 Application 对象的实例。
那么根据 __callStatic 的调用方式,App::make() 自然就调用 $inctance 实例的 make 方法。
边读代码边写的,可能写得有点乱,但还是希望这篇文章对你有帮助 ^_^