通过阅读Laravel源码,配合官方文档介绍,了解Laravel框架的运行流程。本章介绍启动应用程序的时候,初始化服务容器过程中,执行的一系列基础内容和服务的注册,绑定。最后生成一个服务容器
启动应用程序介绍
Laravel应用的所有请求入口是public/index.php文件,首先看该文件中的代码以及简单介绍
文件:bootstrap/app.php
<?php
define('LARAVEL_START', microtime(true));
// 载入Composer生成的自动加载文件
require __DIR__.'/../vendor/autoload.php';
// 启动应用,完成服务容器的实例化和基本注册
$app = require_once __DIR__.'/../bootstrap/app.php';
// 处理请求并返回响应结果
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
$response->send();
$kernel->terminate($request, $response);
入口文件中的代码,简单清晰的展示了请求到响应的整个生命周期的过程,首先是加载Composer的自动加载文件,然后实例化服务容器,并注册核心服务。然后通过kernel内核处理请求并返回响应结果。
服务容器实例化过程
启动Laravel应用程序的入口文件中,通过$app = require_once __DIR__.'/../bootstrap/app.php';
获取服务容器对象,查看实例化服务容器过程中的详细代码片段,然后再逐步了解每一步的意图
文件: bootstrap/app.php
<?php
// 服务容器的实例化
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
// 绑定 接口和对应到
$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
);
return $app;
这段代码中,首先是实例化Illuminate\Foundation\Application类,得到一个对象,即服务容器对象。继续查看实例化这个类的过程中都做了哪些处理。
文件:vendor/laravel/framework/src/Illuminate/Foundation/Application.php
public function __construct($basePath = null)
{
if ($basePath) {
$this->setBasePath($basePath);
}
$this->registerBaseBindings();
$this->registerBaseServiceProviders();
$this->registerCoreContainerAliases();
}
这个构造函数中,依次实现设置应用的基本路径,注册基础绑定,注册基础服务提供者,最后注册核心类的别名
1. 设置基本路径
if ($basePath) {
$this->setBasePath($basePath);
}
为应用设置基本路径
public function setBasePath($basePath)
{
$this->basePath = rtrim($basePath, '\/');
$this->bindPathsInContainer();
return $this;
}
为服务容器绑定所有项目应用的路径
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());
}
2. 注册基础绑定到容器
$this->registerBaseBindings();
这段代码用来向容器中注册绑定基础服务。
文件:vendor/laravel/framework/src/Illuminate/Foundation/Application.php
protected function registerBaseBindings()
{
static::setInstance($this);
$this->instance('app', $this);
$this->instance(Container::class, $this);
$this->singleton(Mix::class);
$this->instance(PackageManifest::class, new PackageManifest(
new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
));
}
static::setInstance($this);
设置当前容器对象为当前的全局容器,后续代码中,可以通过
文件:vendor/laravel/framework/src/Illuminate/Container/Container.php中的静态方法getInstance()直接获取服务容器实例。
public static function getInstance()
{
if (is_null(static::$instance)) {
static::$instance = new static;
}
return static::$instance;
}
$this->instance('app', $this);
,$this->instance(Container::class, $this);
向服务容器的共享实例数组(instances)中注册两个单例服务。在后续的使用中,可以通过此处绑定的别名找到对应的服务容器实例。
同理,代码$this->instance(PackageManifest::class, new PackageManifest( new Filesystem, $this->basePath(), $this->getCachedPackagesPath() ));
也向instances数组中,绑定一个文件系统单例服务。
instance方法的实现内容:
文件:vendor/laravel/framework/src/Illuminate/Container/Container.php
public function instance($abstract, $instance)
{
$this->removeAbstractAlias($abstract);
$isBound = $this->bound($abstract);
unset($this->aliases[$abstract]);
$this->instances[$abstract] = $instance;
if ($isBound) {
$this->rebound($abstract);
}
return $instance;
}
$this->removeAbstractAlias($abstract);
从上下文绑定别名缓存中删除该别名
protected function removeAbstractAlias($searched)
{
// 这个$this->aliases数组存放的是已注册的类名的别名
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]);
}
}
}
}
$isBound = $this->bound($abstract);
判断是否已绑定给定的抽象类型。
$this->instances[$abstract] = $instance;
将传进来的instance对象和abstract接口注册到容器中到共享实例数组($instances)中
3. 注册基础服务提供者
$this->registerBaseServiceProviders();
这行代码实现向服务容器注册最基础的服务提供者。分别为事件服务,日志服务和路由服务
文件:vendor/laravel/framework/src/Illuminate/Foundation/Application.php
protected function registerBaseServiceProviders()
{
$this->register(new EventServiceProvider($this));
$this->register(new LogServiceProvider($this));
$this->register(new RoutingServiceProvider($this));
}
向服务容器提供这些基础服务,首先实例化服务提供者,然后通过容器中的register方法完成注册。注册过程:
文件:vendor/laravel/framework/src/Illuminate/Foundation/Application.php
public function register($provider, $force = false)
{
if (($registered = $this->getProvider($provider)) && ! $force) {
return $registered;
}
if (is_string($provider)) {
$provider = $this->resolveProvider($provider);
}
$provider->register();
if (property_exists($provider, 'bindings')) {
foreach ($provider->bindings as $key => $value) {
$this->bind($key, $value);
}
}
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;
}
首先,判断该服务提供者是否已经被注册过,并且$force = false
,则直接返回注册结果
然后,判断传进来的provider是否为字符串,如果是,应该是一个类名的字符串,则实例化该服务提供者。
拿到未被注册过的服务提供者的对象之后名,调用该对象的register方法
每个服务提供者都会继承Illuminate\Support\ServiceProvider。在该类中有一个register方法,然后每个服务提供者都会各自实现这个register方法,用来提供自己的服务。
$this->markAsRegistered($provider);
标示该服务器提供者已经完成注册。在该方法中会将完成注册的服务提供者的对象存入serviceProviders数组中,然后将该服务器提供者的类名作为key,value设置为true的键值对的形式,存储loadedProviders数组中。
4. 注册核心类别名
public function registerCoreContainerAliases()
{
foreach ([
'app' => [self::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);
}
}
}
这段代码主要是将这个框架用到的核心服务设置一个别名,在服务解析过过程中,需要根据类名或者接口名找到服务别名,然后通过服务别名获取具体的服务。
向服务容器绑定一些重要接口
文件:bootstrap/app.php
$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
);
这里$app
就是服务容器对象,调用其singleton()方法是向服务容器中注册一个共享绑定。这个方法接收的第一个参数作为服务名的接口,第二个参数作为提供服务的类,在方法内部会通过反射机制,将其实例化之后。存入容器。
文件:vendor/laravel/framework/src/Illuminate/Container/Container.php
public function singleton($abstract, $concrete = null)
{
$this->bind($abstract, $concrete, true);
}
这个方法内部只是调用了bind()方法,并且第三个参数传入true,表示作为共享服务绑定到容器。
public function bind($abstract, $concrete = null, $shared = false)
{
$this->dropStaleInstances($abstract);
if (is_null($concrete)) {
$concrete = $abstract;
}
if (! $concrete instanceof Closure) {
$concrete = $this->getClosure($abstract, $concrete);
}
$this->bindings[$abstract] = compact('concrete', 'shared');
if ($this->resolved($abstract)) {
$this->rebound($abstract);
}
}
这个方法中,是将传进来的接口参数和提供服务的回调函数绑定到容器的bindings数组中。
至此,应用程序的准备工作就已经做完了,生成了一个服务容器,并向容器中注册绑定了一些基础服务,以及绑定了一些重要的接口。下一篇文章<<Laravel源码阅读 - 接收请求并返回结果>>介绍应用接收到http请求并返回结果的内容