文章地址:http://www.hcoding.com/?p=46

  上一篇分析Symfony2框架源码,探究Symfony2如何完成一个请求的前半部分,前半部分可以理解为Symfony2框架为处理请求做准备工作,包括container生成、缓存、bundls初始化等一些列准备工作(Symfony2源码分析——启动过程1)。而这一篇讲的是Symfony2如何根据请求的数据生成Response对象,向客户端返回响应数据。

  在分析前需要了解Symfony2的事件驱动机制:Symfony2事件驱动

  言归正传,Symfony2请求的工作流程其实是Symfony2内核的事件驱动完成的,下面是Symfony2框架定义好的内核事件:

final class KernelEvents
{
/**
* The REQUEST event occurs at the very beginning of request
* dispatching
*
* This event allows you to create a response for a request before any
* other code in the framework is executed. The event listener method
* receives a Symfony\Component\HttpKernel\Event\GetResponseEvent
* instance.
*
* @var string
*
* @api
*/
const REQUEST = 'kernel.request'; /**
* The EXCEPTION event occurs when an uncaught exception appears
*
* This event allows you to create a response for a thrown exception or
* to modify the thrown exception. The event listener method receives
* a Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent
* instance.
*
* @var string
*
* @api
*/
const EXCEPTION = 'kernel.exception'; /**
* The VIEW event occurs when the return value of a controller
* is not a Response instance
*
* This event allows you to create a response for the return value of the
* controller. The event listener method receives a
* Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent
* instance.
*
* @var string
*
* @api
*/
const VIEW = 'kernel.view'; /**
* The CONTROLLER event occurs once a controller was found for
* handling a request
*
* This event allows you to change the controller that will handle the
* request. The event listener method receives a
* Symfony\Component\HttpKernel\Event\FilterControllerEvent instance.
*
* @var string
*
* @api
*/
const CONTROLLER = 'kernel.controller'; /**
* The RESPONSE event occurs once a response was created for
* replying to a request
*
* This event allows you to modify or replace the response that will be
* replied. The event listener method receives a
* Symfony\Component\HttpKernel\Event\FilterResponseEvent instance.
*
* @var string
*
* @api
*/
const RESPONSE = 'kernel.response'; /**
* The TERMINATE event occurs once a response was sent
*
* This event allows you to run expensive post-response jobs.
* The event listener method receives a
* Symfony\Component\HttpKernel\Event\PostResponseEvent instance.
*
* @var string
*/
const TERMINATE = 'kernel.terminate'; /**
* The FINISH_REQUEST event occurs when a response was generated for a request.
*
* This event allows you to reset the global and environmental state of
* the application, when it was changed during the request.
*
* @var string
*/
const FINISH_REQUEST = 'kernel.finish_request';
}

  我们可以编写事件监听器,监听相应的内核事件,在Symfony2触发该事件的时候,相应的事件监听器就会执行。监听和唤醒形象的描述,就像,你(事件监听器)参加校运会,去大会(Symfony2)登记(监听)参加50米短跑(事件),当50米短跑比赛开始了(事件被触发),那你就奔跑吧(监听器执行,其实就是一个执行函数,函数完成什么工作就取决于你的需求了),少年。

  Symfony2的内核事件处理流程大部分工作都在HttpKernel::handleRaw方法中:

     private function handleRaw(Request $request, $type = self::MASTER_REQUEST)
{
$this->requestStack->push($request); // request
// 初始化事件,事件对象会被传递给监听器,所以事件可以说是一个信息的载体,事件内存放着监听器感兴趣的数据。
$event = new GetResponseEvent($this, $request, $type);
// 触发kernel.request事件,后续详细讲解EventDispatcher::dispatch方法的实现,
// 这里我们需要知道的是,dispatcher把$event传递给所有监听了kernel.request事件的监听器,监听器将会执行。
// kernel.request事件发生在controller执行之前,我们可以在这一步奏完成路由解析等为controller执行提供准备数据,
// 在这个过程允许我们直接生成Response对象,向客户端输出数据,那么controller就不会被执行了。
$this->dispatcher->dispatch(KernelEvents::REQUEST, $event); // 如果我们在kernel.request事件生成了Response对象(响应数据),那么就跳过kernel.controller、kernel.view事件、
// controller也会被跳过,直接执行kernel.response事件。
if ($event->hasResponse()) {
return $this->filterResponse($event->getResponse(), $request, $type);
} // load controller
// 根据路由规则返回 一个对象或者数组或者字符串 ,如果$controller是一个数组,$controller[0]是存放的是要执行的controller对象,
// $controller[0]存放的是controller对象执行的方法,即action,方法的参数没有保存在$controller数组中;
// 如果$controller是对象,那么该对象就实现了__invoke 方法;
// 如果$controller是字符串,那么$controller就是要运行的函数的函数名。
// 图2是$controller的一个var_dump例子
if (false === $controller = $this->resolver->getController($request)) {
throw new NotFoundHttpException(sprintf('Unable to find the controller for path "%s". Maybe you forgot to add the matching route in your routing configuration?', $request->getPathInfo()));
} $event = new FilterControllerEvent($this, $controller, $request, $type);
// 触发kernel.controller事件,这个事件发生在controller执行前。我们可以通过监听这个事件在controller执行前修改controller,
// 或者完成一些动作。
$this->dispatcher->dispatch(KernelEvents::CONTROLLER, $event);
$controller = $event->getController(); // controller arguments
// 从request对象中获取controller方法的参数
$arguments = $this->resolver->getArguments($request, $controller); // call controller
// 执行controller
$response = call_user_func_array($controller, $arguments); // view
// 如果$response不是Response对象,那么kernel.view事件就会触发,监听kernel.view事件的监听器通过$response值生成Response对象。
if (!$response instanceof Response) {
$event = new GetResponseForControllerResultEvent($this, $request, $type, $response);
$this->dispatcher->dispatch(KernelEvents::VIEW, $event); if ($event->hasResponse()) {
$response = $event->getResponse();
} if (!$response instanceof Response) {
$msg = sprintf('The controller must return a response (%s given).', $this->varToString($response)); // the user may have forgotten to return something
if (null === $response) {
$msg .= ' Did you forget to add a return statement somewhere in your controller?';
}
throw new \LogicException($msg);
}
} // 触发kernel.response事件,在向客户端输出Response对象前,我们可以对Response对象进行修改,
// 例如修改response头部,设置缓存、压缩输出数据等。 // 接着触发kernel.finish_request事件,把当前请求从请求栈中弹出,当前请求就完成。
return $this->filterResponse($response, $request, $type); // 千万别忘记了,filterResponse执行完后,Symfony2内核事件处理流程还有最后一步,位于app_dev.php[app.php]最后一行,
// $kernel->terminate($request, $response);这个方法触发kernel.terminate事件,此时,Symfony2已经响应了客户端的请求,
// 向客户端输出了Response对象。监听kernel.terminate事件的监听器,主要是为了完成一些耗时的操作,操作的结果不需要返回给
// 客户端的,例如邮件发送、图片压缩等等。
// 到这里,Symfony2的整个流程就走完了。
}
HttpKernel::filterResponse方法和HttpKernel::finishRequest方法:
     private function filterResponse(Response $response, Request $request, $type)
{
$event = new FilterResponseEvent($this, $request, $type, $response); $this->dispatcher->dispatch(KernelEvents::RESPONSE, $event); $this->finishRequest($request, $type); return $event->getResponse();
} /**
* Publishes the finish request event, then pop the request from the stack.
*
* Note that the order of the operations is important here, otherwise
* operations such as {@link RequestStack::getParentRequest()} can lead to
* weird results.
*
* @param Request $request
* @param int $type
*/
private function finishRequest(Request $request, $type)
{
$this->dispatcher->dispatch(KernelEvents::FINISH_REQUEST, new FinishRequestEvent($this, $request, $type));
$this->requestStack->pop();
}

图2

Symfony2框架的事件分发机制的核心代码:

     public function dispatch($eventName, Event $event = null)
{
if (null === $event) {
$event = new Event();
} $event->setDispatcher($this);
$event->setName($eventName); if (!isset($this->listeners[$eventName])) {
return $event;
} // $eventName即:KernelEvents::REQUEST、KernelEvents::CONTROLLER、KernelEvents::VIEW、KernelEvents::RESPONSE、KernelEvents::TERMINATE等
// getListeners返回所有监听$eventName事件的监听器
$this->doDispatch($this->getListeners($eventName), $eventName, $event); return $event;
} protected function doDispatch($listeners, $eventName, Event $event)
{
// 监听器执行
foreach ($listeners as $listener) {
call_user_func($listener, $event, $eventName, $this);
// 如果其中一个监听器把$event的propagationStopped属性设置为true,那么表示$eventName这一事件终止执行,
// 事件不会往$listeners里尚未执行的监听器传递该事件。
if ($event->isPropagationStopped()) {
break;
}
}
}

Symfony2源码分析——启动过程2的更多相关文章

  1. Symfony2源码分析——启动过程1

    本文通过阅读分析Symfony2的源码,了解Symfony2启动过程中完成哪些工作,从阅读源码了解Symfony2框架. Symfony2的核心本质是把Request转换成Response的一个过程. ...

  2. quartz2.x源码分析——启动过程

    title: quartz2.x源码分析--启动过程 date: 2017-04-13 14:59:01 categories: quartz tags: [quartz, 源码分析] --- 先简单 ...

  3. mysql源码分析-启动过程

    mysql源码分析-启动过程 概要 # sql/mysqld.cc, 不包含psi的初始化过程 mysqld_main: // 加载my.cnf和my.cnf.d,还有命令行参数 if (load_d ...

  4. Spring Boot源码分析-启动过程

    Spring Boot作为目前最流行的Java开发框架,秉承"约定优于配置"原则,大大简化了Spring MVC繁琐的XML文件配置,基本实现零配置启动项目. 本文基于Spring ...

  5. Nginx学习笔记(六) 源码分析&启动过程

    Nginx的启动过程 主要介绍Nginx的启动过程,可以在/core/nginx.c中找到Nginx的主函数main(),那么就从这里开始分析Nginx的启动过程. 涉及到的基本函数 源码: /* * ...

  6. Zookeeper 源码分析-启动

    Zookeeper 源码分析-启动 博客分类: Zookeeper   本文主要介绍了zookeeper启动的过程 运行zkServer.sh start命令可以启动zookeeper.入口的main ...

  7. apiserver源码分析——启动流程

    前言 apiserver是k8s控制面的一个组件,在众多组件中唯一一个对接etcd,对外暴露http服务的形式为k8s中各种资源提供增删改查等服务.它是RESTful风格,每个资源的URI都会形如 / ...

  8. 鸿蒙内核源码分析(编译过程篇) | 简单案例窥视GCC编译全过程 | 百篇博客分析OpenHarmony源码| v57.01

    百篇博客系列篇.本篇为: v57.xx 鸿蒙内核源码分析(编译过程篇) | 简单案例窥视编译全过程 | 51.c.h.o 编译构建相关篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙 ...

  9. Tomcat源码分析——启动与停止服务

    前言 熟悉Tomcat的工程师们,肯定都知道Tomcat是如何启动与停止的.对于startup.sh.startup.bat.shutdown.sh.shutdown.bat等脚本或者批处理命令,大家 ...

随机推荐

  1. oldboy第十三天学习

    1.现在给我的感觉是,python终于入门了开始越学越简单了.变得更好理解了. 一.memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它 ...

  2. codevs 3094 寻找sb4

    3094 寻找sb4  时间限制: 1 s  空间限制: 32000 KB  题目等级 : 黄金 Gold 题解  查看运行结果     题目描述 Description sb有一天和sml吵架了,她 ...

  3. 如何统一修改 Altium Designer 中的字符大小

    如下图 1 所示: Q1. Q2. C1. C2. R1 等等的字符你想统一修改他们的大小.原来是 Text Height( 100mil), Text Width( 12mil),想改成 Text ...

  4. 【转】adobe acrobat pro修改pdf文字

    原文网址:http://zhidao.baidu.com/link?url=7MTeEu5IM49lNIISNQMcZLyLAwMPsRQWF5WAwQPfvkPsbbZLHSQE43MWaIxxVm ...

  5. 【转】文件同步软件FreeFileSync

    原文网址:http://imcn.me/html/y2012/9855.html FreeFileSync 是一款开源的文件夹比较和同步工具,可用于 Win 和 Lin 平台,最近发布了 5.0 版本 ...

  6. SPOJ3267--D-query (树状数组离线操作)

    题意查询区间 [l,r]内有多少个不同的数字 先把所有询问按 右端点进行排序,然后离线操作.如果该位置的数字 已经出现过那么把前一个位置-1,当前位置+1.扫一遍输出. #include <cs ...

  7. Java设计模式之简单工厂、工厂方法和抽象工厂

    在前面的学习中(参见前面的博客),我们学到了很多OO原则: 封装变化 多用组合,少用继承 针对接口/超类编程,不针对实现编程 松耦合 开闭原则 让我们从一个简单的类开始,看看如何将之改造成符合OO原则 ...

  8. ReactJS 的背景及原理

    原文链接:http://www.infoq.com/cn/articles/subversion-front-end-ui-development-framework-react 在Web开发中,我们 ...

  9. DedeCMS源码安装

    一.源码下载地址 可以从以下网站下载DedeCMS源码进行安装,这里我下载了AB模板网的一个服装网站源码来演示DedeCMS源码的安装 http://www.adminbuy.cn/dedecms/2 ...

  10. office2007序列号/密钥

    绝对可以用的许可证:V9MTG-3GX8P-D3Y4R-68BQ8-4Q8VD