Laravel 之道第四章:步调 Laravel 容器 Application 的初始化

栏目: PHP · 发布时间: 6年前

内容简介:Laravel 之道第四章:步调 Laravel 容器 Application 的初始化

Step.019--项目根目录/public/index.php #41

$app = require_once __DIR__.'/../bootstrap/app.php';

执行 项目根目录/bootstrap/app.php 文件,取得返回值,赋值给 $app

下一步,进入 项目根目录/bootstrap/app.php 文件

Step.020--项目根目录/bootstrap/app.php #14

$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

承接上一步,开始 $app 容器的初始化工作

new 一个 Laravel 容器对象,注意这个类还没有在内存中。需要调用上一章注册号的自动加载函数,将类包含到内存中,然后才能 new

realpath 返回一个完整、规范化、去掉 ../ 的绝对路径。 realpath 官方文档

下一步,进入 项目根目录/vendor/composer/ClassLoader.php 文件中,执行之前注册好的自动加载方法,并携带 "Illuminate\Foundation\Application"参数

Step.021--项目根目录/vendor/composer/ClassLoader.php #321

public function loadClass($class)
{  // $class: "Illuminate\Foundation\Application"
    if ($file = $this->findFile($class)) {
        includeFile($file);

        return true;
    }
}

承接上一步,开始加载 "Illuminate\Foundation\Application" 类进入内存中,以供 new 使用

如果 $this->findFile($class) 返回非空字符串,则执行 if 代码块,调用 includFile 方法,将对应的类文件包含执行。

下一步,进入 $this->findFile($class) 方法中,并携带 "Illuminate\Foundation\Application" 参数

Step.022--项目根目录/vendor/composer/ClassLoader.php #338

public function findFile($class)
{
    // class map lookup
    if (isset($this->classMap[$class])) {
        return $this->classMap[$class];  // $class: "Illuminate\Foundation\Application"
    }
    // 由于上面条件满足,下面不执行,暂时先不管

}

承接上一步,开始寻找 "Illuminate\Foundation\Application" 是否在当前对象的 classMap 属性中(此属性为数组),即 $this->classMap[$class] 如果有设置,则返回它。到上一步的 $file 变量

下一步,因为在属性 "classMap" 找到了键为 "Illuminate\Foundation\Application" 的键值对,故返回其值至上一步的 "$file" 变量

Step.023--项目根目录/vendor/composer/ClassLoader.php #322

includeFile($file);  // $file: "D:\www\learn\vendor\composer/../laravel/framework/src/Illuminate/Foundation/Application.php"

承接上一步,"$file" 为真,执行此语句,调用 includeFile 方法,以 "$file" 为参数

下一步,进入 includFile 函数

Step.024--项目根目录/vendor/composer/ClassLoader.php #422

function includeFile($file)  
{
    include $file;  // $file: "D:\www\learn\vendor\composer/../laravel/framework/src/Illuminate/Foundation/Application.php"
}

承接上一步,开始加载 $file 文件到内存中

所包含执行的类文件,如果其类继承的父类和实现的接口类,同样需要自动加载。则重复 loadClass 方法。如果父类又继承了父类和实现接口类,也是需要加载,则同样需要重复 loadClass 方法,直到所有需要的类全部加载到内存。才开始真正实例化,并调用构造函数

下一步,具体加载流程如上所述,此处不细看,直接进入 "Illuminate\Foundation\Application" 类中的构造方法中

Step.025--项目根目录/vendor/laravel/framework/src/Illuminate/Foundation/Application.php #140

public function __construct($basePath = null)
{
    if ($basePath) {  // $basePath: "D:\www\learn"
        $this->setBasePath($basePath);
    }

    $this->registerBaseBindings();

    $this->registerBaseServiceProviders();

    $this->registerCoreContainerAliases();
}

承接上一步,从第 20 步中拿到项目根目录的绝对路径参数作为 $basePath 变量的值,开始容器类的构造方法

上面第一行的意思是如果 $basePath 不为空,则设置项目 PATH 基本环境变量。

接下来的三个方法调用分别是

$this->registerBaseBindings(): 注册应用基本绑定,如 app 指向容器对象

