Laravel 5.1 源码阅读
2019独角兽企业重金招聘Python工程师标准>>>
安装,和创建项目,都是通过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(Http\Kernel::handle()
),生成Response,发送Resonse。常规的Web处理流程。
注意 Illuminate\Contracts\Http\Kernel
只是一个Interface:
interface Kernel
{public function bootstrap();public function handle($request);public function terminate($request, $response);public function getApplication();
}
可见入口文件中的代码,$kernel = $app->make
是关键,后面都是调用Kerenl接口的实例对象方法(特别是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;
第一行创建,最后一行返回。现在我们知道啦,$app
是Illuminate\Foundation\Application
类型的对象。(因为require_once
,$app
是一个单实例对象。中间几行代码后面可能有用,此处暂时忽略。)
自然,$kernel = $app->make()
也就是调用Applicaton::make()
了,代码拿来看一下:
/*** Resolve the given type from the container.* (Overriding Container::make)* @return mixed*/
public function 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
);
我们推论得出:$kernel = $app->make()
的真实类型是App\Http\Kernel
(同时实现了接口Illuminate\Contracts\Http\Kernel
)。
这个推论很容易被证实,此处省略细节($app->singleton->bind->alias->make())。
更加具体的创建Kernel对象的细节十分琐碎,我们也一并忽略,有兴趣的可以去看父类方法Illuminate\Container\Container::make()/build()
的代码。
现在我们初步总结一下:先new出Application类型对象app,app创建Kernel类型对象kernel,kernel处理Request、生成Response并发送给客户端。
附加:$app是被直接new出来的(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
下一步我想知道 $response = $kernel->handle($request)
内部具体做了什么工作,是怎么处理Request并生成Response的。
前面我们已经分析过了,$kernel的真实类型是App\Http\Kernel
,也实现了接口Illuminate\Contracts\Http\Kernel
。
拿来App\Http\Kernel::handle()
的源代码看看,咦,没有此方法。
看其父类同名方法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;
}
上面代码中我感兴趣的只有sendRequestThroughRouter($request)
这个调用,进去看一下:
/*** Send the given request through the middleware / router.** @param \Illuminate\Http\Request $request* @return \Illuminate\Http\Response*/
protected function 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
流水线Illuminate\Pipeline\Pipeline
实现了接口Illuminate\Contracts\Pipeline\Pipeline
。
其主要接口方法有send
,through
,via
,then
。其中send
设置Request对象,through
设置中间件数组,via
设置方法名(默认为”handle”),then
最终运行此并执行闭包参数(then
的代码极其复杂,各种闭包嵌套,把我搞糊涂了,有兴趣的朋友可以看一下)。
简单推断来说,其工作内容是:依次调用各中间件的handle
方法。特别的,如果某个中间件是闭包,以闭包的形式调用之。
Middleware
中间件Illuminate\Contracts\Routing\Middleware
是一个很简单的接口:
interface Middleware
{/*** Handle an incoming request.** @param \Illuminate\Http\Request $request* @param \Closure $next* @return mixed*/public function handle($request, Closure $next);
}
其文档也极其简陋,看不出太多有价值的信息。第二个参数什么意思,返回值什么意思,鬼才能看出来。可能需要从其他地方入手研究中间件。
Router
将请求派发给Router的调用流程:$kernel->handle($request)
=> $kernel->sendRequestThroughRouter
=> $kernel->dispatchToRouter()
=> $kernel->router->dispatch($request)
。
其中$kernel->router
是创建$kernel时通过构造函数传入的Router对象。
有必要先看一下Router是怎样创建出来的。调用流程:$app = new Applicaton
(__construct) => $app->register(new RoutingServiceProvider($app))
=> RoutingServiceProvider->register()->registerRouter()
。
protected function registerRouter()
{$this->app['router'] = $this->app->share(function ($app) {return new Router($app['events'], $app);});
}
Unresolved
流水线怎么调用中间件,怎么派发给路由器,路由器又是怎么工作的呢?这中间有很多细节还没搞明白。
流水线那里,代码很绕,暂时无法理解。中间件那里,文档太简陋,暂时无法理解。路由器运行原理那里,暂时还没有去看代码。
目前就是这个样子,此文到此为止吧。我想我需要去看一下Laravel 5.1的基础文档,然后再回头去读源码,可能效果会更好。
补记
Bootstrap
我之前在分析Kernel::handle()
时,忽略了一个地方,Kernel::sendRequestThroughRouter()
内部调用了Kernel::bootstrap()
方法:
/*** Bootstrap the application for HTTP requests.** @return void*/
public function bootstrap()
{if (! $this->app->hasBeenBootstrapped()) {$this->app->bootstrapWith($this->bootstrappers());}
}
Kernel::bootstrap()
内部又调用了Applicaton::bootstrapWith()
:
/*** Run the given array of bootstrap classes.** @param array $bootstrappers* @return void*/
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]);}
}
Applicaton::bootstrapWith()
的参数是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',
];
以其中RegisterProviders
为例,其bootstrap()
方法调用了$app->registerConfiguredProviders()
:
public function registerConfiguredProviders()
{$manifestPath = $this->getCachedServicesPath();(new ProviderRepository($this, new Filesystem, $manifestPath))->load($this->config['app.providers']);
}
其中$this->config['app.providers']
的值来自于文件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还十分陌生的情况下。适可而止是理智的选择。
还是先看一下基础性的文档吧:
- http://laravel.com/docs/5.1/lifecycle
- http://laravel.com/docs/5.1/providers
- http://laravel.com/docs/5.1/container
- http://laravel.com/docs/5.1/middleware
- http://laravel.com/docs/5.1/requests
- http://laravel.com/docs/5.1/responses
- http://laravel.com/docs/5.1/routing
转载于:https://my.oschina.net/botkenni/blog/755712
Laravel 5.1 源码阅读相关推荐
- 应用监控CAT之cat-client源码阅读(一)
CAT 由大众点评开发的,基于 Java 的实时应用监控平台,包括实时应用监控,业务监控.对于及时发现线上问题非常有用.(不知道大家有没有在用) 应用自然是最初级的,用完之后,还想了解下其背后的原理, ...
- centos下将vim配置为强大的源码阅读器
每日杂事缠身,让自己在不断得烦扰之后终于有了自己的清静时光来熟悉一下我的工具,每次熟悉源码都需要先在windows端改好,拖到linux端,再编译.出现问题,还得重新回到windows端,这个过程太耗 ...
- 源码阅读:AFNetworking(十六)——UIWebView+AFNetworking
该文章阅读的AFNetworking的版本为3.2.0. 这个分类提供了对请求周期进行控制的方法,包括进度监控.成功和失败的回调. 1.接口文件 1.1.属性 /**网络会话管理者对象*/ @prop ...
- 源码阅读:SDWebImage(六)——SDWebImageCoderHelper
该文章阅读的SDWebImage的版本为4.3.3. 这个类提供了四个方法,这四个方法可分为两类,一类是动图处理,一类是图像方向处理. 1.私有函数 先来看一下这个类里的两个函数 /**这个函数是计算 ...
- mybatis源码阅读
说下mybatis执行一个sql语句的流程 执行语句,事务等SqlSession都交给了excutor,excutor又委托给statementHandler SimpleExecutor:每执行一次 ...
- 24 UsageEnvironment使用环境抽象基类——Live555源码阅读(三)UsageEnvironment
24 UsageEnvironment使用环境抽象基类--Live555源码阅读(三)UsageEnvironment 24 UsageEnvironment使用环境抽象基类--Live555源码阅读 ...
- Transformers包tokenizer.encode()方法源码阅读笔记
Transformers包tokenizer.encode()方法源码阅读笔记_天才小呵呵的博客-CSDN博客_tokenizer.encode
- 源码阅读笔记 BiLSTM+CRF做NER任务 流程图
源码阅读笔记 BiLSTM+CRF做NER任务(二) 源码地址:https://github.com/ZhixiuYe/NER-pytorch 本篇正式进入源码的阅读,按照流程顺序,一一解剖. 一.流 ...
- 源码阅读:AFNetworking(八)——AFAutoPurgingImageCache
该文章阅读的AFNetworking的版本为3.2.0. AFAutoPurgingImageCache该类是用来管理内存中图片的缓存. 1.接口文件 1.1.AFImageCache协议 这个协议定 ...
最新文章
- python数据分析是什么意思_python数据分析有什么用
- Spaly_Tree 模版
- 修改hostname有几种方式?
- 看懂 IPv6+,这篇就够了
- linux下nmap工具的使用
- c语言 函数的参数传递示例_isunordered()函数与C ++中的示例
- 微信 php收藏功能实现,关于微信小程序收藏功能的实现
- MyBatis查询返回Map类型数据
- 用VB如读取内存地址
- 写程序需要做的几件事
- Ubuntu 20.04美化及QQ、微信、ssh客户端安装
- 用友U8打开起初采购入库单报错
- 深度解析输入偏置电流和输入失调电流的定义来源以及对电路的影响
- 转载|领英开源TonY:构建在Hadoop YARN上的TensorFlow框架
- failed to respond问题查找
- 程序员只能吃青春饭?3条晋升之路帮你摆脱程序员中年魔咒!
- [ICCV 2017] Predicting Visual Exemplars of Unseen Classes for Zero-Shot Learning
- 自制一个图片链接的方法
- X64dbg正确使用姿势
- CDKF、UKF和EKF滤波算法
热门文章
- jithub使用整理资料
- android谷歌反地理,Android反向地理编码显示不出来!
- 7-3 括号匹配 (15 分)
- 作为现代计算机理论的基础的,作为现代计算机理论基础的冯·诺依曼原理和思想是()。...
- SpringDataRedis的简单案例使用
- python学习 day1 (3月1日)
- js字符串方法、数组方法整理
- js 操作table: insertRow(),deleteRow(),insertCell(),deleteCell()方法
- codeforces 758 A
- hdu 4125 Moles(kmp+树状数组)