Laravel Kernel引导流程分析

2022-10-11 22:02:09 130 0
魁首哥

代码展示

protected function send request ThroughRouter($request)
{
 # $this->app->instance('request', $request);
 # Facade::clearResolvedInstance('request');
 // 主要是这句代码
 $this->bootstrap();
 # return (new Pipeline($this->app))
 # ->send($request)
 # ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
 # ->then($this->dispatchToRouter());
}
public function bootstrap()
{
 if (! $this->app->hasBeenBootstrapped()) {
 $this->app->bootstrapWith($this->bootstrappers());
 }
}
protected function bootstrappers()
{
 #####################################################################
 #$bootstrappers = [
 # \Illuminate\ Foundation \Bootstrap\LoadEnvironment var iables::class,
 # \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
 # \Illuminate\Foundation\Bootstrap\HandleExceptions::class, 
 # \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
 # \Illuminate\Foundation\Bootstrap\RegisterProviders::class, 
 # \Illuminate\Foundation\Bootstrap\BootProviders::class,
 #];
 #####################################################################
 return $this->bootstrappers;
}
public function bootstrapWith(array $bootstrappers)
{
 $this->hasBeenBootstrapped = true;
  foreach  ($bootstrappers as $bootstrapper) {
 $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);
 $this->make($bootstrapper)->bootstrap($this);
 $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
 }
} 

$this->make($bootstrapper)->bootstrap($this):会先创建$bootstrapper实例,在执行实例的引导方法,参数为应用实例