$this->registerBaseServiceProviders(): 注册基本服务提供者

$this->registerCoreContainerAliases(): 注册核心容器别名

下一步,进入 $this->setBasePath($basePath) 看看此方法的执行流程

Step.026--项目根目录/vendor/laravel/framework/src/Illuminate/Foundation/Application.php #267

public function setBasePath($basePath)
{
    $this->basePath = rtrim($basePath, '\/');  // $basePath: "D:\www\learn"

    $this->bindPathsInContainer();

    return $this;
}

承接上一步,开始设置 Laravel 运行的基本路径环境变量

代码中,第一行调用 PHPrtrim 函数,修剪项目根目录路径字符串,去掉尾部的 '/',并赋值给当前对象的 basePath 属性

下一步,进入 $this->bindPathsInContainer() 看一下执行流程

Step.027--项目根目录/vendor/laravel/framework/src/Illuminate/Foundation/Application.php #281

protected function bindPathsInContainer()
{
    $this->instance('path', $this->path());
    $this->instance('path.base', $this->basePath());
    $this->instance('path.lang', $this->langPath());
    $this->instance('path.config', $this->configPath());
    $this->instance('path.public', $this->publicPath());
    $this->instance('path.storage', $this->storagePath());
    $this->instance('path.database', $this->databasePath());
    $this->instance('path.resources', $this->resourcePath());
    $this->instance('path.bootstrap', $this->bootstrapPath());
}

承接上一步,开始调用父类的 instance 方法,注册基本路径环境变量

$this->path() 返回就是 '项目根目录/app' ,即 app 目录路径。同理:

$this->basePath():项目根目录路径

$this->langPath():项目语言包路径,即 '项目根目录/resources/lang'

$this->configPath():项目配置文件路径,即 '项目根目录/config'

$this->publicPath():项目 public 文件夹路径

$this->storagePath():项目 storage 文件夹路径

$this->databasePath():项目 database 文件夹路径

$this->bootstrapPath():项目 bootstrap 文件路径

以上所述路径,皆为绝对路径

下一步,让我们看看 $this->path() 如何执行的

Step.028--项目根目录/vendor/laravel/framework/src/Illuminate/Foundation/Application.php #300

public function path($path = '')
{
    return $this->basePath.DIRECTORY_SEPARATOR.'app'.($path ? DIRECTORY_SEPARATOR.$path : $path);  // $path: ''
}

承接上一步,获取 app 文件夹绝对路径,并返回

DIRECTORY_SEPARATOR:PHP 常量,代表目录分隔符

($path ? DIRECTORY_SEPARATOR.$path : $path):代表三元运算符,因为 $path 参数并未从上一步传入进来,故为空,则 $path 为假,取 $path 与前面的字符串拼接

下一步,将获取的 app 文件夹绝对路径返回到上一步,并执行父类的 instance 方法,下面我们看一下这个 instance 方法

Step.029--项目根目录/vendor/laravel/framework/src/Illuminate/Container/Container.php #385

public function instance($abstract, $instance)
{  // $abstract: "path"; $instance: "D:\www\learn\app"
    $this->removeAbstractAlias($abstract);

    $isBound = $this->bound($abstract);

    unset($this->aliases[$abstract]);

    // We'll check to determine if this type has been bound before, and if it has
    // we will fire the rebound callbacks registered with the container and it
    // can be updated with consuming classes that have gotten resolved here.
    $this->instances[$abstract] = $instance;

    if ($isBound) {
        $this->rebound($abstract);
    }

    return $instance;
}

承接上一步,此方法的作用就是将 $abstract (摘要,或者称标识,只能是字符串类型)作为键,$instance (实例,可以是对象类型),作为值。叠加给当前对象的 instances 属性(此属性的值是数组类型)

下一步,$this->removeAbstractAlias($abstract) 从字面上看,指移除摘要的别名,我们看看具体执行

Step.030--项目根目录/vendor/laravel/framework/src/Illuminate/Container/Container.php #409

protected function removeAbstractAlias($searched)
{  // $searched: "path"
    if (! isset($this->aliases[$searched])) {
        return;
    }

    foreach ($this->abstractAliases as $abstract => $aliases) {
        foreach ($aliases as $index => $alias) {
            if ($alias == $searched) {
                unset($this->abstractAliases[$abstract][$index]);
            }
        }
    }
}

