by Liigo. 20151105.
安装,和创建项目,都是通过Composer,简单,略过。
Entry && Kernel
网站入口文件,${Laravel-project}/public/index.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);
生成Request,处理Request(@H_403_68@Http\Kernel::handle()),生成Response,发送Resonse。常规的Web处理流程。
注意 @H_403_68@Illuminate\Contracts\Http\Kernel 只是一个Interface:
interface @H_502_79@Kernel {
public function @H_502_79@bootstrap();
public function @H_502_79@handle($request);
public function @H_502_79@terminate($request,$response);
public function @H_502_79@getApplication();
}
可见入口文件中的代码,@H_403_68@$kernel = $app->make是关键,后面都是调用Kerenl接口的实例对象方法(特别是@H_403_68@Kernel::handle())。
可是 $app 是谁创建的呢?是什么类型呢?
Bootstrap && Application
$app = require_once __DIR__.'/../bootstrap/app.PHP';
引导我们去查看 bootstrap/app.PHP 源码,代码不多,都拿过来看看吧:
$app = new Illuminate\Foundation\Application(
realpath(__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;
第一行创建,最后一行返回。现在我们知道啦,@H_403_68@$app是@H_403_68@Illuminate\Foundation\Application类型的对象。(因为@H_403_68@require_once,@H_403_68@$app是一个单实例对象。中间几行代码后面可能有用,此处暂时忽略。)
自然,@H_403_68@$kernel = $app->make()也就是调用@H_403_68@Applicaton::make()了,代码拿来看一下:
/** * Resolve the given type from the container. * (Overriding Container::make) * @return mixed */
public function @H_502_79@make($abstract,array $parameters = [])
{
$abstract = $this->getAlias($abstract);
if (isset($this->deferredServices[$abstract])) {
$this->loadDeferredProvider($abstract);
}
return parent::make($abstract,$parameters);
}
然而还是不清楚它具体返回哪个类型。
对照前面bootstrap的代码:
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
我们推论得出:@H_403_68@$kernel = $app->make()的真实类型是@H_403_68@App\Http\Kernel(同时实现了接口@H_403_68@Illuminate\Contracts\Http\Kernel)。
这个推论很容易被证实,此处省略细节($app->singleton->bind->alias->make())。
更加具体的创建Kernel对象的细节十分琐碎,我们也一并忽略,有兴趣的可以去看父类方法@H_403_68@Illuminate\Container\Container::make()/build()的代码。
现在我们初步总结一下:先new出Application类型对象
附加:$app是被直接new出来的(@H_403_68@new Illuminate\Foundation\Application),其构造函数做了一些重要的初始化工作,整理代码如下:
public function __construct($basePath = null)
{
$this->instance('app',$this);
$this->instance('Illuminate\Container\Container',$this);
$this->register(new EventServiceProvider($this));
$this->register(new RoutingServiceProvider($this));
// ...
$this->setBasePath($basePath);
}
Kernel::handle() && Request=>Response
下一步我想知道 @H_403_68@$response = $kernel->handle($request) 内部具体做了什么工作,是怎么处理Request并生成Response的。
前面我们已经分析过了,$kernel的真实类型是@H_403_68@App\Http\Kernel,也实现了接口@H_403_68@Illuminate\Contracts\Http\Kernel。
拿来@H_403_68@App\Http\Kernel::handle()的源代码看看,咦,没有此方法。
看其父类同名方法@H_403_68@Illuminate\Foundation\Http\Kernel::handle()代码:
public function handle($request)
{
try {
$request->enableHttpMethodParameterOverride();
$response = $this->sendRequestThroughRouter($request);
} catch (...) {
// ...
}
$this->app['events']->fire('kernel.handled',[$request,$response]);
return $response;
}
上面代码中我感兴趣的只有@H_403_68@sendRequestThroughRouter($request)这个调用,进去看一下:
/** * Send the given request through the middleware / router. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */
protected function @H_502_79@sendRequestThroughRouter($request)
{
$this->app->instance('request',$request);
Facade::clearResolvedInstance('request');
$this->bootstrap(); //! Note: $kernel->bootstrap();
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
}
上面代码中最后一行是我们关注的重点,它把Request送进一个新创建的流水线(Pipeline),
供各个中间件(Middleware)处理,然后再派发给路由器(Router)。下文将展开分析。
Pipeline && Middleware && Router
流水线,中间件,路由器。
Pipleline
流水线@H_403_68@Illuminate\Pipeline\Pipeline实现了接口@H_403_68@Illuminate\Contracts\Pipeline\Pipeline。
其主要接口方法有@H_403_68@send,@H_403_68@through,@H_403_68@via,@H_403_68@then。其中@H_403_68@send设置Request对象,@H_403_68@through设置中间件数组,@H_403_68@via设置方法名(默认为”handle”),@H_403_68@then最终运行此并执行闭包参数(@H_403_68@then的代码极其复杂,各种闭包嵌套,把我搞糊涂了,有兴趣的朋友可以看一下)。
简单推断来说,其工作内容是:依次调用各中间件的@H_403_68@handle方法。特别的,如果某个中间件是闭包,以闭包的形式调用之。
Middleware
中间件@H_403_68@Illuminate\Contracts\Routing\Middleware是一个很简单的接口:
interface Middleware
{
/** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */
public function @H_502_79@handle($request,Closure $next);
}
其文档也极其简陋,看不出太多有价值的信息。第二个参数什么意思,返回值什么意思,鬼才能看出来。可能需要从其他地方入手研究中间件。
Router
将请求派发给Router的调用流程:@H_403_68@$kernel->handle($request) => @H_403_68@$kernel->sendRequestThroughRouter => @H_403_68@$kernel->dispatchToRouter() => @H_403_68@$kernel->router->dispatch($request)。
其中@H_403_68@$kernel->router是创建$kernel时通过构造函数传入的Router对象。
有必要先看一下Router是怎样创建出来的。调用流程:@H_403_68@$app = new Applicaton(__construct) => @H_403_68@$app->register(new RoutingServiceProvider($app)) => @H_403_68@RoutingServiceProvider->register()->registerRouter()。
protected function @H_502_79@registerRouter() {
$this->app['router'] = $this->app->share(function ($app) {
return new Router($app['events'],$app);
});
}
Unresolved
流水线怎么调用中间件,怎么派发给路由器,路由器又是怎么工作的呢?这中间有很多细节还没搞明白。
流水线那里,代码很绕,暂时无法理解。中间件那里,文档太简陋,暂时无法理解。路由器运行原理那里,暂时还没有去看代码。
目前就是这个样子,此文到此为止吧。我想我需要去看一下Laravel 5.1的基础文档,然后再回头去读源码,可能效果会更好。
补记
Bootstrap
我之前在分析@H_403_68@Kernel::handle()时,忽略了一个地方,@H_403_68@Kernel::sendRequestThroughRouter()内部调用了@H_403_68@Kernel::bootstrap()方法:
/** * Bootstrap the application for HTTP requests. * * @return void */
public function @H_502_79@bootstrap()
{
if (! $this->app->hasBeenBootstrapped()) {
$this->app->bootstrapWith($this->bootstrappers());
}
}
@H_403_68@Kernel::bootstrap()内部又调用了@H_403_68@Applicaton::bootstrapWith():
/** * Run the given array of bootstrap classes. * * @param array $bootstrappers * @return void */
public function @H_502_79@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]);
}
}
@H_403_68@Applicaton::bootstrapWith()的参数是@H_403_68@Kernel::bootstrappers(),其初始值为:
/**
* The bootstrap classes for the application.
*
* @var array
*/
protected $bootstrappers = [ 'Illuminate\Foundation\Bootstrap\DetectEnvironment','Illuminate\Foundation\Bootstrap\LoadConfiguration','Illuminate\Foundation\Bootstrap\ConfigureLogging','Illuminate\Foundation\Bootstrap\HandleExceptions','Illuminate\Foundation\Bootstrap\RegisterFacades','Illuminate\Foundation\Bootstrap\RegisterProviders','Illuminate\Foundation\Bootstrap\BootProviders',];
以其中@H_403_68@RegisterProviders为例,其@H_403_68@bootstrap()方法调用了@H_403_68@$app->registerConfiguredProviders():
public function @H_502_79@registerConfiguredProviders() {
$manifestPath = $this->getCachedServicesPath();
(new ProviderRepository($this,new Filesystem,$manifestPath))
->load($this->config['app.providers']);
}
其中@H_403_68@$this->config['app.providers']的值来自于文件@H_403_68@config/app.PHP:
'providers' => [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Foundation\Providers\ArtisanServiceProvider::class,Illuminate\Auth\AuthServiceProvider::class,Illuminate\Broadcasting\BroadcastServiceProvider::class,Illuminate\Bus\BusServiceProvider::class,Illuminate\Cache\CacheServiceProvider::class,Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,Illuminate\Routing\ControllerServiceProvider::class,Illuminate\Cookie\CookieServiceProvider::class,Illuminate\Database\DatabaseServiceProvider::class,Illuminate\Encryption\EncryptionServiceProvider::class,Illuminate\Filesystem\FilesystemServiceProvider::class,Illuminate\Foundation\Providers\FoundationServiceProvider::class,Illuminate\Hashing\HashServiceProvider::class,Illuminate\Mail\MailServiceProvider::class,Illuminate\Pagination\PaginationServiceProvider::class,Illuminate\Pipeline\PipelineServiceProvider::class,Illuminate\Queue\QueueServiceProvider::class,Illuminate\Redis\RedisServiceProvider::class,Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,Illuminate\Session\SessionServiceProvider::class,Illuminate\Translation\TranslationServiceProvider::class,Illuminate\Validation\ValidationServiceProvider::class,Illuminate\View\ViewServiceProvider::class,/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,App\Providers\AuthServiceProvider::class,App\Providers\EventServiceProvider::class,App\Providers\RouteServiceProvider::class,],
大家都看到了,Kernel和Application互相交叉调用,Bootstrap过程又穿插在Request处理过程中间。暂时看不出清晰的思路。
References
Laravel不是一个小项目,逻辑复杂,划分模块之后,布局分散。你很难在短时间内仅仅通过浏览源代码理清框架主体设计思路,尤其是在本人对Laravel还十分陌生的情况下。适可而止是理智的选择。
还是先看一下基础性的文档吧: