php如何处理查询请求,PHP处理WEB请求的流程

PHP作为世界上最好的编程语音,被广泛的运用到Web开发中。因为其语法和C类似,有着非常平缓的学习曲线,越来越多的人使用PHP进行Web产品的快速开发。PHP世界里也涌现了很多开发框架,比如Laravel、ThinkPHP等,但不论何总框架,他们在处理Web请求时的模式都是一样的,本文首先阐述PHP开发Web应用的基本架构,然后分别分析Laravel和ThinkPHP在处理Web请求时的处理流程。

PHP开发Web应用的基本架构

PHP开发Web应用时所以的请求需要指向具体的入口文件。WebServer是一个内容分发者,他接受用户的请求后,如果是请求的是css、js等静态文件,WebServer会找到这个文件,然后发送给浏览器;如果请求的是/index.php,根据配置文件,WebServer知道这个不是静态文件,需要去找PHP解析器来处理,那么他会把这个请求简单处理后交给PHP解析器。

652d118b63a97152164bf54f84a87b8c.png

WebServer会依据CGI协议,将请求的Url、数据、Http Header等信息发送给PHP解析器,接下来PHP解析器会解析php.ini文件,初始化执行环境,然后处理请求,再以CGI规定的格式返回处理后的结果,退出进程。web server再把结果返回给浏览器。整个处理过程如上图所示。

FastCGI

这里的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进程进行管理的角色。

PHP-FPM

PHP-FPM是一个专门针对PHP实现了FastCGI协议的程序,它实际上就是一个PHP FastCGI进程管理器,负责管理一个进程池,调用PHP解析器来处理来自Web服务器的请求。PHP-FPM能够对php.ini文件的修改进行平滑过度。

新建一个helloworld.php文件,写入下列代码

echo "helloworld,";

echo "this is my first php script.";

echo phpinfo();

?>

配置好WebServer和PHP-FPM等php运行环境后,在浏览器中访问该文件就可以直接得到输出。

基于PHP的Web框架

PHP Web框架是

基于某模式将PHP开发常用功能封装实现使开发者快速开发的工具

它主要的任务包括:

代码重用:定义包、类、函数的放置和加载规则,建议直接整合Composer及其AutoLoad特性。

请求的分发管理:这个就是路由,Rest风的框架喜欢Rewrite,简单的一点的框架主要通过参数来定位模块和方法所在。

配置文件管理:加载和动态加载配置数据

错误和异常管理:异常捕捉、错误日志记录以及错误码规范。

Layout和模板引擎:如何规划页面布局、widget如何重用、ajax页面如何结合、过期- session如何重定向;数据和模板怎么渲染成HTML,是否压缩和设置过期头。

数据库:如何融入控制器;支持什么样的driver;考虑主从分离的扩展性;以及是否使用ORM

ThinkPHP3.2框架处理流程分析

TP的设计逻辑就是简单粗暴,面对问题解决问题,所以他的处理流程是基于面向过程的思想,而没有采用面向对象的依赖注入、控制反转等思路。他的自动加载、错误处理通过php原生函数的回调来实现。TP处理每次请求要经过四个步骤如下图所示:

0872b2a0f10dcb36307e5e23d11b040d.png

调用应用路口index.php

index.php是TP的入口文件,所有的请求都由该文件接管,它的工作也很简单主要是引入ThinkPHP入口文件

// 应用入口文件

// 检测PHP环境

if(version_compare(PHP_VERSION,'5.3.0','

PHP > 5.3.0 !');

// 开启调试模式 建议开发阶段开启 部署阶段注释或者设为false

define('APP_DEBUG',False);

// 定义应用目录

define('APP_PATH','./Application/');

// 引入ThinkPHP入口文件

require

'./ThinkPHP/ThinkPHP.php';

载入框架入口文件ThinkPHP.php

在ThinkPHP.php中主要记录初始运行时间和内存开销,然后完成系统常量判断及定义,最后载入框架引导类(ThinkThink)并执行Think::start方法进行应用初始化。

应用初始化ThinkThink: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方法启动应用

运行应用App::run()

此后TP进入请求处理管道,TP为管道中定义了14个事件,每个事件都可以绑定回调函数,请求到达管道后依次触发这些事件,事件触发后就会调用绑定到事件的回调函数,整个管道的生命周期由app_init开始,由app_end结束。具体实现上,TP将这些事件命名为标签(位),也可以称之为钩子,将回调函数命名为行为,当应用程序运行到标签的时候,就会被拦截下来,统一执行相关的行为。

Laravel框架处理流程分析

统一入口

Laravel框架使用了统一入口,入口文件:/public/index.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” 文件,就可以直接实例化这些第三方类库中的类了。

服务容器——Laravel真正的核心

服务容器,也叫IoC容器,其实包含了依赖注入(DI)和控制反转(IoC)两部分,是Laravel的真正核心。其他的各种功能模块比如 Route(路由)、Eloquent ORM(数据库 ORM 组件)、Request and Response(请求和响应)等等等等,实际上都是与核心无关的类模块提供的,这些类从注册到实例化,最终被使用,其实都是 Laravel 的服务容器负责的。

启动Kernel代码

Kernel实例调用handle方法,意味着Laravel的核心和公用代码已经准备完毕,此项目正式开始运行

代码清单/app/Http/Kernel.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);

});

}

终于到达控制器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值