承接上一步,此方法 if 执行段判断有没有摘要的别名,有则执行 foreach 段去移除别名,没有则返回

由于第一次执行,所以 $this->aliases[$searched] 并没有设置,故返回

下一步,继续 instance 方法,看一下 $isBound = $this->bound($abstract)bound 方法返回何值

Step.031--项目根目录/vendor/laravel/framework/src/Illuminate/Foundation/Application.php #745

public function bound($abstract)
{  // $abstract: "path"
    return isset($this->deferredServices[$abstract]) || parent::bound($abstract);
}

承接上一步,此方法意思是,查看 $abstract 这个摘要有没有绑定过。

由于第一次执行故 isset($this->deferredServices[$abstract]) 返回 false,|| 未发生短路,执行 parent::bound($abstract)

下一步,看一下 parent::bound($abstract) 的执行流程

Step.032--项目根目录/vendor/laravel/framework/src/Illuminate/Container/Container.php #151

public function bound($abstract)
{  // $abstract: "path"
    return isset($this->bindings[$abstract]) ||
           isset($this->instances[$abstract]) ||
           $this->isAlias($abstract);
}

承接上一步,由于 $this->bindings[$abstract]$this->instances[$abstract] 均未设置,故未短路,执行 $this->isAlias($abstract)

下一步,看一下 $this->isAlias($abstract)

Step.033--项目根目录/vendor/laravel/framework/src/Illuminate/Container/Container.php #201

public function isAlias($name)
{  // $name: "path"
    return isset($this->aliases[$name]);
}

承接上一步,由于 $this->aliases[$name] 未设置,故返回 false。

从这一步开始返回,一直返回到 instance 方法的 $isBound = $this->bound($abstract) 一行,皆为 false。 故 $isBound 值为 false

下一步,执行 instance 方法的 unset($this->aliases[$abstract]),由于未设置 $this->aliases[$abstract],在这段流程中,没有意义。我们看下一行 $this->instances[$abstract] = $instance;

Step.034--项目根目录/vendor/laravel/framework/src/Illuminate/Container/Container.php #394

$this->instances[$abstract] = $instance// $abstract: "path"; $instance: "D:\www\learn\app"

承接上一步,以 $abstract 为键,$instance 为值,叠加给 instances 属性

此行就是 instance 方法核心了

下一步,根据 $isBound 的值判断,是否调用重绑定方法

Step.035--项目根目录/vendor/laravel/framework/src/Illuminate/Container/Container.php #396

if ($isBound) {  // $isBound: false
    $this->rebound($abstract);
}

return $instance;

承接上一步,从此处开始,一直返回到 instance 方法调用行

由于,$isBound 为 false,不执行 if 里的代码,直接返回 $instance。由第 27 步看并没有变量接受返回值,故此行返回在这没有意义

由于第 27 步都是调用 instance 方法,注册 instances 属性键值对,故直接执行到方法尾部。让我们看看最后执行的结果,上图

Laravel 之道第四章:步调 Laravel 容器 Application 的初始化

下一步,重新回到 Application 的构造方法中,看一下 $this->registerBaseBindings()

Step.036--项目根目录/vendor/laravel/framework/src/Illuminate/Foundation/Application.php #166

protected function registerBaseBindings()
{
    static::setInstance($this);

    $this->instance('app', $this);

    $this->instance(Container::class, $this);

    $this->instance(PackageManifest::class, new PackageManifest(
        new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
    ));
}

承接上一步,开始注册基本绑定

设置容器静态属性 $instance,其值为当前对象本身。分别以 "app""Illuminate\Container\Container" 为键,当前对象本身为值,叠加到 instances 属性中,再以 "Illuminate\Foundation\PackageManifest" 为键,PackageManifest 类对象为值,叠加到 instances 属性中

Container::class: 此句返回的是类的标准全类名(包含完整的命名空间),是字符串类型

下一步,我们看一下 static::setInstance($this)

Step.037--项目根目录/vendor/laravel/framework/src/Illuminate/Container/Container.php #1186

