精华内容
下载资源
问答
  • Laravel kernel实例化

    2017-03-27 10:04:10
    Laravel Kernel实例化 $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); 实例化 Kernel 在应用进行实例化时,已经初始化了很多的基础操作,所以下面的构造方法将会直接使用服务容器的依赖注入...
        

    Laravel Kernel实例化

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

    实例化 Kernel

    1. 在应用进行实例化时,已经初始化了很多的基础操作,所以下面的构造方法将会直接使用服务容器的依赖注入来解决类之间的依赖关系。

      // \Illuminate\Contracts\Http\Kernel 类构造器依赖 \Illuminate\Contracts\Foundation\Application 和 \Illuminate\Routing\Router,将会通过服务容器来处理依赖关系
      public function __construct(Application $app, Router $router)
      {
          $this->app = $app;
      
          // 主要委托 $router 来处理
          $this->router = $router;
          // 以下均为中间件的设置
          $router->middlewarePriority = $this->middlewarePriority;
      
          foreach ($this->middlewareGroups as $key => $middleware) {
              $router->middlewareGroup($key, $middleware);
          }
      
          foreach ($this->routeMiddleware as $key => $middleware) {
              $router->aliasMiddleware($key, $middleware);
          }
      }
      
      \Illuminate\Contracts\Foundation\Application 的处理:
      make 时通过别名方式直接调用 $this->instances['app']
      
      \Illuminate\Routing\Router 的处理:
      make 时通过别名方式直接调用 $this->bindings['router'] 数组里面 concrete 对应的匿名函数
      Router 依赖 \Illuminate\Contracts\Events\Dispatcher 和 \Illuminate\Container\Container
      public function __construct(Dispatcher $events, Container $container = null)
      {
          $this->events = $events;
          $this->routes = new RouteCollection;
          $this->container = $container ?: new Container;
      }
      
      \Illuminate\Contracts\Events\Dispatcher 的处理:
      make 时通过别名方式直接调用 $this->bindings['events'] 数组里面 concrete 对应的匿名函数
      Dispatcher 依赖 \Illuminate\Contracts\Container\Container
      public function __construct(ContainerContract $container = null)
      {
          $this->container = $container ?: new Container;
      }
      
      \Illuminate\Container\Container 的处理:
      make 时直接调用 $this->instances['Illuminate\Container\Container'] = Object(app)
      \Illuminate\Contracts\Container\Container 的处理:
      make 时调用别名直接调用 $this->instances['app'] = Object(app)
      上面两个一样,没有区别
      

      注意:以上所列出的依赖关系,都直接委托给服务容器进行自动处理了,不需要怕怕

    2. 对 $this->bindings['router'] 和 $this->bindings['events'] 绑定事件的处理,make 时将会直接调用数组键 concrete 对应的匿名函数。

      make 时使用到的代码片段

      ##############################################
      if ($concrete instanceof Closure) {            
          return $concrete($this, end($this->with)); 
      }
      ###############################################
      
      $this->bindings['router'] = [
              'concrete' => function ($app) {
                                  return new Router($app['events'], $app);
                              },
              'shared' => 'true',
          ];
      $router = new Router($app['events'], $app);
      
      \Illuminate\Routing\Router
      public function __construct(Dispatcher $events, Container $container = null)
      {
          $this->events = $events;
          $this->routes = new RouteCollection;
          $this->container = $container ?: new Container;
      }
      

      返回一个 Router 对象,同时会重置 $this->instances['router'] = $router 对象,供下次直接调用。

      $this->bindings['events'] = [
          'concrete' => function ($app) {
                  return (new Dispatcher($app))->setQueueResolver(function () use ($app) {
                      return $app->make(QueueFactoryContract::class);
                  });
                  }
          'shared' => 'true',
      ];
      
      $dispatcher = (new \Illuminate\Events\Dispatcher($app))->setQueueResolver(function () use ($app) {
                      return $app->make(QueueFactoryContract::class);
                  });
      
      Illuminate\Events\Dispatcher:
      public function __construct(ContainerContract $container = null)
      {
          $this->container = $container ?: new Container;
      }
      public function setQueueResolver(callable $resolver)
      {
          $this->queueResolver = $resolver;
      
          return $this;
      }
      

      返回一个 Dispatcher 对象,同时会重置 $this->instances['events'] = $dispatcher 对象,供下次直接调用。

    注意:
    kernel对象是融合了应用和路由的对象,路由又注入了IlluminateEventsDispatcher对象,此为核心对象。

    展开全文
  • Laravel Kernel引导流程分析 代码展示 protected function sendRequestThroughRouter($request) { # $this->app->instance('request', $request); # Facade::clearResolvedInsta...
        

    Laravel Kernel引导流程分析

    代码展示

    protected function sendRequestThroughRouter($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\LoadEnvironmentVariables::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->checkForSpecificEnvironmentFile($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'] = ['a'=>'hello,world!'];
      获取方式: $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, '<?php return '.var_export($manifest, true).';'
          );
      
          return array_merge(['when' => []], $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']);
          }
      }
    展开全文
  • Laravel Kernel实例化后的处理 $response = $kernel->handle( $request = Illuminate\Http\Request::capture() ); 创建并获取Request对象 $request = Illuminate\Http\Request::capture() ...
        

    Laravel Kernel实例化后的处理

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

    创建并获取Request对象

    $request = Illuminate\Http\Request::capture()
    
    \Illuminate\Http\Request extends \Symfony\Component\HttpFoundation\Request
    
    public static function capture()
    {
        static::enableHttpMethodParameterOverride();
    
        return static::createFromBase(SymfonyRequest::createFromGlobals());
    }
    public static function enableHttpMethodParameterOverride()
    {
        self::$httpMethodParameterOverride = true;
    }
    public static function createFromGlobals()
    {
        $server = $_SERVER;
        // CLI mode
        if ('cli-server' === PHP_SAPI) {
            if (array_key_exists('HTTP_CONTENT_LENGTH', $_SERVER)) {
                $server['CONTENT_LENGTH'] = $_SERVER['HTTP_CONTENT_LENGTH'];
            }
            if (array_key_exists('HTTP_CONTENT_TYPE', $_SERVER)) {
                $server['CONTENT_TYPE'] = $_SERVER['HTTP_CONTENT_TYPE'];
            }
        }
        // 创建并返回\Symfony\Component\HttpFoundation\Request对象,实际上是用全局变量来实例化对应的类(可以对全局变量进行安全过滤),在赋予Request对象
        $request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $server);
        // 如果是以PUT|DELETE|PATCH方法进行的标准编码传输方式,就从原始数据的只读流解析数据到request属性(此属性其实对应的是POST键值对,PUT|DELETE|PATCH传输方式会被转成POST方式进行统一处理)
        if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
            && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH'))
        ) {
            parse_str($request->getContent(), $data);
            $request->request = new ParameterBag($data);
        }
    
        return $request;
    }
    private static function createRequestFromFactory(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
    {
        // 如果存在自定义的方法,则调用并返回相应的对象
        if (self::$requestFactory) {
            // 此方法必须返回Symfony\Component\HttpFoundation\Request的对象,否则抛异常
            $request = call_user_func(self::$requestFactory, $query, $request, $attributes, $cookies, $files, $server, $content);
    
            if (!$request instanceof self) {
                throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.');
            }
    
            return $request;
        }
    
        return new static($query, $request, $attributes, $cookies, $files, $server, $content);
    }
    // 创建并返回\Illuminate\Http\Request对象
    public static function createFromBase(SymfonyRequest $request)
    {
        if ($request instanceof static) {
            return $request;
        }
        
        $content = $request->content;
        
        $request = (new static)->duplicate(
            $request->query->all(), $request->request->all(), $request->attributes->all(),
            $request->cookies->all(), $request->files->all(), $request->server->all()
        );
    
        $request->content = $content;
    
        $request->request = $request->getInputSource();
    
        return $request;
    }
    public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null)
    {
        return parent::duplicate($query, $request, $attributes, $cookies, $this->filterFiles($files), $server);
    }
    public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null)
    {
        $dup = clone $this;
        if ($query !== null) {
            $dup->query = new ParameterBag($query);
        }
        if ($request !== null) {
            $dup->request = new ParameterBag($request);
        }
        if ($attributes !== null) {
            $dup->attributes = new ParameterBag($attributes);
        }
        if ($cookies !== null) {
            $dup->cookies = new ParameterBag($cookies);
        }
        if ($files !== null) {
            $dup->files = new FileBag($files);
        }
        if ($server !== null) {
            $dup->server = new ServerBag($server);
            $dup->headers = new HeaderBag($dup->server->getHeaders());
        }
        $dup->languages = null;
        $dup->charsets = null;
        $dup->encodings = null;
        $dup->acceptableContentTypes = null;
        $dup->pathInfo = null;
        $dup->requestUri = null;
        $dup->baseUrl = null;
        $dup->basePath = null;
        $dup->method = null;
        $dup->format = null;
    
        if (!$dup->get('_format') && $this->get('_format')) {
            $dup->attributes->set('_format', $this->get('_format'));
        }
    
        if (!$dup->getRequestFormat(null)) {
            $dup->setRequestFormat($this->getRequestFormat(null));
        }
    
        return $dup;
    }
    public function getContent($asResource = false)
    {
        $currentContentIsResource = is_resource($this->content);
        if (PHP_VERSION_ID < 50600 && false === $this->content) {
            throw new \LogicException('getContent() can only be called once when using the resource return type and PHP below 5.6.');
        }
        // 资源类型时的处理
        if (true === $asResource) {
            if ($currentContentIsResource) {
                rewind($this->content);
    
                return $this->content;
            }
    
            // Content passed in parameter (test)
            if (is_string($this->content)) {
                $resource = fopen('php://temp', 'r+');
                fwrite($resource, $this->content);
                rewind($resource);
    
                return $resource;
            }
    
            $this->content = false;
    
            return fopen('php://input', 'rb');
        }
    
        if ($currentContentIsResource) {
            rewind($this->content);
    
            return stream_get_contents($this->content);
        }
        // 否则读取标准的输入字节流
        if (null === $this->content || false === $this->content) {
            $this->content = file_get_contents('php://input');
        }
    
        return $this->content;
    }
    

    总之:最后创建了一个解析了$_GET, $_POST, $_COOKIE, $_FILES, $_SERVER等变量之后的IlluminateHttpRequest类的对象

    handle处理(核心)

    public function handle($request)
    {
        try {
            $request->enableHttpMethodParameterOverride();
    
            $response = $this->sendRequestThroughRouter($request);
        } catch (Exception $e) {
            $this->reportException($e);
    
            $response = $this->renderException($request, $e);
        } catch (Throwable $e) {
            $this->reportException($e = new FatalThrowableError($e));
    
            $response = $this->renderException($request, $e);
        }
    
        event(new Events\RequestHandled($request, $response));
    
        return $response;
    }
    // 核心方法
    protected function sendRequestThroughRouter($request)
    {
        // 注入请求对象到服务容器,供后期使用
        $this->app->instance('request', $request);
    
        Facade::clearResolvedInstance('request');
        // 启动应用(包括加载设置环境变量、加载配置文件、设置系统错误异常、Facade、启动各服务提供者的引导项等),后续分析
        $this->bootstrap();
        // 委托管道形式处理请求,这个是middleware实现的本质,后续分析
        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }
    public static function clearResolvedInstance($name)
    {
        unset(static::$resolvedInstance[$name]);
    }
    public function bootstrap()
    {
        if (! $this->app->hasBeenBootstrapped()) {
            $this->app->bootstrapWith($this->bootstrappers());
        }
    }
    protected function bootstrappers()
    {
        #####################################################################
        #$bootstrappers = [
        #    \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::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]);
        }
    }
    // 位于Illuminate\Events\Dispatcher文件,$payload用来传参给监听器,$halt表示是否终止后续事件的监听
    public function fire($event, $payload = [], $halt = false)
    {
        return $this->dispatch($event, $payload, $halt);
    }
    public function dispatch($event, $payload = [], $halt = false)
    {
        list($event, $payload) = $this->parseEventAndPayload(
            $event, $payload
        );
        // 若实现了广播类则加入广播队列
        if ($this->shouldBroadcast($payload)) {
            $this->broadcastEvent($payload[0]);
        }
    
        $responses = [];
        // 获取此事件相关的监听事件函数
        foreach ($this->getListeners($event) as $listener) {
            $response = $listener($event, $payload);    // 触发事件
    
            if (! is_null($response) && $halt) {
                return $response;
            }
    
            if ($response === false) {
                break;
            }
    
            $responses[] = $response;
        }
    
        return $halt ? null : $responses;
    }
    public function getListeners($eventName)
    {
        $listeners = isset($this->listeners[$eventName]) ? $this->listeners[$eventName] : [];
    
        $listeners = array_merge(
            $listeners, $this->getWildcardListeners($eventName)
        );
    
        return class_exists($eventName, false)
                    ? $this->addInterfaceListeners($eventName, $listeners)  // 在$listeners增加接口监听事件
                    : $listeners;
    }
    

    $this['events']含义参考[kernel对象化]:

    1. Illuminate\Foundation\Application extends Illuminate\Container\Container
    2. Container implements ArrayAccess,故Application可以按数组形式读取。
    3. public function offsetGet($key) { return $this->make($key); }
    4. public function offsetSet($key, $value) { $this->bind($key, $value instanceof Closure ? $value : function () use ($value) { return $value; });}
    5. public function __get($key) { return $this[$key]; }
    6. public function __set($key, $value) { $this[$key] = $value; }
    7. 所以$this['events'] 就是 $this->instances['events'] 对象($dispatcher);
    8. 其他的$this['config']都是类似的。
    9. $this['events']和$this->events一样,既可以数组形式访问,也可以按对象方式访问。
    

    大体流程是: 创建获取请求对象(包括请求头和请求体)=>注入请求对象到服务容器=>配置系统运行环境=>发送请求

    展开全文
  • php scoket有很大的不确定性(容易断开连接成死进程,以及容易报错),以及laravel 的定时统计(kernel)也是,所以有以下建议,用try{}catch(e){}把该部分代码包裹起来,并记录日志及设计推送消息...

    php scoket有很大的不确定性(容易断开连接成死进程,以及容易报错),以及laravel 的定时统计(kernel)也是,所以有以下建议,用

    try{}catch(e){}

    把该部分代码包裹起来,并记录日志及设计推送消息

    展开全文
  • Learning_Laravel_Kernel Laravel核心代码学习 前言 如果您对Laravel里面的依赖注入,服务绑定,服务解析等等这些东西很好奇,并且觉得只有理解了一个框架的核心代码才能真正把一个框架用好才能写出最佳实践,那么...
  • Laravel开发-kernel

    2019-08-28 12:54:20
    Laravel开发-kernel 基于Swoole的高性能FastCGI服务器。
  • laravel 核心类Kernel

    2019-12-11 10:32:30
    vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php 是laravel处理网络请求的最核心类,在app容器准备好了之后,就会调用本类,之后所有的处理都在此类中。 初始化 调用router,因为router已经...
  • 由于 PHP 可以处理 WEB 和 CLI 两种接口请求,所以 Laravel中设计 HttpKernel 和 ConsoleKernel 来处理这两种类型的请求,Http Kernel...
  • Laravel 5.5框架 核心类Kernel

    千次阅读 2017-12-23 15:23:19
    ;use Illuminate\Foundation\Http\Kernel as HttpKernel;class Kernel extends HttpKernel { /** * The application's global HTTP middleware stack. * * These middlewa
  • Laravel开发-kusikusi-php-kernel Kusikusi PHP内核。
  • 背景:断点调试寻找对应文件,忽略次要步骤,仅描述核心动作,‘/’表示index.php所在目录 地址:index.php $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); ...
  • 万事万物总逃不出一个理字,程序尤其如此,你之所以活得轻松,是因为有人替你负重前行,帮你屏蔽掉了很多乱七八糟的事情,但总有一天你要直面这些事情。程序亦是如此,某个框架你用的很轻松,那是因为底层逻辑已经有...
  • Laravel关于$errors变量的问题

    千次阅读 2018-03-14 16:50:37
    1.laravel视图开发中有些版本中,可能遇到$errors未定义。这种问题参照segment中这个问题:...laravel kernel中 $middlewareGroups-&gt;web 配置有\Illuminate\View\Middleware...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,276
精华内容 1,310
关键字:

laravel的kernel