laravel5.5源码笔记(三、门面类facade)
程序员文章站
2023-08-11 21:16:46
上次说了provider,那么这次来说说facade 首先是启动的源头,从laravel的kernel类中的$bootstrappers 数组,我们可以看到它的一些系统引导方法,其中的RegisterFacades便是用来注册facade门面类的了。 同样是有一个register类,通过这个类进行别 ......
上次说了provider,那么这次来说说facade
首先是启动的源头,从laravel的kernel类中的$bootstrappers 数组,我们可以看到它的一些系统引导方法,其中的registerfacades便是用来注册facade门面类的了。
protected $bootstrappers = [ \illuminate\foundation\bootstrap\loadenvironmentvariables::class, \illuminate\foundation\bootstrap\loadconfiguration::class, \illuminate\foundation\bootstrap\handleexceptions::class, \illuminate\foundation\bootstrap\registerfacades::class, \illuminate\foundation\bootstrap\registerproviders::class, \illuminate\foundation\bootstrap\bootproviders::class, ];
同样是有一个register类,通过这个类进行别名等注册操作
namespace illuminate\foundation\bootstrap; use illuminate\foundation\aliasloader; use illuminate\support\facades\facade; use illuminate\foundation\packagemanifest; use illuminate\contracts\foundation\application; class registerfacades { /** * bootstrap the given application. * * @param \illuminate\contracts\foundation\application $app * @return void */ public function bootstrap(application $app) { //清除facade所有已绑定实例 facade::clearresolvedinstances(); //将app存入对象 facade::setfacadeapplication($app); //将这些别名数组通过构造函数存入对象中 aliasloader::getinstance(array_merge( //通过illuminate\config\repository获取config/app.php中的aliases数组 $app->make('config')->get('app.aliases', []), //还记得最开始的博文中提到的那个包清单吗,这里也从中获取了它的aliases $app->make(packagemanifest::class)->aliases() //通过spl_autoload_register函数注册另一种自动加载函数 ))->register(); } }
public static function getinstance(array $aliases = []) { //注册时的实例是空的,会通过构造方法把刚刚传入的别名数组存入对象 if (is_null(static::$instance)) { return static::$instance = new static($aliases); } $aliases = array_merge(static::$instance->getaliases(), $aliases); static::$instance->setaliases($aliases); return static::$instance; } private function __construct($aliases) { $this->aliases = $aliases; }
public function register() { if (! $this->registered) { $this->prependtoloaderstack(); $this->registered = true; } } /** * prepend the load method to the auto-loader stack. * * @return void */ protected function prependtoloaderstack() { //最后通过自动加载函数将本对象中的load方法放入自动加载队列的前端,在我们通过类名调用方法时,会触发自动加载函数队列,会优先触发这个函数,查找到对应文件的路径然后加载相应文件 spl_autoload_register([$this, 'load'], true, true); }
public function load($alias) { //若传入的命名空间不为当前facade数组内才会通过这个方法加载 if (static::$facadenamespace && strpos($alias, static::$facadenamespace) === 0) { $this->loadfacade($alias); return true; } //否则直接返回其别名 if (isset($this->aliases[$alias])) { return class_alias($this->aliases[$alias], $alias); } } /** * load a real-time facade for the given alias. * * @param string $alias * @return void */ protected function loadfacade($alias) { require $this->ensurefacadeexists($alias); } /** * ensure that the given alias has an existing real-time facade class. * * @param string $alias * @return string */ protected function ensurefacadeexists($alias) { //从缓存中返回路径 if (file_exists($path = storage_path('framework/cache/facade-'.sha1($alias).'.php'))) { return $path; } file_put_contents($path, $this->formatfacadestub( $alias, file_get_contents(__dir__.'/stubs/facade.stub') )); return $path; }
由于laravel中composer的执行过程过于繁琐,这里就不做深究了。原理是一样的,通过别名找到命名空间的类名,再由composer的类名与文件路径映射关系,自动加载函数找到相应文件加载进来。
那么我们来自定义一个门面类试一下
1、新建一个facade类,这里返回的test是上一篇博客中定义的对象
namespace app\facades; use illuminate\support\facades\facade; class test extends facade { protected static function getfacadeaccessor() { return 'app\contracts\test'; } }
2、将它添加到config/app.php的aliases别名数组中
'aliases' => [ 'app' => illuminate\support\facades\app::class, 'artisan' => illuminate\support\facades\artisan::class, 'auth' => illuminate\support\facades\auth::class, 'blade' => illuminate\support\facades\blade::class, 'broadcast' => illuminate\support\facades\broadcast::class, 'bus' => illuminate\support\facades\bus::class, 'cache' => illuminate\support\facades\cache::class, 'config' => illuminate\support\facades\config::class, 'cookie' => illuminate\support\facades\cookie::class, 'crypt' => illuminate\support\facades\crypt::class, 'db' => illuminate\support\facades\db::class, 'eloquent' => illuminate\database\eloquent\model::class, 'event' => illuminate\support\facades\event::class, 'file' => illuminate\support\facades\file::class, 'gate' => illuminate\support\facades\gate::class, 'hash' => illuminate\support\facades\hash::class, 'lang' => illuminate\support\facades\lang::class, 'log' => illuminate\support\facades\log::class, 'mail' => illuminate\support\facades\mail::class, 'notification' => illuminate\support\facades\notification::class, 'password' => illuminate\support\facades\password::class, 'queue' => illuminate\support\facades\queue::class, 'redirect' => illuminate\support\facades\redirect::class, 'redis' => illuminate\support\facades\redis::class, 'request' => illuminate\support\facades\request::class, 'response' => illuminate\support\facades\response::class, 'route' => illuminate\support\facades\route::class, 'schema' => illuminate\support\facades\schema::class, 'session' => illuminate\support\facades\session::class, 'storage' => illuminate\support\facades\storage::class, 'url' => illuminate\support\facades\url::class, 'validator' => illuminate\support\facades\validator::class, 'view' => illuminate\support\facades\view::class, //刚刚新建的facades 'test' => \app\facades\test::class, ],
3、在路由文件中调用它
route::any('/index', function (){ test::doing(); });
我们会发现,即使我们没有通过app对象进行make也没有任何命名空间的引入,它就这样被调用了。
如果是在类中调用,那么就要麻烦一些了,需要ues这个别名,注意这一点
namespace app\http\controllers; use illuminate\http\request; //use app\contracts\test; //注意这里只引入了别名,由于命名空间冲突,所以我注释了其他部分 use test; class indexcontroller extends controller { // public function __construct(test $test) // { // $this->test = $test; // } public function index(test $test) { test::doing(); // app()->make('app\contracts\test')->doing(); // echo '<br>'; // // //只有通过构造方法进行自动加载依赖的方式才能触发契约的when绑定 // $this->test->doing(); // // echo '<br>'; // //因为laravel中的上下文绑定只能具体到类,所以这里的$test实例依然为普通绑定 // $test->doing(); } }
通过这个demo的实践,facade还是比较简单的。