public static function setInstance(ContainerContract $container = null)
{  // $container: 当前对象本身
    return static::$instance = $container;
}

承接上一步,设置 $instance 静态属性为当前对象本身

由于PackageManifest 类实例化所需的参数 Filesystem 类的对象无构造函数,先不用看 Filesystem 类的实例化,注意先自动加载 PackageManifest 类,然后自动加载 Filesystem

$this->basePath() 返回项目根目录的绝对路径,不用看,很简单

$this->getCachedPackagesPath() 返回 Packages 包缓存文件路径,不用看,很简单

下一步,看一下 PackageManifest 类实例化执行的构造函数

Step.038--项目根目录/vendor/laravel/framework/src/Illuminate/Foundation/PackageManifest.php #53

public function __construct(Filesystem $files, $basePath, $manifestPath)
{  // $files: Filesystem 类的实例; $basePath: "D:\www\learn"; $manifestPath: "D:\www\learn\bootstrap/cache/packages.php"
    $this->files = $files;
    $this->basePath = $basePath;
    $this->manifestPath = $manifestPath;
    $this->vendorPath = $basePath.'/vendor';
}

承接上一步,执行 PackageManifest 类的构造函数

可以看出,构造函数,仅做了属性赋值运算,未调用其它方法

是到 registerBaseBindings 方法完成的时候了,来,上图看执行结果

Laravel 之道第四章:步调 Laravel 容器 Application 的初始化

下一步,查看 $this->registerBaseServiceProviders() 执行流程

Step.039--项目根目录/vendor/laravel/framework/src/Illuminate/Foundation/Application.php #184

protected function registerBaseServiceProviders()
{
    $this->register(new EventServiceProvider($this));

    $this->register(new LogServiceProvider($this));

    $this->register(new RoutingServiceProvider($this));
}

承接上一步,开始注册事件服务、日志服务和路由服务

注册方式,采用的是 IoC/DI,即控制反转与依赖注入。具体如何实现的我们详细看代码

实例化 EventServiceProvider 类之前,需调用之前注册好的自动加载方法,从 classMap 属性中获取 EventServiceProvider 类所在绝对路径,然后加载到内存,最后实例化调用构造函数

下一步,看一下 EventServiceProvider 的类构造函数(注:其构造函数是父类的构造函数)

Step.040--项目根目录/vendor/laravel/framework/src/Illuminate/Support/ServiceProvider.php #43

public function __construct($app)
{
    $this->app = $app;
}

承接上一步,把容器对象赋值到当前服务提供者对象中的 app 属性上。

这就叫依赖注入:事件服务应用依赖 Laravel 容器

下一步,看一下第 39 步的 register 方法是执行的

Step.041--项目根目录/vendor/laravel/framework/src/Illuminate/Foundation/Application.php #559

public function register($provider, $options = [], $force = false)
{
    // 这段意思是,查看事件服务对象之前有没有注册过, 有则返回注册过的事件服务对象
    if (($registered = $this->getProvider($provider)) && ! $force) {  // $provider: 事件服务对象; $options: []; $force: false
        return $registered;
    }

    // 这段意思是,如果 $provider 是字符串(但必须是全类名),则根据其解析出对象
    if (is_string($provider)) {  // false 不执行里面的代码
        $provider = $this->resolveProvider($provider);
    }

    // 这段意思是,事件服务对象有没有 register 方法,有则调用
    if (method_exists($provider, 'register')) {
        $provider->register();
    }

    // 这段意思是,事件服务对象有没有 bindings 属性,把属性内的键值对绑定到容器里面
    if (property_exists($provider, 'bindings')) {
        foreach ($provider->bindings as $key => $value) {
            $this->bind($key, $value);
        }
    }

    // 这段意思是,事件服务对象有没有 singletons 属性,把属性内的键值对绑定到容器里面
    if (property_exists($provider, 'singletons')) {
        foreach ($provider->singletons as $key => $value) {
            $this->singleton($key, $value);
        }
    }

    // 记录注册的服务对象,防止重复注册,提高效率
    $this->markAsRegistered($provider);

    // 我这段基本没怎么运行到,具体作用不是很清楚
    if ($this->booted) {
        $this->bootProvider($provider);
    }

    return $provider;
}

