Lumen开发:lumen源码解读之初始化(2)——门面(Facades)与数据库(db)

紧接上一篇

$app->withFacades();//为应用程序注册门面。
  
$app->withEloquent();//为应用程序加载功能强大的库。

先来看看withFacades()

   /**
     * Register the facades for the application.(为应用程序注册门面。)
     *
     * @param  bool  $aliases
     * @param  array $userAliases
     * @return void
     */
    public function withFacades($aliases = true, $userAliases = [])
    {
        Facade::setFacadeApplication($this);
 
        if ($aliases) {
            $this->withAliases($userAliases);
        }
    }
setFacadeApplication()
/**
 * Set the application instance.(设置应用程序实例。)
 *
 * @param  \Illuminate\Contracts\Foundation\Application  $app
 * @return void
 */
public static function setFacadeApplication($app)
{
    static::$app = $app;
}

将当前实例传给门面类(Facade)

$this->withAliases($userAliases)

/**
 * Register the aliases for the application.(注册应用程序的别名。)
 *
 * @param  array  $userAliases
 * @return void
 */
public function withAliases($userAliases = [])
{
    $defaults = [
        'Illuminate\Support\Facades\Auth' => 'Auth',
        'Illuminate\Support\Facades\Cache' => 'Cache',
        'Illuminate\Support\Facades\DB' => 'DB',
        'Illuminate\Support\Facades\Event' => 'Event',
        'Illuminate\Support\Facades\Gate' => 'Gate',
        'Illuminate\Support\Facades\Log' => 'Log',
        'Illuminate\Support\Facades\Queue' => 'Queue',
        'Illuminate\Support\Facades\Schema' => 'Schema',
        'Illuminate\Support\Facades\URL' => 'URL',
        'Illuminate\Support\Facades\Validator' => 'Validator',
    ];
 
    if (! static::$aliasesRegistered) {//判断是否已注册类别名。
        static::$aliasesRegistered = true;
 
        $merged = array_merge($defaults, $userAliases);
 
        foreach ($merged as $original => $alias) {
            class_alias($original, $alias);//设置别名
        }
    }
}

然后就是withEloquent()函数

/**
 * Load the Eloquent library for the application.(为应用程序加载功能强大的库。)
 *
 * @return void
 */
public function withEloquent()
{
    $this->make('db');
}
/**
 * Resolve the given type from the container.(从容器中解析给定类型。)
 *
 * @param  string  $abstract
 * @return mixed
 */
public function make($abstract)
{
    $abstract = $this->getAlias($abstract);
 
    if (array_key_exists($abstract, $this->availableBindings) &&
        ! array_key_exists($this->availableBindings[$abstract], $this->ranServiceBinders)) {
        $this->{$method = $this->availableBindings[$abstract]}(); // 服务绑定方法执行
 
        $this->ranServiceBinders[$method] = true;  // 变量是记录已执行的服务绑定方法。
    }
 
    return parent::make($abstract);
}

一步一步来,make()函数以后经常用得上。

/**
 * Get the alias for an abstract if available.(如果可用的话,获取抽象的别名。)
 *
 * @param  string  $abstract
 * @return string
 *
 * @throws \LogicException
 */
public function getAlias($abstract)
{
    if (! isset($this->aliases[$abstract])) {  //$this->aliases是注册类别名,如果没有别名,则直接返回$abstract
        return $abstract;
    }
 
    if ($this->aliases[$abstract] === $abstract) {  //如果$abstract是别名本身,则会抛出异常
        throw new LogicException("[{$abstract}] is aliased to itself.");
    }
 
    return $this->getAlias($this->aliases[$abstract]);
}

这一个的$this->aliases的值来着上一篇文章registerContainerAliases()函数对它的赋值

接下来是这段

if (array_key_exists($abstract, $this->availableBindings) &&
        ! array_key_exists($this->availableBindings[$abstract], $this->ranServiceBinders)) {
        $this->{$method = $this->availableBindings[$abstract]}();  // 服务绑定方法执行
 
        $this->ranServiceBinders[$method] = true;  // 变量是记录已执行的服务绑定方法。
    }