处理流程

  1. 加载并设置本次请求的系统 环境变量 (IlluminateFoundationBootstrapLoadEnvironmentVariables)

    public function bootstrap(Application $app)
    {
     // /var/www/laravel/bootstrap/cache/config.php 存在则直接返回
     if ($app->configurationIsCached()) {
     return;
     }
     $this->checkForSpecificEnvironment file ($app);
     try {
     // 委托Dotenv来临时设置此次请求的系统环境变量,默认传参依次为'/var/www/laravel'和'.env'
     (new Dotenv($app->environmentPath(), $app->environmentFile()))->load();
     } catch (InvalidPathException $e) {
     //
     }
    }
    protected function checkForSpecificEnvironmentFile($app)
    {
     // cli模式下,并且存在--env参数(类似命令为: cammond --env=example)
     if (php_sapi_name() == 'cli' && with($input = new ArgvInput)->hasParameterOption('--env')) {
     // 将系统环境文件(类似:/var/www/laravel/.env.example)设置为$app应用的environmentFile属性,供后面使用
     $this->setEnvironmentFilePath(
     $app, $app->environmentFile().'.'.$input->getParameterOption('--env')
     );
     }
     if (! env('APP_ENV')) {
     return;
     }
     $this->setEnvironmentFilePath(
     $app, $app->environmentFile().'.'.env('APP_ENV')
     );
    } 

    (new Dotenv($app->environmentPath(), $app->environmentFile()))->load()

    public function __construct($path, $file = '.env')
    {
     // 类似/var/www/laravel/.env
     $this->filePath = $this->getFilePath($path, $file);
     // 创建加载器,委托Loader处理
     $this->loader = new Loader($this->filePath, true);
    }
    protected function getFilePath($path, $file)
    {
     if (!is_string($file)) {
     $file = '.env';
     }
     $filePath = rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$file;
     return $filePath;
    }
    public function load()
    {
     return $this->loadData();
    }
    protected function loadData($overload = false)
    {
     $this->loader = new Loader($this->filePath, !$overload);
     return $this->loader->load();
    } 

    new Loader($this->filePath, !$overload)

    public function __construct($filePath, $immutable = false)
    {
     $this->filePath = $filePath;
     $this->immutable = $immutable;
    }
    public function load()
    {
     $this->ensureFileIsReadable();
     $filePath = $this->filePath;
     $lines = $this->readLinesFromFile($filePath);
     foreach ($lines as $line) {
     // 如果行不是注释行且含有=号,则进行
     if (!$this->isComment($line) && $this->looksLikeSetter($line)) {
     $this->setEnvironmentVariable($line);
     }
     }
     return $lines;
    }
    // 将文件按行的形式读入到数组并返回
    protected function readLinesFromFile($filePath)
    {
     $autodetect = ini_get('auto_detect_line_endings');
     ini_set('auto_detect_line_endings', '1');
     $lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
     ini_set('auto_detect_line_endings', $autodetect);
     return $lines;
    }
    public function setEnvironmentVariable($name, $value = null)
    {
     // 检测过滤校验环境变量
     list($name, $value) = $this->normaliseEnvironmentVariable($name, $value);
     // 当immutable为真时,不覆盖对应的环境变量
     if ($this->immutable && $this->getEnvironmentVariable($name) !== null) {
     return;
     }
     // apache运行环境下,尝试临时覆盖系统环境变量
     if (function_exists('apache_getenv') && function_exists('apache_setenv') && apache_getenv($name)) {
     apache_setenv($name, $value);
     }
     // 尝试临时设置当前请求的系统环境变量
     if (function_exists('putenv')) {
     putenv("$name=$value");
     }
     // 赋值全局变量
     $_ENV[$name] = $value;
     $_SERVER[$name] = $value;
    } 
  2. 将系统配置文件目录(/var/www/laravel/config)下所有php文件返回的数组载入到$config实例(IlluminateFoundationBootstrapLoadConfiguration)

    public function bootstrap(Application $app)
    {
     $items = [];
     // /var/www/laravel/bootstrap/cache/config.php文件[配置文件的缓存合集,加快加载速度]存在则载入,并标记已加载
     if (file_exists($cached = $app->getCachedConfigPath())) {
     $items = require $cached;
     $loadedFromCache = true;
     }
     // 构建config实例,并注入到服务容器
     $app->instance('config', $config = new Repository($items));
     if (! isset($loadedFromCache)) {
     // 将系统的配置文件载入到$config实例
     $this->loadConfigurationFiles($app, $config);
     }
     // 设置$this['env']为系统环境变量app.env,没有则默认为production
     $app->detectEnvironment(function () use ($config) {
     return $config->get('app.env', 'production');
     });
     date_default_timezone_set($config->get('app.timezone', 'UTC'));
     mb_internal_encoding('UTF-8');
    }
    $config = new \Illuminate\Config\Repository($items)
    public function __construct(array $items = [])
    {
     $this->items = $items;
    }
    protected function loadConfigurationFiles(Application $app, RepositoryContract $repository)
    {
     foreach ($this->getConfigurationFiles($app) as $key => $path) {
     // 此操作将在$repository实例里面构造一个多维数组属性$this->items,值为相应的系统配置文件返回的数组,后续可以直接通过get获取
     $repository->set($key, require $path);
     }
    }
    /** $files数组形式如下
     [
     'app' => '/var/www/laravel/config/app.php',
     'auth' => '/var/www/laravel/config/auth.php',
     'xx.file' => '/var/www/laravel/config/xx/file.php',
     'xx.yy.file' => '/var/www/laravel/config/xx/yy/file.php',
     ]
    */
    protected function getConfigurationFiles(Application $app)
    {
     $files = [];
     // 系统配置文件的路径(/var/www/laravel/config)
     $configPath = realpath($app->configPath());
     // 文件相关的操作委托给Finder类(很强大)来处理,Finder实现了IteratorAggregate的getIterator方法
     foreach (Finder::create()->files()->name('*.php')->in($configPath) as $file) {
     // 迭代/var/www/laravel/config下面嵌套的层层子目录构造成.形式的目录
     $directory = $this->getNestedDirectory($file, $configPath);
     $files[$directory.basename($file->getRealPath(), '.php')] = $file->getRealPath();
     }
     return $files;
    }
    $repository->set($key, require $path)
    // 构造将.形式转变为相应层级的数组$this->items。比如:$key='xx.yy.file',$value='/var/www/laravel/config/xx/yy/file.php',将会构建为:$this->items['xx']['yy']['file'] = $value返回的数组。
    public function set($key, $value = null)
    {
     $keys = is_array($key) ? $key : [$key => $value];
     foreach ($keys as $key => $value) {
     Arr::set($this->items, $key, $value);
     }
    } 

    根据默认的系统配置文件目录,以上操作的结果如下:

    $config实例(new Repository)里面的$this->items数组属性,后期可以通过$config->get()来获取

    $this->items['app'] = '/var/www/laravel/config/app.php';
    $this->items['auth'] = '/var/www/laravel/config/auth.php';
    $this->items['broadcasting'] = '/var/www/laravel/config/broadcasting.php';
    $this->items['cache'] = '/var/www/laravel/config/cache.php';
    $this->items['database'] = '/var/www/laravel/config/database.php';
    $this->items['filesystems'] = '/var/www/laravel/config/filesystems.php';
    $this->items['mail'] = '/var/www/laravel/config/mail.php';
    $this->items['queue'] = '/var/www/laravel/config/queue.php';
    $this->items['services'] = '/var/www/laravel/config/services.php';
    $this->items['session'] = '/var/www/laravel/config/session.php';
    $this->items['view'] = '/var/www/laravel/config/view.php';
    假如有这样的文件(/var/www/laravel/config/xx/yy/zz/file.php),返回['a'=>'hello,world!']数组
    将得到:$this->items['xx']['yy']['zz']['file'] = '/var/www/laravel/config/xx/yy/zz/file.php';
    获取方式: $config->get('xx.yy.zz.file.a', $default),直接返回'hello,world!'; 
  3. 设置系统的错误异常等处理事件(IlluminateFoundationBootstrapHandleExceptions)

    public function bootstrap(Application $app)
    {
     $this->app = $app;
     error_reporting(-1);
     set_error_handler([$this, 'handleError']);
     set_exception_handler([$this, 'handleException']);
     register_shutdown_function([$this, 'handleShutdown']);
     if (! $app->environment('testing')) {
     ini_set('display_errors', 'Off');
     }
    }
    public function handleError($level, $message, $file = '', $line = 0, $context = [])
    {
     if (error_reporting() & $level) {
     throw new ErrorException($message, 0, $level, $file, $line);
     }
    }
    public function handleException($e)
    {
     if (! $e instanceof Exception) {
     $e = new FatalThrowableError($e);
     }
     $this->getExceptionHandler()->report($e);
     if ($this->app->runningInConsole()) {
     $this->renderForConsole($e);
     } else {
     $this->renderHttpResponse($e);
     }
    }
    // 核心代码,获取的\App\Exceptions\Handle实例
    protected function getExceptionHandler()
    {
     // make时将会直接调用$this->bindings['Illuminate\Contracts\Debug\ExceptionHandler']['concrete'](此代码位于/var/www/laravel/bootstrap/app.php,应用实例化后,直接注入到服务容器的几个单例),返回\App\Exceptions\Handle实例,并将此实例注入到服务容器[参考]
     return $this->app->make(ExceptionHandler::class);
    }
    protected function renderHttpResponse(Exception $e)
    {
     $this->getExceptionHandler()->render($this->app['request'], $e)->send();
    }
    // \App\Exceptions\Handle
    public function render($request, Exception $e)
    {
     $e = $this->prepareException($e);
     if ($e instanceof HttpResponseException) {
     return $e->getResponse();
     } elseif ($e instanceof AuthenticationException) {
     return $this->unauthenticated($request, $e);
     } elseif ($e instanceof ValidationException) {
     return $this->convertValidationExceptionToResponse($e, $request);
     }
     return $this->prepareResponse($request, $e);
    }
    protected function renderHttpException(HttpException $e)
    {
     $status = $e->getStatusCode();
     view()->replaceNamespace('errors', [
     resource_path('views/errors'),
     __DIR__.'/views',
     ]);
     if (view()->exists("errors::{$status}")) {
     return response()->view("errors::{$status}", ['exception' => $e], $status, $e->getHeaders());
     } else {
     return $this->convertExceptionToResponse($e);
     }
    }
    public function handleShutdown()
    {
     if (! is_null($error = error_get_last()) && $this->isFatal($error['type'])) {
     $this->handleException($this->fatalExceptionFromError($error, 0));
     }
    } 
  4. Facades的注册(IlluminateFoundationBootstrapRegisterFacades)

    public function bootstrap(Application $app)
    {
     Facade::clearResolvedInstances();
     Facade::setFacadeApplication($app);
     // 将配置文件/var/www/laravel/config/app.php返回数组的键为aliases的值赋给\Illuminate\Foundation\AliasLoader的aliases属性,并进行注册
     AliasLoader::getInstance($app->make('config')->get('app.aliases', []))->register();
    }
    public  static  function clearResolvedInstances()
    {
     static::$resolvedInstance = [];
    }
    public static function setFacadeApplication($app)
    {
     static::$app = $app;
    }
    \Illuminate\Foundation\AliasLoader
    public static function getInstance(array $aliases = [])
    {
     if (is_null(static::$instance)) {
     return static::$instance = new static($aliases);
     }
     $aliases = array_merge(static::$instance->getAliases(), $aliases);
     static::$instance->setAliases($aliases);
     return static::$instance;
    }
    private function __construct($aliases)
    {
     $this->aliases = $aliases;
    }
    public function getAliases()
    {
     return $this->aliases;
    }
    public function setAliases(array $aliases)
    {
     $this->aliases = $aliases;
    }
    public function register()
    {
     if (! $this->registered) {
     $this->prependToLoaderStack();
     $this->registered = true;
     }
    }
    protected function prependToLoaderStack()
    {
     // 将$this->load注册到自动加载器的最前面,失败时抛异常
     spl_autoload_register([$this, 'load'], true, true);
    }
    public function load($alias)
    {
     // $facadeNamespace = 'Facades\\',估计是框架内部使用的,以后再看吧
     if (static::$facadeNamespace && strpos($alias, static::$facadeNamespace) === 0) {
     $this->loadFacade($alias);
     return true;
     }
     if (isset($this->aliases[$alias])) {
     return class_alias($this->aliases[$alias], $alias);
     }
    } 

    Facade的本质

    实际上是通过$app->make(‘config’)->get(‘app.aliases’, [])取出config/app.php文件里面的aliases数组并实例化AliasLoader,再将AliasLoader->load方法放到spl自动加载器最前面,最后通过class_alias($this->aliases[$alias], $alias)。当调用Cache::Method时,会触发Facdes的__callStatic魔术方法,此方法会调用相应实例里面的方法。

  5. 注册基本的服务提供者(IlluminateFoundationBootstrapRegisterProviders)

    public function bootstrap(Application $app)
    {
     $app->registerConfiguredProviders();
    }
    public function registerConfiguredProviders()
    {
     (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
     ->load($this->config['app.providers']);
    }
    public function getCachedServicesPath()
    {
     return $this->bootstrapPath().'/cache/services.php';
    }
    // 先取services缓存文件,再对\Illuminate\Foundation\ProviderRepository进行实例化,随后加载系统配置文件(./config/app.php)里面的providers数组
    (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
     ->load($this->config['app.providers'])
    public function __construct(ApplicationContract $app, Filesystem $files, $manifestPath)
    {
     $this->app = $app;
     $this->files = $files;
     $this->manifestPath = $manifestPath;
    }
    public function load(array $providers)
    {
     $manifest = $this->loadManifest();
     if ($this->shouldRecompile($manifest, $providers)) {
     $manifest = $this->compileManifest($providers);
     }
     foreach ($manifest['when'] as $provider => $events) {
     $this->registerLoadEvents($provider, $events);
     }
     foreach ($manifest['eager'] as $provider) {
     // 直接注册服务(将直接调用服务的register方法)
     $this->app->register($provider);
     }
     $this->app->addDeferredServices($manifest['deferred']);
    }
    public function loadManifest()
    {
     if ($this->files->exists($this->manifestPath)) {
     $manifest = $this->files->getRequire($this->manifestPath);
     if ($manifest) {
     return array_merge(['when' => []], $manifest);
     }
     }
    }
    public function shouldRecompile($manifest, $providers)
    {
     return is_null($manifest) || $manifest['providers'] != $providers;
    }
    protected function compileManifest($providers)
    {
     $manifest = $this->freshManifest($providers);
     foreach ($providers as $provider) {
     $instance = $this->createProvider($provider);
     // 延迟加载的服务
     if ($instance->isDeferred()) {
     foreach ($instance->provides() as $service) {
     $manifest['deferred'][$service] = $provider;
     }
     // 注册延迟的事件
     $manifest['when'][$provider] = $instance->when();
     }
     // 即时加载的服务
     else {
     $manifest['eager'][] = $provider;
     }
     }
     return $this->writeManifest($manifest);
    }
    protected function freshManifest(array $providers)
    {
     return ['providers' => $providers, 'eager' => [], 'deferred' => []];
    }
    public function createProvider($provider)
    {
     return new $provider($this->app);
    }
    public function isDeferred()
    {
     return $this->defer;
    }
    public function writeManifest($manifest)
    {
     if (! is_writable(dirname($this->manifestPath))) {
     throw new Exception('The bootstrap/cache directory must be present and writable.');
     }
     $this->files->put(
     $this->manifestPath, ' []], $manifest);
    }
    protected function registerLoadEvents($provider, array $events)
    {
     if (count($events) < 1) {
     return;
     }
     $this->app->make('events')->listen($events, function () use ($provider) {
     $this->app->register($provider);
     });
    }
    public function addDeferredServices(array $services)
    {
     $this->deferredServices = array_merge($this->deferredServices, $services);
    } 

    大致流程

    通过/var/www/laravel/bootstrap/cache/services.php等实例化IlluminateFoundationProviderRepository,并加载$this->config[‘app.providers’]数组。实例化app.providers各服务提供者,根据其defer属性将服务进行分类(延迟服务|即时服务),从而得到一个$manifest数组(格式如services.php,延迟处理:deferred=>注册延迟的服务,以后再进行调用;when=>注册延迟的事件;即时处理:eager=>直接进行注册调用等),并重新写入到services.php,然后根据此文件进行相应的处理。

  6. 启动服务提供者的boot方法等操作(IlluminateFoundationBootstrapBootProviders)

    public function bootstrap(Application $app)
    {
     $app->boot();
    }
    public function boot()
    {
     if ($this->booted) {
     return;
     }
     // 可以通过应用的booting方法来注册服务启动前的事件监听者
     $this->fireAppCallbacks($this->bootingCallbacks);
     // 尝试调用所有的服务提供者的boot方法
     array_walk($this->serviceProviders, function ($p) {
     $this->bootProvider($p);
     });
     $this->booted = true;
     // 可以通过应用的booted方法来注册服务启动后的事件监听者,若已经启用了,则直接出发事件
     $this->fireAppCallbacks($this->bootedCallbacks);
    }
    protected function fireAppCallbacks(array $callbacks)
    {
     foreach ($callbacks as $callback) {
     call_user_func($callback, $this);
     }
    }
    protected function bootProvider(ServiceProvider $provider)
    {
     if (method_exists($provider, 'boot')) {
     return $this->call([$provider, 'boot']);
     }
    } 
收藏
分享
海报
0 条评论
130
上一篇:NIO、BIO、AIO 与 PHP 实现 下一篇:laravel框架发送邮件配置

本站已关闭游客评论,请登录或者注册后再评论吧~

忘记密码?

图形验证码