承接上一步,开始注册事件服务对象, register 方法具体流程解释看代码中注释

下一步,看一下 $this->getProvider($provider) 如何取得缓存好的对象

Step.042--项目根目录/vendor/laravel/framework/src/Illuminate/Foundation/Application.php #609

public function getProvider($provider)
{  // $provider: 事件服务对象
    return array_values($this->getProviders($provider))[0] ?? null;
}

承接上一步,通过 null 合并运算,如果 ?? 前的值不为null,则返回?? 前的值,否则返回 null

array_values:作用是返回含所有值的索引数组;上面的 [0] 指取数组的第一个值

下一步,看一下 $this->getProviders($provider)

Step.043--项目根目录/vendor/laravel/framework/src/Illuminate/Foundation/Application.php #620

public function getProviders($provider)
{   // $provider: 事件服务对象
    $name = is_string($provider) ? $provider : get_class($provider);  // $name: "Illuminate\Events\EventServiceProvider"

    return Arr::where($this->serviceProviders, function ($value) use ($name) {
        return $value instanceof $name;
    });
}

承接上一步,通过容器中的 serviceProviders 属性来获取相应服务提供对象

$name 赋值哪一行:指如果 $provider 是字符串,则返回其本身,如果不是字符串(一定是对象),则返回对象所属类的全名

下一步, Arr::where() 方法,如果遍历 serviceProviders 属性,找到属于 $name 的对象

Step.044--项目根目录/vendor/laravel/framework/src/Illuminate/Support/Arr.php #604

public static function where($array, callable $callback)
{
    return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
}

承接上一步,利用 php 中的 array_filter 数组元素过滤方法,来取得需要的值

合并两步,等同于下列代码

public function getProviders($provider)
{   // $provider: 事件服务对象
    $name = is_string($provider) ? $provider : get_class($provider);  // $name: "Illuminate\Events\EventServiceProvider"

    return array_filter($this->serviceProviders, function ($value) use ($name) {
        return $value instanceof $name;
    }, ARRAY_FILTER_USE_BOTH);
}

array_filter 函数详解

由于第一次运行,serviceProviders 属性是空数组,故第 41 步中的 $registered 为 null,if 里面的代码不执行

下一步,重点 $provider->register() 即 事件服务对象中的 register 方法

Step.045--项目根目录/vendor/laravel/framework/src/Illuminate/Events/EventServiceProvider.php #15

public function register()
{
    $this->app->singleton('events', function ($app) {
        return (new Dispatcher($app))->setQueueResolver(function () use ($app) {
            return $app->make(QueueFactoryContract::class);
        });
    });
}

承接上一步,从容器对象调用事件服务中的 register 方法,实现控制反转

$this->app 返回容器对象,然后调用容器对象中的 singleton,传入一个 'events' 字符串和一个闭包函数,记住未执行的闭包函数哦

下一步,看一下容器中的 singleton 方法,如何处理控制反转过来的参数

Step.046--项目根目录/vendor/laravel/framework/src/Illuminate/Container/Container.php #345

public function singleton($abstract, $concrete = null)
{  // $abstract: "events"; $concrete: 闭包对象
    $this->bind($abstract, $concrete, true);
}

承接上一步,调用 bind 方法

下一步,看一下 bind 方法执行

Step.047--项目根目录/vendor/laravel/framework/src/Illuminate/Container/Container.php #214

public function bind($abstract, $concrete = null, $shared = false)
{ // $abstract: "events"; $concrete: 闭包对象; $shared: false
    // 从容器中,删除之前以 events 为键的数值对
    $this->dropStaleInstances($abstract);
    // 如果没有传闭包进来,则 $abstract 赋值给 $concrete
    if (is_null($concrete)) {
        $concrete = $abstract;
    }

    // 如果 $concrete 不是闭包对象,则执行里面的代码,获取一个闭包
    if (! $concrete instanceof Closure) {
        $concrete = $this->getClosure($abstract, $concrete);
    }

    // 主要!以 $abstract 为键,$concrete 和 $shared 合并的关联数组为值,叠加到 bindings 属性上
    $this->bindings[$abstract] = compact('concrete', 'shared');

    // 根据 $abstract 或其别名查看 `resolved` 属性和 `instances` 属性有没有赋值过 $abstract 或其别名,有则执行里面的代码
    if ($this->resolved($abstract)) {
        $this->rebound($abstract);
    }
}