$this->availableBindings变量在Applilcation类的定义是是

/**
 * The available container bindings and their respective load methods.(可用的容器绑定及其各自的加载方法。)
 *
 * @var array
 */
public $availableBindings = [
    'auth' => 'registerAuthBindings',
    'auth.driver' => 'registerAuthBindings',
    'Illuminate\Auth\AuthManager' => 'registerAuthBindings',
    'Illuminate\Contracts\Auth\Guard' => 'registerAuthBindings',
    'Illuminate\Contracts\Auth\Access\Gate' => 'registerAuthBindings',
    'Illuminate\Contracts\Broadcasting\Broadcaster' => 'registerBroadcastingBindings',
    'Illuminate\Contracts\Broadcasting\Factory' => 'registerBroadcastingBindings',
    'Illuminate\Contracts\Bus\Dispatcher' => 'registerBusBindings',
    'cache' => 'registerCacheBindings',
    'cache.store' => 'registerCacheBindings',
    'Illuminate\Contracts\Cache\Factory' => 'registerCacheBindings',
    'Illuminate\Contracts\Cache\Repository' => 'registerCacheBindings',
    'composer' => 'registerComposerBindings',
    'config' => 'registerConfigBindings',
    'db' => 'registerDatabaseBindings',
    'Illuminate\Database\Eloquent\Factory' => 'registerDatabaseBindings',
    'encrypter' => 'registerEncrypterBindings',
    'Illuminate\Contracts\Encryption\Encrypter' => 'registerEncrypterBindings',
    'events' => 'registerEventBindings',
    'Illuminate\Contracts\Events\Dispatcher' => 'registerEventBindings',
    'files' => 'registerFilesBindings',
    'hash' => 'registerHashBindings',
    'Illuminate\Contracts\Hashing\Hasher' => 'registerHashBindings',
    'log' => 'registerLogBindings',
    'Psr\Log\LoggerInterface' => 'registerLogBindings',
    'queue' => 'registerQueueBindings',
    'queue.connection' => 'registerQueueBindings',
    'Illuminate\Contracts\Queue\Factory' => 'registerQueueBindings',
    'Illuminate\Contracts\Queue\Queue' => 'registerQueueBindings',
    'Psr\Http\Message\ServerRequestInterface' => 'registerPsrRequestBindings',
    'Psr\Http\Message\ResponseInterface' => 'registerPsrResponseBindings',
    'translator' => 'registerTranslationBindings',
    'url' => 'registerUrlGeneratorBindings',
    'validator' => 'registerValidatorBindings',
    'Illuminate\Contracts\Validation\Factory' => 'registerValidatorBindings',
    'view' => 'registerViewBindings',
    'Illuminate\Contracts\View\Factory' => 'registerViewBindings',
];
$this->ranServiceBinders 变量是记录已执行的服务绑定方法。
当前执行的是make('db'),所以$abstract=‘db’;
$this->availableBindings['db'] = 'registerDatabaseBindings';
$this->{$method = $this->availableBindings[$abstract]}();

会执行到Application的registerDatabaseBindings方法

/**
 * Register container bindings for the application.(为应用程序注册容器绑定。)
 *
 * @return void
 */
protected function registerDatabaseBindings()
{
    $this->singleton('db', function () {  //这里是用闭包函数注册一个db的单例
        return $this->loadComponent(
            'database', [
                'Illuminate\Database\DatabaseServiceProvider',
                'Illuminate\Pagination\PaginationServiceProvider',
            ], 'db'
        );
    });
}


    singleton()函数
    public function singleton($abstract, $concrete = null)
    {   
        $this->bind($abstract, $concrete, true);
    }

这里是用闭包函数注册一个db的单例,接着看闭包内执行了什么

/**
 * Configure and load the given component and provider.(配置并加载给定的组件和提供程序。)
 *
 * @param  string  $config
 * @param  array|string  $providers
 * @param  string|null  $return
 * @return mixed
 */
