Laravel 生命周期

请求访问的入口文件index.php主要完成了几部分工作,分别是自动加载函数的添加、服务容器的实例化、服务注册、路由加载、请求实例化与路由分发、响应生成与发送。

1. 引入依赖

1
require __DIR__.'/../vendor/autoload.php';

2. 创建 Laravel 应用实例

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

$app为服务容器实例,$app['router']则表示容器中的路由服务。

2.1 创建 Laravel 实例

1
2
3
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
2.1.1 设定项目路径信息
1
2
3
4
5
6
7
8
9
10
11
12
protected function bindPathsInContainer()
{
$this->instance('path', $this->path()); //app_path() app 目录的绝对路径
$this->instance('path.base', $this->basePath()); //base_path() 项目根目录的绝对路径
$this->instance('path.lang', $this->langPath());
$this->instance('path.config', $this->configPath()); //config_path() config 目录的绝对路径
$this->instance('path.public', $this->publicPath()); //public_path() public 目录的绝对路径
$this->instance('path.storage', $this->storagePath()); //storage_path() storage 目录的绝对路径
$this->instance('path.database', $this->databasePath()); //database_path() database 目录的绝对路径
$this->instance('path.resources', $this->resourcePath()); //resource_path() resource 目录的绝对路径
$this->instance('path.bootstrap', $this->bootstrapPath());
}
2.1.2 注册基础绑定
1
2
3
4
5
6
7
8
9
10
11
12
13
protected function registerBaseBindings()
{
static::setInstance($this); //实例化 Application

$this->instance('app', $this); //将 Application 注册到容器中 ???

$this->instance(Container::class, $this); //将 ???
$this->singleton(Mix::class); //实例化 Mix 类 ???

$this->instance(PackageManifest::class, new PackageManifest( //将文件路径注册到容器中 ???
new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
));
}
2.1.3 注册基础服务提供者
1
2
3
4
5
6
protected function registerBaseServiceProviders()
{
$this->register(new EventServiceProvider($this)); //注册事件服务提供者
$this->register(new LogServiceProvider($this)); //注册日志服务提供者
$this->register(new RoutingServiceProvider($this)); //注册路由服务提供者
}
2.1.4 注册内核容器别名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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);
}
}
}

2.2 内核绑定

接下来,我们需要绑定一些重要接口到容器中,以使我们在需要时能够从容器中解析。内核服务可以处理来自 web 与 cli 的请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);

$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);

$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);

3. 从容器中解析 Kernel

1
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public function build($concrete)
{
if ($concrete instanceof Closure) {
return $concrete($this, $this->getLastParameterOverride());
}

try {
$reflector = new ReflectionClass($concrete);
} catch (ReflectionException $e) {
throw new BindingResolutionException("Target class [$concrete] does not exist.", 0, $e);
}

if (! $reflector->isInstantiable()) {
return $this->notInstantiable($concrete);
}

$this->buildStack[] = $concrete;

$constructor = $reflector->getConstructor();

if (is_null($constructor)) {
array_pop($this->buildStack);

return new $concrete;
}

$dependencies = $constructor->getParameters();

try {
$instances = $this->resolveDependencies($dependencies);
} catch (BindingResolutionException $e) {
array_pop($this->buildStack);

throw $e;
}

array_pop($this->buildStack);

return $reflector->newInstanceArgs($instances);
}

在Laravel框架中,解析服务是通过build()函数实现的,一般分为两种情况: 一种是查找对应服务是否被服务提供者注册为实例或提供服务的匿名函数,如果是,则直接进行服务解析;第二种是服务名称没有相应的服务绑定,通过反射机制来动态创建服务。通过反射机制动态创建服务的过程可以分为两个步骤:第一步是通过反射机制获取服务类构造函数的信息,第二步是解决服务类构造函数的依赖问题。首先,通过$reflector = new ReflectionClass($concrete);来创建一个反射类实例,其中$concrete 是类的名称,然后通过$reflector->isInstantiable();判断这个类是否可以实例化,如果不可以则抛出异常,接着通过$constructor = $reflector->getConstructor();来获取类的构造方法,当该类存在构造函数时,返回一个 ReflectionMethod 对象,相当于获取构造函数的反射类,当类不存在构造函数时返回 NULL,最后通过is_null($constructor) 判断是否存在构造函数,如果不存在则直接实例化该类,如果存在则通过$dependencies = $constructor->getParameters();来获取构造函数依赖的输人参数。下面将解决构造函数中依赖参数的问题,进而实现依赖注人。

4. 接收请求并处理

1
2
3
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);

当我们向项目发起请求时,服务器会调用入口文件进行处理,入口文件会通过Illuminate\Http\Request类的静态方法createFromGlobals()实现请求的实例化,然后通过路由进行分发处理,路由会根据请求的地址查找路由表,查找到将通过路由表中对应的相应函数进行请求处理并返回响应,否则将返回404 错误。这就是路由的基本工作过程。

启动 Eloquent ORM 模块阶段需要用到数据库的管理类,即 Illuminate\Database\Capsule\Manager 类,于是添加了对应的命名空间并进行了初始化,然后通过addConnection()函数完成数据库的相关配置并通过bootEloquent()函数完成数据库 Eloquent ORM 模块的启动。在启动完成后,就可以操作数据库了,通过 Eloquent ORM 方式操作数据库需要两个步骤来实现,一是创建模型类,二是通过模型类的方法操作数据库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
use Illuminate\Database\Capsule\Manager;

define('LARAVEL_START', microtime(true));

require __DIR__.'/../vendor/autoload.php';

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

$manager = new Manager();
$manager->addConnection(require '../config/database.php');
$manager->bootEloquent();

$request = \Illuminate\Http\Request::createFromGlobals();
$response = $app['router']->dispatch($request);
$response->send();

// $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

// $response = $kernel->handle(
// $request = Illuminate\Http\Request::capture()
// );

$kernel->terminate($request, $response);

5. 响应请求

1
$response->send();

6. 终止程序

1
$kernel->terminate($request, $response);

未完待续……

因为热爱,所以执着。