承接上一步,调用 bind 方法,将摘要和生成实例的闭包对象绑定到 bindings 属性上去

下一步,看一下 dropStaleInstances 方法执行

Step.048--项目根目录/vendor/laravel/framework/src/Illuminate/Container/Container.php #1126

protected function dropStaleInstances($abstract)
{  // $abstract: "events"
    unset($this->instances[$abstract], $this->aliases[$abstract]);
}

承接上一步,销毁 $this->instances[$abstract]$this->aliases[$abstract]

由于 $concrete 是闭包对象,所以前两个 if 都不执行

返回到第 41 步的 $provider->register() 方法,继续向下执行

由于事件对象不存在 bindingssingletons 属性,所以接下来的两个 if 都不执行

由于第一次运行,故 $this->resolved($abstract) 返回 false

下一步,看一下 $this->markAsRegistered($provider);

Step.049--项目根目录/vendor/laravel/framework/src/Illuminate/Foundation/Application.php #646

protected function markAsRegistered($provider)
{  // $provider: 事件服务对象
    $this->serviceProviders[] = $provider;

    $this->loadedProviders[get_class($provider)] = true;
}

承接上一步,记录注册的事件服务对象

可以返回到第 39 步,由于注册日志服务和路由服务大同小异,直接执行完毕。上图看变量

Laravel 之道第四章:步调 Laravel 容器 Application 的初始化

你可能疑问,为什么 bindings 属性这么多,我记得只有三个呀。那是应为路由服务对象,反向注册了多个闭包对象

我们看一下路由服务提供者的 register 方法

public function register()
{
    $this->registerRouter();  // 注册路由服务
    $this->registerUrlGenerator();  // 注册URL生成器服务
    $this->registerRedirector();  // 注册重定向服务
    $this->registerPsrRequest();  // 注册Psr的request服务
    $this->registerPsrResponse();  // 注册Psr的Response服务
    $this->registerResponseFactory();  // 注册response工厂服务
    $this->registerControllerDispatcher();  // 注册控制器迪调控服务
}

下一步,我看一下 $this->registerCoreContainerAliases();, 初始化的最后一步

Step.050--项目根目录/vendor/laravel/framework/src/Illuminate/Foundation/Application.php #1075