public function loadComponent($config, $providers, $return = null)
{
    $this->configure($config);//将配置文件加载到应用程序中。
 
    foreach ((array) $providers as $provider) {
        $this->register($provider);//注册传过来的服务供应类
    }
 
    return $this->make($return ?: $config);//
}

加载配置文件和注册服务的内容

    configure() //将配置文件加载到应用程序中。
    public function configure($name)
    {    
        if (isset($this->loadedConfigurations[$name])) {
            return;
        }

        $this->loadedConfigurations[$name] = true;

        $path = $this->getConfigurationPath($name);
 
        if ($path) {
            $this->make('config')->set($name, require $path);
        }
    }
    // 加载配置文件路径,若无文件名,返回配置文件夹路径
    public function getConfigurationPath($name = null)
    {  
        if (! $name) {
            $appConfigDir = $this->basePath('config').'/';  // 获取文件真实路径

            if (file_exists($appConfigDir)) {
                return $appConfigDir;
            } elseif (file_exists($path = __DIR__.'/../config/')) {
                return $path;
            }
        } else {
            $appConfigPath = $this->basePath('config').'/'.$name.'.php';  // 获取文件真实路径

            if (file_exists($appConfigPath)) {
                return $appConfigPath;
            } elseif (file_exists($path = __DIR__.'/../config/'.$name.'.php')) {
                return $path;
            }
        }
    }
    // 获取文件真实路径
    public function basePath($path = null)
    {
        if (isset($this->basePath)) {
            return $this->basePath.($path ? '/'.$path : $path);
        }

        if ($this->runningInConsole()) {
            $this->basePath = getcwd();
        } else {
            $this->basePath = realpath(getcwd().'/../');
        }

        return $this->basePath($path);
    }
    // PHP运行环境检测
    public function runningInConsole()
    {
        return php_sapi_name() == 'cli';
    }



    //注册传过来的服务供应类
    public function register($provider)
    {    
        if (! $provider instanceof ServiceProvider) {
            $provider = new $provider($this);
        }

        if (array_key_exists($providerName = get_class($provider), $this->loadedProviders)) {
            return;
        }

        $this->loadedProviders[$providerName] = true;

        if (method_exists($provider, 'register')) {
            $provider->register();
        }

        if (method_exists($provider, 'boot')) {
            return $this->call([$provider, 'boot']);
        }
    }

因为这里第一次初始化后,$this->ranServiceBinders[$method]=true,
所以以后调用db时,都会直接调用父类(Container)的make()函数。
讲远了的感觉,不过刚好可以一起讲一下最后一步return parent::make($abstract);
/**
 * Resolve the given type from the container.
 *
 * @param  string  $abstract
 * @return mixed
 */
public function make($abstract)
{
    return $this->resolve($abstract);
}

Container类的make()只调用了$this->resolve()函数,马不停蹄,我们来看看这个函数

/**
     * Resolve the given type from the container.(从容器中解析给定类型。)
     *
     * @param  string  $abstract
     * @param  array  $parameters
     * @return mixed
     */
    protected function resolve($abstract, $parameters = [])
    {
        $abstract = $this->getAlias($abstract);//取实际类名
 
        $needsContextualBuild = ! empty($parameters) || ! is_null(
            $this->getContextualConcrete($abstract)
        );
 
       //如果该类型的实例目前作为单例管理,
       //我们将只返回一个现有的实例而不是实例化新的实例,
       //所以开发人员每次都可以继续使用同一个对象实例。
        if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {  //单例已存在直接返回
            return $this->instances[$abstract];
        }
 
        $this->with[] = $parameters;
 
        $concrete = $this->getConcrete($abstract);  //返回给定抽象的具体类型(包括一些关于上下文的绑定操作)
 
        //我们已经准备好实例化绑定注册的具体类型的实例。
        //这将实例化类型,以及递归地解析所有的“嵌套”依赖关系,直到所有问题都得到解决为止。
        if ($this->isBuildable($concrete, $abstract)) {  //判断是否为递归
            $object = $this->build($concrete);  //递归用build(),用make()会死循环
        } else {
            $object = $this->make($concrete);   //非递归用make()
        }
 
        //如果我们定义了这种类型的扩展程序,
        //我们需要将它们旋转并将它们应用到正在构建的对象中。
        //这允许扩展服务,例如更改配置或装饰对象。
        foreach ($this->getExtenders($abstract) as $extender) {  //执行扩展
            $object = $extender($object, $this);
        }
 
        //如果请求的类型被注册为单例,我们将缓存“内存”中的实例,
        //以便稍后返回它,而不必为每一个后续请求创建一个对象的全新实例。
        if ($this->isShared($abstract) && ! $needsContextualBuild) {
            $this->instances[$abstract] = $object;
        }
 
        $this->fireResolvingCallbacks($abstract, $object);  //回调
 
        //返回之前,我们还将解析的标志设置为“true”,
        //并弹出此构建的参数重写。完成这两件事后,我们将准备返回完全构造的类实例。
        $this->resolved[$abstract] = true;
 
        array_pop($this->with);  //移除最后一个键值,也就是$this->with[] = $parameters;

	return $object;
}

