PHP做为世界上最好的编程语音,被普遍的运用到Web开发中。由于其语法和C相似,有着很是平缓的学习曲线,愈来愈多的人使用PHP进行Web产品的快速开发。PHP世界里也涌现了不少开发框架,好比Laravel、ThinkPHP等,但不论何总框架,他们在处理Web请求时的模式都是同样的,本文首先阐述PHP开发Web应用的基本架构,而后分别分析Laravel和ThinkPHP在处理Web请求时的处理流程。php
PHP开发Web应用时因此的请求须要指向具体的入口文件。WebServer是一个内容分发者,他接受用户的请求后,若是是请求的是css、js等静态文件,WebServer会找到这个文件,而后发送给浏览器;若是请求的是/index.php,根据配置文件,WebServer知道这个不是静态文件,须要去找PHP解析器来处理,那么他会把这个请求简单处理后交给PHP解析器。css
WebServer会依据CGI协议,将请求的Url、数据、Http Header等信息发送给PHP解析器,接下来PHP解析器会解析php.ini文件,初始化执行环境,而后处理请求,再以CGI规定的格式返回处理后的结果,退出进程。web server再把结果返回给浏览器。整个处理过程如上图所示。laravel
这里的PHP解析器就是实现了CGI协议的程序,每次请求到来时他会解析php.ini文件,初始化执行环境,这就致使PHP解析器性能低下,因而就出现了CGI的改良升级版FastCGI。FastCGI是一种语言无关的协议,用来沟通程序(如PHP, Python, Java)和Web服务器(Apache2, Nginx), 理论上任何语言编写的程序均可以经过FastCGI来提供Web服务。它的特色是会在动态分配处理进程给请求,以达到提升效率的目的,大多数FastCGI实现都会维护一个进程池。FastCGI会先启一个master进程,解析配置文件,初始化执行环境,而后再启动多个worker进程。当请求过来时,master进程会这个请求传递给一个worker进程,而后当即接受下一个请求。并且当worker进程不够用时,master能够根据配置预先启动几个worker进程等待;固然空闲worker进程太多时,也会自动关闭,这样就提升了性能,节约了系统资源。整个过程FastCGI扮演着对CGI进程进行管理的角色。web
PHP-FPM是一个专门针对PHP实现了FastCGI协议的程序,它实际上就是一个PHP FastCGI进程管理器,负责管理一个进程池,调用PHP解析器来处理来自Web服务器的请求。PHP-FPM可以对php.ini文件的修改进行平滑过分。ajax
新建一个helloworld.php文件,写入下列代码数据库
<?php echo "helloworld,"; echo "this is my first php script."; echo phpinfo(); ?>
配置好WebServer和PHP-FPM等php运行环境后,在浏览器中访问该文件就能够直接获得输出。编程
基于某模式将PHP开发经常使用功能封装实现使开发者快速开发的工具bootstrap
代码重用:定义包、类、函数的放置和加载规则,建议直接整合Composer及其AutoLoad特性。浏览器
请求的分发管理:这个就是路由,Rest风的框架喜欢Rewrite,简单的一点的框架主要经过参数来定位模块和方法所在。服务器
配置文件管理:加载和动态加载配置数据
错误和异常管理:异常捕捉、错误日志记录以及错误码规范。
Layout和模板引擎:如何规划页面布局、widget如何重用、ajax页面如何结合、过时- session如何重定向;数据和模板怎么渲染成HTML,是否压缩和设置过时头。
数据库:如何融入控制器;支持什么样的driver;考虑主从分离的扩展性;以及是否使用ORM
ThinkPHP3.2框架处理流程分析
TP的设计逻辑就是简单粗暴,面对问题解决问题,因此他的处理流程是基于面向过程的思想,而没有采用面向对象的依赖注入、控制反转等思路。他的自动加载、错误处理经过php原生函数的回调来实现。TP处理每次请求要通过四个步骤以下图所示:
index.php是TP的入口文件,全部的请求都由该文件接管,它的工做也很简单主要是引入ThinkPHP入口文件
<?php // 应用入口文件 // 检测PHP环境 if(version_compare(PHP_VERSION,'5.3.0','<')) die('require PHP > 5.3.0 !'); // 开启调试模式 建议开发阶段开启 部署阶段注释或者设为false define('APP_DEBUG',False); // 定义应用目录 define('APP_PATH','./Application/'); // 引入ThinkPHP入口文件 require './ThinkPHP/ThinkPHP.php';
在ThinkPHP.php中主要记录初始运行时间和内存开销,而后完成系统常量判断及定义,最后载入框架引导类(ThinkThink)并执行Think::start方法进行应用初始化。
应用初始化首先设置错误处理机制和自动加载机制
static public function start() { // 注册AUTOLOAD方法 spl_autoload_register('Think\Think::autoload'); // 设定错误和异常处理 register_shutdown_function('Think\Think::fatalError'); set_error_handler('Think\Think::appError'); set_exception_handler('Think\Think::appException');
而后加载相关配置文件和运行模式定义文件,最后调用ThinkApp类的run方法启动应用
此后TP进入请求处理管道,TP为管道中定义了14个事件,每一个事件均可以绑定回调函数,请求到达管道后依次触发这些事件,事件触发后就会调用绑定到事件的回调函数,整个管道的生命周期由app_init开始,由app_end结束。具体实现上,TP将这些事件命名为标签(位),也能够称之为钩子,将回调函数命名为行为,当应用程序运行到标签的时候,就会被拦截下来,统一执行相关的行为。
Laravel框架处理流程分析
Laravel框架使用了统一入口,入口文件:/public/index.php
<?php //自动加载文件设置 require __DIR__.'/../bootstrap/autoload.php'; //初始化服务容器(能够查看一下关于‘服务容器’的相关文档) $app = require_once __DIR__.'/../bootstrap/app.php'; //经过服务容器生成一个kernel类的实例(Illuminate\Contracts\Http\Kernel实际上只是一个接口,真正生成的实例是App\Http\Kernel类,至于怎么把接口和类关联起来,请查看Contracts相关文档) $kernel = $app->make('Illuminate\Contracts\Http\Kernel'); //运行Kernel类的handle方法,主要动做是运行middleware和启动URL相关的Contrller $response = $kernel->handle( $request = Illuminate\Http\Request::capture() ); //控制器返回结果以后的操做,暂时还没看,之后补上 $response->send(); $kernel->terminate($request, $response);
laravel的自动加载,其实也就是Composer的自动加载
Composer根据声明的依赖关系,从相关库的源下载代码文件,并根据依赖关系在 Composer 目录下生成供类自动加载的 PHP 脚本,使用的时候,项目开始处引入 “/vendor/autoload.php” 文件,就能够直接实例化这些第三方类库中的类了。
服务容器,也叫IoC容器,其实包含了依赖注入(DI)和控制反转(IoC)两部分,是Laravel的真正核心。其余的各类功能模块好比 Route(路由)、Eloquent ORM(数据库 ORM 组件)、Request and Response(请求和响应)等等等等,实际上都是与核心无关的类模块提供的,这些类从注册到实例化,最终被使用,其实都是 Laravel 的服务容器负责的。
Kernel实例调用handle方法,意味着Laravel的核心和公用代码已经准备完毕,此项目正式开始运行
代码清单/app/Http/Kernel.php
<?php namespace App\Http; use Illuminate\Foundation\Http\Kernel as HttpKernel; class Kernel extends HttpKernel { //这是在调用路由以前须要启动的中间件,通常都是核心文件,不要修改 protected $middleware = [ 'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode', 'Illuminate\Cookie\Middleware\EncryptCookies', 'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse', 'Illuminate\Session\Middleware\StartSession', 'Illuminate\View\Middleware\ShareErrorsFromSession', 'App\Http\Middleware\VerifyCsrfToken', ]; //这是咱们在router.php文件里面或者Controller文件里面,可使用的Middleware元素,能够自定义加入不少 protected $routeMiddleware = [ 'auth' => 'App\Http\Middleware\Authenticate', 'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth', 'guest' => 'App\Http\Middleware\RedirectIfAuthenticated', 'test' => 'App\Http\Middleware\testMiddleWare', ]; }
能够看到,其实这个文件里面没有handle方法,只有一些属性定义,因此真正的handle方法,实在父类里面实现的
代码清单…/Illuminate/Foundation/Http/Kernel.php
//这个很重要,是项目的一些启动引导项,Kernel的重要步骤中,首先就是启动这些文件的bootstrap方法 protected $bootstrappers = [ //检测环境变量文件是否正常 'Illuminate\Foundation\Bootstrap\DetectEnvironment', //取得配置文件,即把/config/下的全部配置文件读取到容器(app()->make('config')能够查看全部配置信息) 'Illuminate\Foundation\Bootstrap\LoadConfiguration', //绑定一个名字为log的实例到容器,怎么访问??(app()->make('log')) 'Illuminate\Foundation\Bootstrap\ConfigureLogging', //设置异常抓取信息,这个还没仔细看,但大概就是这个意思 'Illuminate\Foundation\Bootstrap\HandleExceptions', //把/config/app.php里面的aliases项利用PHP库函数class_alias建立别名,今后,咱们可使用App::make('app')方式取得实例 'Illuminate\Foundation\Bootstrap\RegisterFacades', //把/config/app.php里面的providers项,注册到容器 'Illuminate\Foundation\Bootstrap\RegisterProviders', //运行容器中注册的全部的ServiceProvider中得boot方法 'Illuminate\Foundation\Bootstrap\BootProviders', ]; //真正的handle方法 public function handle($request) { try { //主要是这行,调度了须要运行的方法 return $this->sendRequestThroughRouter($request); } catch (Exception $e) { $this->reportException($e); return $this->renderException($request, $e); } } protected function sendRequestThroughRouter($request) { $this->app->instance('request', $request); Facade::clearResolvedInstance('request'); //运行上述$bootstrappers里面包含的文件的bootstrap方法,运行的做用,上面已经注释 $this->bootstrap(); //这是在对URL进行调度以前,也就是运行Route以前,进行的一些准备工做 return (new Pipeline($this->app)) ->send($request) //须要运行$this->middleware里包含的中间件 ->through($this->middleware) //运行完上述中间件以后,调度dispatchToRouter方法,进行Route的操做 ->then($this->dispatchToRouter()); } //前奏执行完毕以后,进行Route操做 protected function dispatchToRouter() { return function($request) { $this->app->instance('request', $request); //跳转到Router类的dispatch方法 return $this->router->dispatch($request); }; }
下面就须要根据URL和/app/Http/routes.php文件,进行Route操做
文件清单…/Illuminate/Routing/Router.php
public function dispatch(Request $request) { $this->currentRequest = $request; //在4.2版本里面,Route有一个筛选属性;5.0以后的版本,被Middleware代替 $response = $this->callFilter('before', $request); if (is_null($response)) { //继续调度 $response = $this->dispatchToRoute($request); } $response = $this->prepareResponse($request, $response); //在4.2版本里面,Route有一个筛选属性;5.0以后的版本,被Middleware代替 $this->callFilter('after', $request, $response); return $response; } public function dispatchToRoute(Request $request) { $route = $this->findRoute($request); $request->setRouteResolver(function() use ($route) { return $route; }); $this->events->fire('router.matched', [$route, $request]); $response = $this->callRouteBefore($route, $request); if (is_null($response)) { // 只看这一行,仍是调度文件 $response = $this->runRouteWithinStack( $route, $request ); } $response = $this->prepareResponse($request, $response); $this->callRouteAfter($route, $request, $response); return $response; } protected function runRouteWithinStack(Route $route, Request $request) { // 取得routes.php里面的Middleware节点 $middleware = $this->gatherRouteMiddlewares($route); //这个有点眼熟 return (new Pipeline($this->container)) ->send($request) //执行上述的中间件 ->through($middleware) ->then(function($request) use ($route) { //到Controller类了 return $this->prepareResponse( $request, //run控制器 $route->run($request) ); }); } public function run(Request $request) { $this->container = $this->container ?: new Container; try { if ( ! is_string($this->action['uses'])) return $this->runCallable($request); if ($this->customDispatcherIsBound()) //其实是运行了这行 return $this->runWithCustomDispatcher($request); //其实我是直接想运行这行 return $this->runController($request); } catch (HttpResponseException $e) { return $e->getResponse(); } } //继续调度,最终调度到.../Illuminate/Routing/ControllerDispatcher.php文件的dispatch方法 protected function runWithCustomDispatcher(Request $request) { list($class, $method) = explode('@', $this->action['uses']); $dispatcher = $this->container->make('illuminate.route.dispatcher'); return $dispatcher->dispatch($this, $request, $class, $method); }
文件清单…/Illuminate/Routing/ControllerDispatcher.php
public function dispatch(Route $route, Request $request, $controller, $method) { $instance = $this->makeController($controller); $this->assignAfter($instance, $route, $request, $method); $response = $this->before($instance, $route, $request, $method); if (is_null($response)) { //还要调度 $response = $this->callWithinStack( $instance, $route, $request, $method ); } return $response; } protected function callWithinStack($instance, $route, $request, $method) { //又是Middleware......有没有忘记,官方文档里面Middleware能够加在控制器的构造函数中!!没错,这个Middleware就是在控制器里面申明的 $middleware = $this->getMiddleware($instance, $method); //又是这个,眼熟吧 return (new Pipeline($this->container)) ->send($request) //再次运行Middleware ->through($middleware) ->then(function($request) use ($instance, $route, $method) { 运行控制器,返回结果 return $this->call($instance, $route, $method); }); }
终于到达控制器