内容简介: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 运行的基本路径环境变量
代码中,第一行调用 PHP 的 rtrim
函数,修剪项目根目录路径字符串,去掉尾部的 '/',并赋值给当前对象的 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
属性键值对,故直接执行到方法尾部。让我们看看最后执行的结果,上图
下一步,重新回到 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
方法完成的时候了,来,上图看执行结果
下一步,查看 $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);
}
由于第一次运行,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()
方法,继续向下执行
由于事件对象不存在 bindings
和 singletons
属性,所以接下来的两个 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 步,由于注册日志服务和路由服务大同小异,直接执行完毕。上图看变量
你可能疑问,为什么 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 循环,把数组中的键和值中值,赋值到容器对象的 aliases
和 abstractAliases
属性中
看一下 alias
方法
public function alias($abstract, $alias)
{
$this->aliases[$alias] = $abstract;
$this->abstractAliases[$abstract][] = $alias;
}
这段代码,就是简单是属性数组叠加赋值
现在执行完毕,看一下数据
下一步,返回到 项目根目录/bootstrap/app.php
中,将初始化好的容器对象赋值给 $app
我们是一群被时空压迫的孩子。 ---- 爱因斯坦
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Laravel 之道第三章:步调 Composer 自动加载原理
- Laravel 之道第二章:关于 PhpStrom 步调工具的介绍
- C++ 的一大误区——深入解释直接初始化与复制初始化的区别
- 初始化监听端口
- 类初始化导致死锁
- nodejs源码—初始化
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
基于内容图像检索技术
周明全 / 清华大学 / 2007-12 / 28.00元
《基于内容图像检索技术》从理论方法研究与实现技术角度,总结归纳了基于内容图像检索(CBIR)技术的研究与进展,并融入了作者多年来的相关研究与应用成果,系统地介绍了CBIR的主要概念、基本原理、典型方法、实用范例以及新动向。《基于内容图像检索技术》共有12章分为五部分:第一部分是概述,分析了CBIR的体系结构、技术现状和发展趋势;第一部分讨论图像特征提取,给出图像低层特征(颜色、形状、纹理、空间关系......一起来看看 《基于内容图像检索技术》 这本书的介绍吧!