上下文具体绑定


    // 获取给定抽象的上下文具体绑定。
    protected function getContextualConcrete($abstract)
    {  
        if (! is_null($binding = $this->findInContextualBindings($abstract))) {
            return $binding;
        }

        // 接下来,我们需要查看上下文绑定是否可以绑定在给定抽象类型的别名下。
        // 因此,我们需要检查此类型是否存在别名,然后遍历这些别名,并检查这些别名的上下文绑定。
        if (empty($this->abstractAliases[$abstract])) {
            return;
        }

        foreach ($this->abstractAliases[$abstract] as $alias) {
            if (! is_null($binding = $this->findInContextualBindings($alias))) {
                return $binding;
            }
        }
    }

    // 在上下文绑定数组中查找是否有给定抽象的上下文具体绑定
    protected function findInContextualBindings($abstract)
    {   
        if (isset($this->contextual[end($this->buildStack)][$abstract])) { // $this->buildStack[] = $concrete;  build();
            return $this->contextual[end($this->buildStack)][$abstract];
        }
    }

    //为给定的抽象获取具体类型
    protected function getConcrete($abstract)
    {   
        if (! is_null($concrete = $this->getContextualConcrete($abstract))) {
            return $concrete;
        }

        // 如果我们没有该类型的注册的解析器或具体的实现,
        // 我们将假设每个类型都是一个具体的名称,并尝试按原样解析它,
        // 因为容器应该能够自动解析具体的名称。
        if (isset($this->bindings[$abstract])) {
            return $this->bindings[$abstract]['concrete'];
        }
        return $abstract;
    }


    // 确定给定的实现是否可建造
    protected function isBuildable($concrete, $abstract)
    {
        return $concrete === $abstract || $concrete instanceof Closure;
    }

    // 获取给定类型的扩展程序回调
    protected function getExtenders($abstract)
    { 
        $abstract = $this->getAlias($abstract);

        if (isset($this->extenders[$abstract])) {
            return $this->extenders[$abstract];
        }

        return [];
    }

    // 确定给定类型是否被共享
    public function isShared($abstract)
    {  
        return isset($this->instances[$abstract]) ||
              (isset($this->bindings[$abstract]['shared']) &&
               $this->bindings[$abstract]['shared'] === true);
    }

