Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

12. Facade 机制 #12

Open
xiaohuilam opened this issue Sep 27, 2018 · 0 comments
Open

12. Facade 机制 #12

xiaohuilam opened this issue Sep 27, 2018 · 0 comments
Labels
book The digital book for laravel learning

Comments

@xiaohuilam
Copy link
Owner

xiaohuilam commented Sep 27, 2018

总结: Facade 就是一个标记便捷的调用容器盛放对象的方法的设计。

一般的,Facade 类都继承于 Illuminate\Support\Facades\Facade

比如 Illuminate\Support\Facades\Gate 这个门面:

<?php
namespace Illuminate\Support\Facades;

use Illuminate\Contracts\Auth\Access\Gate as GateContract;

class Gate extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return GateContract::class;
    }
}

代码中我们只需要留意 getFacadeAccessor 方法是在 Illuminate\Support\Facades\Gate 中定义的就好了。
我们来分析 Illuminate\Support\Facades\Facade

魔术方法 part 1

首先我们来看末尾的 __callStatic 方法:

/**
* Handle dynamic, static calls to the object.
*
* @param string $method
* @param array $args
* @return mixed
*
* @throws \RuntimeException
*/
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);
}

还记得 Facade 类我们是怎么调用的吗?

静态调用!比如 Gate::allows()

因为在 Facade 类中定义了 __callStatic 魔术方法,所以只要静态调用了里面没有提供的方法,都会走到魔术方法里面。
这个魔术方法内部有一个取出实例的逻辑:

$instance = static::getFacadeRoot();

代码位于

/**
* Get the root object behind the facade.
*
* @return mixed
*/
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}

前面,我们留意了 static::getFacadeAccessor() 是位于 Illuminate\Support\Facades\Gate 中的,而 static::getFacadeAccessor() 运行后作为参数给了 static::resolveFacadeInstance(),后者实现为

/**
* Resolve the facade root instance from the container.
*
* @param string|object $name
* @return mixed
*/
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 就是容器对象

/**
* The application instance being facaded.
*
* @var \Illuminate\Contracts\Foundation\Application
*/
protected static $app;

这句

return static::$resolvedInstance[$name] = static::$app[$name];

尝试从容器对象 数组取 操作,就是调用了 make
public function offsetGet($key)
{
return $this->make($key);
}

就是从容器取出对象。

总结 getFacadeRoot 的逻辑大概为

  1. 判断 static::getFacadeAccessor() 返回是否对象,如果对象则直接使用。
  2. Facade::$resolvedInstance 数组中尝试根据 $name 取出对象,避免多次调用时,每次都取。
  3. Illuminate\Foundation\Application 容器取出对象。

魔术方法 part 2

拿到 $instance 后,先验证 $instance 是否为空。如果是就报错 A facade root has not been set.

if (! $instance) {
throw new RuntimeException('A facade root has not been set.');
}

所以如果遇到 A facade root has not been set. 这个错误,一般都是 a. 相应的服务提供者未注册; b. 对象绑定到容器的别名跟 static::getFacadeAccessor() 返回不一致。

最后,直接把方法和参数穿透到背后的 $instance 去执行。

Laravel 有很多 facade 门面,背后穿透的不只一个类(譬如 DBViewSession),这是怎么回事呢?
请看 15. Laravel 神奇的 Manager 类

@xiaohuilam xiaohuilam added the book The digital book for laravel learning label Sep 27, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
book The digital book for laravel learning
Projects
None yet
Development

No branches or pull requests

1 participant