各位童鞋,今天我们来研究一下 Laravel 的生命周期,看看 Laravel 都在做些什么,话不多说,先贴代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 define('LARAVEL_START' , microtime(true )); 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);
关于自动加载机制,如果有不了解的同学,可以先阅读一下这篇文章: PHP 的自动加载机制 ,至于 Composer 是怎么实现自动加载的,我们这篇文章里也给大家唠唠:Composer的自动加载机制 。
容器初始化 首先我们看看容器初始化都做了什么:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 $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;
Application 就是我们项目里承上启下的服务容器,那实例化的时候都做了些什么呢,依然是贴代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 public function __construct ($basePath = null) { if ($basePath) { $this ->setBasePath($basePath); } $this ->registerBaseBindings(); $this ->registerBaseServiceProviders(); $this ->registerCoreContainerAliases(); }
第一步,设定路径, setBasePath() 可不单单设定一个 base_path 这么简单:
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 45 46 47 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()); } 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; }
可以看到,除了定义 bash_path, Application 还将各个模块的路径映射关系保存到 $application->instances 共享实例中。
接着,我们看看 registerBaseBindings(),这个步骤主要是绑定一些基础的对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 protected function registerBaseBindings () { static ::setInstance($this ); $this ->instance('app' , $this ); $this ->instance(Container::class, $this ); $this ->instance(PackageManifest::class, new PackageManifest( new Filesystem, $this ->basePath(), $this ->getCachedPackagesPath() )); }
再往下看,registerBaseServiceProviders() 就是注册基础的服务提供者了:
1 2 3 4 5 6 protected function registerBaseServiceProviders () { $this ->register(new EventServiceProvider($this )); $this ->register(new LogServiceProvider($this )); $this ->register(new RoutingServiceProvider($this )); }
我们主要看看这个 register() 方法,这是 Laravel 框架精华的部分。
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 public function register ($provider, $force = false) { if (($registered = $this ->getProvider($provider)) && ! $force) { return $registered; } if (is_string($provider)) { $provider = $this ->resolveProvider($provider); } if (method_exists($provider, 'register' )) { $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; }
大家应该都发现了,register、singleton 最终调用的其实都是 bind() 方法将类和实现绑定在 $bindings 的变量里,这个也是 IoC 容器的核心,通过事先绑定类和实现,在需要用到的时候再通过 make 方法实例化。
对于 IoC 容器不理解的同学,我们可以看看这篇:
基础的服务提供者注册完,还需要做下一步,注册服务别名 registerCoreContainerAliases()。这主要是为了在解析服务时,可以不用输入那么长的类名, 可以精简我们的代码,也更好地去阅读。
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 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], ··· ] as $key => $aliases) { foreach ($aliases as $alias) { $this ->alias($key, $alias); } } } public function alias ($abstract, $alias) { $this ->aliases[$alias] = $abstract; $this ->abstractAliases[$abstract][] = $alias; }
注册完服务别名,容器初始化就算完成了。接着容器还会再将几个核心实现绑定到 $bindings 中。
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 );
至此,引入容器的工作就完成了,接下来就该要运行容器了。
运行容器 上面很大的篇幅描述了初始化容器的工作,毕竟工欲善其事,必先利其器。继续往下走:
1 2 3 4 5 6 7 8 9 10 11 12 $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); $response = $kernel->handle( $request = Illuminate\Http\Request::capture() ); $response->send(); $kernel->teminate($request, $response);
还记得这个 kernel 吗?这就是我们在初始化容器时绑定到容器中的,能叫这个名字,必须很关键了。通过容器 make 得到 $kernel 实例,由 $kernel 创造一个大黑盒,在黑盒内处理请求并得到响应,然后将响应发送给客户端。关于这个大黑盒里发生的事情,我们留到后面的文章里再来讲吧。
最后会先将中间件排序,并调用中间件的 terminate() 方法(如果有的话),然后调用容器中 $terminatingCallbacks 里的方法,完成终止容器的步骤。
1 2 3 4 5 6 7 8 9 10 11 12 13 public function terminate ($request, $response) { $this ->terminateMiddleware($request, $response); $this ->app->terminate(); }
最后,框架会调用 Illuminate\Foundation\Bootstrap\HandleException 的handleShutdown() 方法,处理出现的致命错误。这个方法在框架启动时,通过 register_shutdown_function() 注册。
1 2 3 4 5 6 7 public function handleShutdown () { if (! is_null($error = error_get_last()) && $this ->isFatal($error['type' ])) { $this ->handleException($this ->fatalExceptionFromError($error, 0 )); } }
Laravel 的生命周期我们就讲到这里,下期我们再接着讲讲 Laravel 路由和请求。