回调

    //  激发所有解析回调
    protected function fireResolvingCallbacks($abstract, $object)
    {   // $this->globalResolvingCallbacks[] = $abstract;  所有全局解析回调
        $this->fireCallbackArray($object, $this->globalResolvingCallbacks);  

        $this->fireCallbackArray(
            $object, $this->getCallbacksForType($abstract, $object, $this->resolvingCallbacks) 
            // $this->resolvingCallbacks[$abstract][] = $callback;  所有类的解析回调
        );

        $this->fireAfterResolvingCallbacks($abstract, $object);
    }
    // 激活后面的所有解析回调
    protected function fireAfterResolvingCallbacks($abstract, $object)
    {  // $this->globalAfterResolvingCallbacks[] = $abstract;  所有全局解析后回调
        $this->fireCallbackArray($object, $this->globalAfterResolvingCallbacks);
        $this->fireCallbackArray(
            $object, $this->getCallbacksForType($abstract, $object, $this->afterResolvingCallbacks)
            // $this->afterResolvingCallbacks[$abstract][] = $callback;  所有类的解析后回调
        );
    }

    // 激活对象的回调数组
    protected function fireCallbackArray($object, array $callbacks)
    {   
        foreach ($callbacks as $callback) {
            $callback($object, $this);
        }
    }

    // 获取对象的全部回调
    protected function getCallbacksForType($abstract, $object, array $callbacksPerType)
    {  
        $results = [];
        foreach ($callbacksPerType as $type => $callbacks) {
            if ($type === $abstract || $object instanceof $type) {
                $results = array_merge($results, $callbacks);
            }
        }
        return $results;
    }

  $object = $this->build($concrete);  //递归用build(),用make()会死循环
    // 实例化给定类型的具体实例
    public function build($concrete)
    {   
        // 如果具体类型实际上是一个闭包,我们将只执行它并返回函数的结果,
        // 这允许函数用作解析器,以便更精细地分析这些对象。
        if ($concrete instanceof Closure) {
            return $concrete($this, end($this->with));
        }

        $reflector = new ReflectionClass($concrete);

        // 如果类型是不可实例化的,那么开发人员将尝试解析抽象类型,比如抽象类的接口,并且没有为抽象注册的绑定,
        // 因此我们需要退出。
        if (! $reflector->isInstantiable()) {
            return $this->notInstantiable($concrete);  //   抛出一个异常,即$concrete不是实例化的。
        }

        $this->buildStack[] = $concrete;

        $constructor = $reflector->getConstructor();

        //如果没有构造函数,这意味着没有依赖项,那么我们可以立即解析对象的实例,
        // 而不用从这些容器中解析任何其他类型或依赖项。
        if (is_null($constructor)) {
            array_pop($this->buildStack);

            return new $concrete;
        }

        $dependencies = $constructor->getParameters();

        // 一旦有了构造函数的所有参数,我们就可以创建每个依赖实例,
        // 然后使用反射实例来创建这个类的新实例,将创建的依赖注入其中。
        $instances = $this->resolveDependencies(
            $dependencies
        );

        array_pop($this->buildStack);

        return $reflector->newInstanceArgs($instances);
    }
    // 解析所有来自反射参数的依赖项
    protected function resolveDependencies(array $dependencies)
    {
        $results = [];

        foreach ($dependencies as $dependency) {
            // 如果这个依赖项对这个特定的构建有重写,我们将使用它作为值。
            // 否则,我们将继续执行此一系列决议,并让反射尝试确定结果。
            if ($this->hasParameterOverride($dependency)) {
                $results[] = $this->getParameterOverride($dependency);

                continue;
            }

            // 如果类为空,则意味着依赖项是字符串或其他一些我们不能解决的原始类型,
            // 因为它不是类,并且由于我们无处可去,因此我们将只抛出错误。
            $results[] = is_null($class = $dependency->getClass())
                            ? $this->resolvePrimitive($dependency)
                            : $this->resolveClass($dependency);
        }

        return $results;
    }

    // 确定给定的依赖项是否具有从$this->with中的参数重写
    protected function hasParameterOverride($dependency)
    {
        return array_key_exists($dependency->name, end($this->with));
    }

    // 获取依赖项的参数重写
    protected function getParameterOverride($dependency)
    {
        return end($this->with)[$dependency->name];
    }

    // 解析非类的基元依赖关系
    protected function resolvePrimitive(ReflectionParameter $parameter)
    {
        if (! is_null($concrete = $this->getContextualConcrete('$'.$parameter->name))) {
            return $concrete instanceof Closure ? $concrete($this) : $concrete;
        }

        if ($parameter->isDefaultValueAvailable()) {
            return $parameter->getDefaultValue();
        }

        $this->unresolvablePrimitive($parameter); // 为不可解析的原始类型抛出异常
    }

    // 从容器中解析基于类的依赖项
    protected function resolveClass(ReflectionParameter $parameter)
    {
        try {
            return $this->make($parameter->getClass()->name);
        }

        // 如果我们不能解析类实例,我们将检查该值是否是可选的,
        // 如果是,我们将返回可选的参数值作为依赖项的值,类似于我们如何处理标量。
        catch (BindingResolutionException $e) {
            if ($parameter->isOptional()) {
                return $parameter->getDefaultValue();
            }

            throw $e;
        }
    }

    public function singleton($abstract, $concrete = null)
    { 
        $this->bind($abstract, $concrete, true);
    }

    // 与容器注册绑定
    public function bind($abstract, $concrete = null, $shared = false)
    {   
        // 如果没有给出具体类型,我们将简单地将具体类型设置为抽象类型。
        // 之后,要注册为共享的具体类型,而不必在两个参数中声明它们的类。
        $this->dropStaleInstances($abstract);

        if (is_null($concrete)) {
            $concrete = $abstract;
        }

        // 如果$concrete不是闭包,则意味着它只是一个类名,绑定到这个容器中的抽象类型中,
        // 我们将把它包装在自己的闭包中,以便扩展时更加方便。
        if (! $concrete instanceof Closure) {
            $concrete = $this->getClosure($abstract, $concrete);
        }

        $this->bindings[$abstract] = compact('concrete', 'shared');

        // 如果在此容器中已经解析了抽象类型,我们将触发回弹侦听器,
        // 以便已经解析的任何对象都可以通过侦听器回调更新它们的对象副本。
        if ($this->resolved($abstract)) {
            $this->rebound($abstract);
        }
    }


    // 丢弃旧的实例和别名
    protected function dropStaleInstances($abstract)
    {   
        unset($this->instances[$abstract], $this->aliases[$abstract]);
    }

    // 在构建类型时获得闭包
    protected function getClosure($abstract, $concrete)
    {   
        return function ($container, $parameters = []) use ($abstract, $concrete) {
            $method = ($abstract == $concrete) ? 'build' : 'make';
            return $container->$method($concrete, $parameters);
        };
    }

    // 确定给定的抽象类型是否已被解析
    public function resolved($abstract)
    {  
        if ($this->isAlias($abstract)) {
            $abstract = $this->getAlias($abstract);
        }

        return isset($this->resolved[$abstract]) ||
               isset($this->instances[$abstract]);
    }

    //确定给定字符串是否为别名
    public function isAlias($name)
    {
        return isset($this->aliases[$name]);
    }

    // 如果可用,获取抽象的别名
    public function getAlias($abstract)
    {  
        if (! isset($this->aliases[$abstract])) {
            return $abstract;
        }

        if ($this->aliases[$abstract] === $abstract) {
            throw new LogicException("[{$abstract}] is aliased to itself.");
        }
        return $this->getAlias($this->aliases[$abstract]);
    }

    // 为给定的抽象类型激发“rebound”回调
    protected function rebound($abstract)
    {  
        $instance = $this->make($abstract);

        foreach ($this->getReboundCallbacks($abstract) as $callback) {
            call_user_func($callback, $this, $instance);
        }
    }

    // 获取给定类型的"rebound"回调
    protected function getReboundCallbacks($abstract)
    {   
        if (isset($this->reboundCallbacks[$abstract])) {  // 所有注册的rebound回调
            return $this->reboundCallbacks[$abstract]; 
             // rebinding($abstract, Closure $callback) 
            // $this->reboundCallbacks[$abstract = $this->getAlias($abstract)][] = $callback;
        }

        return [];
    }

这里是整篇文比较难的地方,部分注释是直译的,讲了这么久,其实也执行了$app->withFacades()$app->withEloquent();

其实后面的一些注册和绑定与前面也是类似的,希望能帮到大家!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值