public function registerCoreContainerAliases()
{
    foreach ([
        'app'                  => [\Illuminate\Foundation\Application::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class,  \Psr\Container\ContainerInterface::class],
        'auth'                 => [\Illuminate\Auth\AuthManager::class, \Illuminate\Contracts\Auth\Factory::class],
        'auth.driver'          => [\Illuminate\Contracts\Auth\Guard::class],
        'blade.compiler'       => [\Illuminate\View\Compilers\BladeCompiler::class],
        'cache'                => [\Illuminate\Cache\CacheManager::class, \Illuminate\Contracts\Cache\Factory::class],
        'cache.store'          => [\Illuminate\Cache\Repository::class, \Illuminate\Contracts\Cache\Repository::class],
        'config'               => [\Illuminate\Config\Repository::class, \Illuminate\Contracts\Config\Repository::class],
        'cookie'               => [\Illuminate\Cookie\CookieJar::class, \Illuminate\Contracts\Cookie\Factory::class, \Illuminate\Contracts\Cookie\QueueingFactory::class],
        'encrypter'            => [\Illuminate\Encryption\Encrypter::class, \Illuminate\Contracts\Encryption\Encrypter::class],
        'db'                   => [\Illuminate\Database\DatabaseManager::class],
        'db.connection'        => [\Illuminate\Database\Connection::class, \Illuminate\Database\ConnectionInterface::class],
        'events'               => [\Illuminate\Events\Dispatcher::class, \Illuminate\Contracts\Events\Dispatcher::class],
        'files'                => [\Illuminate\Filesystem\Filesystem::class],
        'filesystem'           => [\Illuminate\Filesystem\FilesystemManager::class, \Illuminate\Contracts\Filesystem\Factory::class],
        'filesystem.disk'      => [\Illuminate\Contracts\Filesystem\Filesystem::class],
        'filesystem.cloud'     => [\Illuminate\Contracts\Filesystem\Cloud::class],
        'hash'                 => [\Illuminate\Hashing\HashManager::class],
        'hash.driver'          => [\Illuminate\Contracts\Hashing\Hasher::class],
        'translator'           => [\Illuminate\Translation\Translator::class, \Illuminate\Contracts\Translation\Translator::class],
        'log'                  => [\Illuminate\Log\LogManager::class, \Psr\Log\LoggerInterface::class],
        'mailer'               => [\Illuminate\Mail\Mailer::class, \Illuminate\Contracts\Mail\Mailer::class, \Illuminate\Contracts\Mail\MailQueue::class],
        'auth.password'        => [\Illuminate\Auth\Passwords\PasswordBrokerManager::class, \Illuminate\Contracts\Auth\PasswordBrokerFactory::class],
        'auth.password.broker' => [\Illuminate\Auth\Passwords\PasswordBroker::class, \Illuminate\Contracts\Auth\PasswordBroker::class],
        'queue'                => [\Illuminate\Queue\QueueManager::class, \Illuminate\Contracts\Queue\Factory::class, \Illuminate\Contracts\Queue\Monitor::class],
        'queue.connection'     => [\Illuminate\Contracts\Queue\Queue::class],
        'queue.failer'         => [\Illuminate\Queue\Failed\FailedJobProviderInterface::class],
        'redirect'             => [\Illuminate\Routing\Redirector::class],
        'redis'                => [\Illuminate\Redis\RedisManager::class, \Illuminate\Contracts\Redis\Factory::class],
        'request'              => [\Illuminate\Http\Request::class, \Symfony\Component\HttpFoundation\Request::class],
        'router'               => [\Illuminate\Routing\Router::class, \Illuminate\Contracts\Routing\Registrar::class, \Illuminate\Contracts\Routing\BindingRegistrar::class],
        'session'              => [\Illuminate\Session\SessionManager::class],
        'session.store'        => [\Illuminate\Session\Store::class, \Illuminate\Contracts\Session\Session::class],
        'url'                  => [\Illuminate\Routing\UrlGenerator::class, \Illuminate\Contracts\Routing\UrlGenerator::class],
        'validator'            => [\Illuminate\Validation\Factory::class, \Illuminate\Contracts\Validation\Factory::class],
        'view'                 => [\Illuminate\View\Factory::class, \Illuminate\Contracts\View\Factory::class],
    ] as $key => $aliases) {
        foreach ($aliases as $alias) {
            $this->alias($key, $alias);
        }
    }
}

承接上一步,执行核心别名注册

上面进行两层 foreach 循环,把数组中的键和值中值,赋值到容器对象的 aliasesabstractAliases 属性中

看一下 alias 方法

public function alias($abstract, $alias)
{
    $this->aliases[$alias] = $abstract;

    $this->abstractAliases[$abstract][] = $alias;
}

这段代码,就是简单是属性数组叠加赋值

现在执行完毕,看一下数据

Laravel 之道第四章:步调 Laravel 容器 Application 的初始化

Laravel 之道第四章:步调 Laravel 容器 Application 的初始化

下一步,返回到 项目根目录/bootstrap/app.php 中,将初始化好的容器对象赋值给 $app

本文章首发在 Laravel China 社区

我们是一群被时空压迫的孩子。 ---- 爱因斯坦

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

基于内容图像检索技术

基于内容图像检索技术

周明全 / 清华大学 / 2007-12 / 28.00元

《基于内容图像检索技术》从理论方法研究与实现技术角度,总结归纳了基于内容图像检索(CBIR)技术的研究与进展,并融入了作者多年来的相关研究与应用成果,系统地介绍了CBIR的主要概念、基本原理、典型方法、实用范例以及新动向。《基于内容图像检索技术》共有12章分为五部分:第一部分是概述,分析了CBIR的体系结构、技术现状和发展趋势;第一部分讨论图像特征提取,给出图像低层特征(颜色、形状、纹理、空间关系......一起来看看 《基于内容图像检索技术》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具