生命周期 4--handle 前期工作
已创建 $app, $kernel, $request 对象,接下来,终于可以开始处理request对象了(实际上是我想多了)。
kernel处理request(上)
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
public function handle($request)
{
try {
$request->enableHttpMethodParameterOverride(); //1
$response = $this->sendRequestThroughRouter($request); //2
} catch (Exception $e) {
$this->reportException($e); //3
$response = $this->renderException($request, $e); //4
} catch (Throwable $e) {
$this->reportException($e = new FatalThrowableError($e)); //5
$response = $this->renderException($request, $e); //6
}
$this->app['events']->dispatch(
new Events\RequestHandled($request, $response)
); //7
return $response;
}
1 启动表单方法重载
- 前面创建IlluminateRequest对象时已经设置过了,这里再设置一次,可能是出于保险考虑。
2 将$request
通过路由和中间件过滤。
protected function sendRequestThroughRouter($request)
{
$this->app->instance('request', $request); //2.1
Facade::clearResolvedInstance('request'); //2.2
$this->bootstrap(); //2.3
return (new Pipeline($this->app)) //2.4
->send($request) //2.5
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) //2.6
->then($this->dispatchToRouter()); //2.7
}
2.1 将$request
对象绑定到service container中,绑定键为request.
2.2 清除一个已解析过的facade实例(目前还没有解析过的facade实例)
2.3 启动laravel应用
/**
* Bootstrap the application for HTTP requests.
*
* @return void
*/
public function bootstrap()
{
if (! $this->app->hasBeenBootstrapped()) {
$this->app->bootstrapWith($this->bootstrappers()); //2.3.1
}
}
2.3.1 如果laravel应用程序还没有启动,就以给定的bootstrappers来启动。
/**
* Run the given array of bootstrap classes.
*
* @param array $bootstrappers
* @return void
*/
public function bootstrapWith(array $bootstrappers)
{
$this->hasBeenBootstrapped = true; //2.3.1.1
foreach ($bootstrappers as $bootstrapper) {
$this['events']->fire('bootstrapping: '.$bootstrapper, [$this]); //2.3.1.2
$this->make($bootstrapper)->bootstrap($this); //2.3.1.3
$this['events']->fire('bootstrapped: '.$bootstrapper, [$this]); //2.3.1.4
}
}
2.3.1.1 标记应用程序为已启动
接下来是遍历给定的bootstrappers来进行一系列的操作。这些bootstrappers分别为:
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class, //加载环境变量,如.env
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class, //加载配置文件
\Illuminate\Foundation\Bootstrap\HandleExceptions::class, //设置自定义错误和异常处理程序,注册shutdown处理函数。
\Illuminate\Foundation\Bootstrap\RegisterFacades::class, //注册Facades
\Illuminate\Foundation\Bootstrap\RegisterProviders::class, //注册service providers
\Illuminate\Foundation\Bootstrap\BootProviders::class, //执行providers中的boot方法
以LoadEnvironmentVariables::class
为例分析:
2.3.1.2 调用\Illuminate\Events\Dispatcher::fire
方法,也就是调用\Illuminate\Events\Dispatcher::dispatch
方法
public function dispatch($event, $payload = [], $halt = false)
{
// When the given "event" is actually an object we will assume it is an event
// object and use the class as the event name and this event itself as the
// payload to the handler, which makes object based events quite simple.
[$event, $payload] = $this->parseEventAndPayload( //2.3.1.2.1
$event, $payload
);
if ($this->shouldBroadcast($payload)) { //2.3.1.2.2
$this->broadcastEvent($payload[0]);
}
$responses = [];
foreach ($this->getListeners($event) as $listener) { //2.3.1.2.3
$response = $listener($event, $payload);
// If a response is returned from the listener and event halting is enabled
// we will just return this response, and not call the rest of the event
// listeners. Otherwise we will add the response on the response list.
if ($halt && ! is_null($response)) { //2.3.1.2.4
return $response;
}
// If a boolean false is returned from a listener, we will stop propagating
// the event to any further listeners down in the chain, else we keep on
// looping through the listeners and firing every one in our sequence.
if ($response === false) { //2.3.1.2.5
break;
}
$responses[] = $response; //2.3.1.2.6
}
return $halt ? null : $responses; //2.3.1.2.7
}
2.3.1.2.1 解析事件和Payload
protected function parseEventAndPayload($event, $payload)
{
if (is_object($event)) {
[$payload, $event] = [[$event], get_class($event)];
}
return [$event, Arr::wrap($payload)];
}
- 如果第一参数为对象,那么就假定为event object,并使用其类名作为事件名,对象本身作为payload。
- 如果第一参数为字符串,比如
bootstrapping: Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables
,那么第一参数就为事件名,第二参数作为payload.
2.3.1.2.2 判断payload是否有一个broadcastable event,为了集中精力,这里暂时不考虑这种情况。
2.3.1.2.3 获取指定event的所有listeners,为了集中精力,这里暂时不考虑这种情况。
2.3.1.3 解析Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables
对象,并运行其bootstrap方法
/**
* Bootstrap the given application.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function bootstrap(Application $app)
{
if ($app->configurationIsCached()) { //2.3.1.3.1
return;
}
$this->checkForSpecificEnvironmentFile($app); //2.3.1.3.2
try {
(new Dotenv($app->environmentPath(), $app->environmentFile()))->load(); //2.3.1.3.3
} catch (InvalidPathException $e) { //2.3.1.3.4
//
} catch (InvalidFileException $e) { //2.3.1.3.5
echo 'The environment file is invalid: '.$e->getMessage();
die(1);
}
}
2.3.1.3.1 判断配置文件是否有缓存
- 如果存在
bootstrap/cache/config.php
这个文件,就有缓存。 - 如果有缓存,就直接return,什么都不做。
2.3.1.3.2 检查是否有匹配APP_ENV的自定义environment变量文件 存在。
/**
* Detect if a custom environment file matching the APP_ENV exists.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
protected function checkForSpecificEnvironmentFile($app)
{
if ($app->runningInConsole() && ($input = new ArgvInput)->hasParameterOption('--env')) {
if ($this->setEnvironmentFilePath(
$app, $app->environmentFile().'.'.$input->getParameterOption('--env')
)) {
return;
}
}
if (! env('APP_ENV')) {
return;
}
$this->setEnvironmentFilePath(
$app, $app->environmentFile().'.'.env('APP_ENV')
);
}
- 判断应用程序是否运行在控制台模式。。。暂时不考虑。
- 调用全局helpers函数env来获得环境变量APP_ENV的值。
- 如果没有值,直接返回到2.3.1.3.3
- 如果有值,就会将该值对应的文件设置为环境变量文件。
2.3.1.3.3 new \Dotenv\Dotenv
- 读取指定目录下的文件(默认为.env)内容,设置环境变量,这里面的处理工作太多,了解即可。
- 如果不发生异常,2.3.1.3.4和2.3.1.3.5都可以跳过,
Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables
启动完成。
2.3.1.4 与2.3.1.2相似,一个是前,一个是后,都暂时不管。
接下去依次启动其他bootstrappers。
-
解析
Illuminate\Foundation\Bootstrap\LoadConfiguration
对象,启动其bootstrap()/** * Bootstrap the given application. * * @param \Illuminate\Contracts\Foundation\Application $app * @return void */ public function bootstrap(Application $app) { $items = []; //首先看缓存的配置文件是否存在,如果存在,则载入缓存配置文件。 if (file_exists($cached = $app->getCachedConfigPath())) { $items = require $cached; $loadedFromCache = true; } //注册一个config实例 $app->instance('config', $config = new Repository($items)); //如果不是从cache中获取,那么就从$app->configPath()目录下的所有配置文件中读取配置(如果有多层目录,会递归读取配置文件)。最后读出来的配置文件是一个多维数组,保存在$config变量和service container的instances数组中。 if (! isset($loadedFromCache)) { $this->loadConfigurationFiles($app, $config); } //根据app.env的值,设置env的service container绑定 $app->detectEnvironment(function () use ($config) { return $config->get('app.env', 'production'); }); //根据app.timezone的值,设置默认时区。 date_default_timezone_set($config->get('app.timezone', 'UTC')); //Set internal character encoding to UTF-8 mb_internal_encoding('UTF-8'); }
-
解析
Illuminate\Foundation\Bootstrap\HandleExceptions
对象,启动其bootstrap()/** * Bootstrap the given application. * * @param \Illuminate\Contracts\Foundation\Application $app * @return void */ public function bootstrap(Application $app) { $this->app = $app; //设置application instance error_reporting(-1); //Report all PHP errors set_error_handler([$this, 'handleError']); //Sets a user-defined error handler function set_exception_handler([$this, 'handleException']); //Sets a user-defined exception handler function register_shutdown_function([$this, 'handleShutdown']); //Register a function for execution on shutdown if (! $app->environment('testing')) { //如果不是测试环境,就不显示errors信息。 ini_set('display_errors', 'Off'); } }
-
解析
Illuminate\Foundation\Bootstrap\RegisterFacades
对象,运行其bootstrap()/** * Bootstrap the given application. * * @param \Illuminate\Contracts\Foundation\Application $app * @return void */ public function bootstrap(Application $app) { Facade::clearResolvedInstances(); //清除所有解析过的facade实例 Facade::setFacadeApplication($app); //set Facade Application 属性 AliasLoader::getInstance(array_merge( $app->make('config')->get('app.aliases', []), //从config/app.php中获取所有类的别名数组 $app->make(PackageManifest::class)->aliases() //从bootstrap/cache/packages.php中获取所有含有aliases的项 ))->register(); //合并上面两个数组,并将合并的数组注册到auto-loader stack }
-
解析
Illuminate\Foundation\Bootstrap\RegisterProviders
对象,运行其bootstrap()public function bootstrap(Application $app) { $app->registerConfiguredProviders(); //Register all of the configured providers. } public function registerConfiguredProviders() { $providers = Collection::make($this->config['app.providers']) //获取config/app.php中的providers ->partition(function ($provider) { //将上述providers按是否以Illuminate\\开头分为两部分 return Str::startsWith($provider, 'Illuminate\\'); }); $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]); //将上述providers的第二部分替换为bootstrap/cache/packages.php中的providers (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath())) //新建一个ProviderRepository对象 ->load($providers->collapse()->toArray()); //注册上述所有providers } public function load(array $providers) { $manifest = $this->loadManifest(); //载入缓存文件中的providers // First we will load the service manifest, which contains information on all // service providers registered with the application and which services it // provides. This is used to know which services are "deferred" loaders. if ($this->shouldRecompile($manifest, $providers)) { //判断manifest是否需要重新compile $manifest = $this->compileManifest($providers); } // Next, we will register events to load the providers for each of the events // that it has requested. This allows the service provider to defer itself // while still getting automatically loaded when a certain event occurs. foreach ($manifest['when'] as $provider => $events) { //注册跟事件相关的providers $this->registerLoadEvents($provider, $events); } // We will go ahead and register all of the eagerly loaded providers with the // application so their services can be registered with the application as // a provided service. Then we will set the deferred service list on it. foreach ($manifest['eager'] as $provider) { // register all of the eagerly loaded providers $this->app->register($provider); } $this->app->addDeferredServices($manifest['deferred']); //设置容器的deferredServices属性。 }
-
解析
Illuminate\Foundation\Bootstrap\BootProviders
,运行其bootstrap()public function bootstrap(Application $app) { $app->boot(); //Boot the application's service providers. } public function boot() { if ($this->booted) { //避免重复启动 return; } // Once the application has booted we will also fire some "booted" callbacks // for any listeners that need to do work after this initial booting gets // finished. This is useful when ordering the boot-up processes we run. $this->fireAppCallbacks($this->bootingCallbacks); //Call the booting callbacks for the application. array_walk($this->serviceProviders, function ($p) { //调用所有非deferred的provider中的boot方法(这里面就有很多的操作了,比如添加中间件到kernel对象)。 $this->bootProvider($p); }); $this->booted = true; //把程序标记为已启动 $this->fireAppCallbacks($this->bootedCallbacks); //Call the booted callbacks for the application. }
2.4 创建一个Pipeline对象。
public function __construct(Container $container = null)
{
$this->container = $container;
}
2.5 设置Pipeline对象的passable传输对象属性为先前传入的$request
对象。
2.6 判断中间件是否已经被禁用,如果没有(默认情况),就将中间件数组赋给Pipeline对象的管道属性pipes。
小结
- 本文主要过了一遍laravel应用程序启动的流程,可以看到需要用到的环境变量,配置参数,facades,service providers都是在这个阶段完成了注册和启动。实际上还没开始处理reqeust,任重而道远。
- 有一些事件处理什么的,暂时没涉及到,以后再来补充。
- 需要特别留意的主要是各个bootstraper的bootstrap()方法,还有各个service Providers的boot()方法。
- 由于Laravel管道流原理比较复杂,待稍后再发另一篇文章进行说明,不然看起来头大。。
参考
- error_reporting
- set_error_handler
- set_exception_handler
- fopen
- flock
- clearstatcache
- fread
- fclose
- spl_autoload_register
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: