- 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
方法。
边读代码边写的,可能写得有点乱,但还是希望这篇文章对你有帮助 ^_^