入门3请求处理
运行机制概述
每一次 Yii 应用开始处理 HTTP 请求时,它都会进行一个近似的流程。
- 用户提交指向 入口脚本
web/index.php
的请求。 - 入口脚本会加载 配置数组 并创建一个 应用 实例用于处理该请求。
- 应用会通过 request(请求) 应用组件 解析被请求的 路由。
- 应用创建一个 controller(控制器) 实例具体处理请求。
- 控制器会创建一个 action(动作) 实例并为该动作执行相关的 Filters(访问过滤器)。
- 如果任何一个过滤器验证失败,该动作会被取消。
- 如果全部的过滤器都通过,该动作就会被执行。
- 动作会加载一个数据模型,一般是从数据库中加载。
- 动作会渲染一个 View(视图),并为其提供所需的数据模型。
- 渲染得到的结果会返回给 response(响应) 应用组件。
- 响应组件会把渲染结果发回给用户的浏览器。
下面的示意图展示了应用是如何处理一个请求的。
启动引导(Bootstrapping)
启动引导是指:在应用开始解析并处理新接受请求之前,一个预先准备环境的过程。 启动引导会在两个地方具体进行:入口脚本(Entry Script) 和 应用主体(application)。
在入口脚本里,需注册各个类库的类文件自动加载器(Class Autoloader,简称自动加载器)。 这主要包括通过其 autoload.php
文件加载的Composer 自动加载器,以及通过 Yii
类加载的 Yii 自动加载器。之后, 入口脚本会加载应用的配置(configuration)并创建一个 应用主体 的实例。
在应用主体的构造函数中,会执行以下引导工作:
- 调用 preInit()(预初始化)方法,配置一些高优先级的应用属性, 比如 basePath 属性。
- 注册错误处理器(ErrorHandler)。
- 通过给定的应用配置初始化应用的各属性。
- 通过调用 init()(初始化)方法,它会顺次调用 bootstrap() 从而运行引导组件。
- 加载扩展清单文件(extension manifest file)
vendor/yiisoft/extensions.php
。 - 创建并运行各个扩展声明的 引导组件(bootstrap components)。
- 创建并运行各个 应用组件 以及在应用的 Bootstrap 属性中声明的各个 模块(modules)组件(如果有)。
因为引导工作必须在处理每一次请求之前都进行一遍,因此让该过程尽可能轻量化就异常重要, 请尽可能地优化这一步骤。
请尽量不要注册太多引导组件。只有他需要在 HTTP 请求处理的全部生命周期中都作用时才需要使用它。 举一个用到它的范例:一个模块需要注册额外的 URL 解析规则,就应该把它列在应用的 bootstrap 属性之中, 这样该 URL 解析规则才能在解析请求之前生效。(译注:换言之,为了性能需要,除了 URL 解析等少量操作之外,绝大多数组件都应该按需加载,而不是都放在引导过程中。)
在生产环境中,可以开启字节码缓存,比如 APC, 来进一步最小化加载和解析 PHP 文件所需的时间。
一些大型应用都包含有非常复杂的应用配置, 它们会被分割到许多更小的配置文件中。 此时,可以考虑将整个配置数组缓存起来, 并在入口脚本创建应用实例之前直接从缓存中加载。
路由
当入口脚本在调用 run() 方法时,它进行的第一个操作就是解析输入的请求,然后实例化对应的控制器动作处理这个请求。 该过程就被称为引导路由(routing)。 路由相反的操作会将给定的路由和参数生成一个可访问的URL地址, 这个操作叫做创建URL。 创建出来的URL被请求的时候,路由处理器可以解析成原始的路由信息和参数。
负责路由解析和创建URL的组件是 URL管理器, URL管理器在程序组件中被注册成 urlManager
。 URL管理器 提供方法 parseRequest() 来 解析请求的URL并返回路由信息和参数, 方法 createUrl() 用来根据提供的路由和参数创建一个可访问的URL。
在程序配置中配置 urlManager
组件,可以让你的应用不改变现有代码的情况下 识别任意的URL格式。
请求
一个应用的请求是用 yii\web\Request 对象来表示的,该对象提供了诸如 请求参数(译者注:通常是GET参数或者POST参数)、HTTP头、cookies等信息。 默认情况下,对于一个给定的请求,你可以通过 request
application component 应用组件(yii\web\Request 类的实例) 获得访问相应的请求对象。
响应
当一个应用在处理完一个请求后, 这个应用会生成一个 response 响应对象并把这个响应对象发送给终端用户 这个响应对象包含的信息有 HTTP 状态码,HTTP 头和主体内容等, 从本质上说,网页应用开发最终的目标就是根据不同的请求去构建这些响应对象。
在大多数实际应用情况下,你应该主要地去处理 response
这个 应用组件, 在默认情况下,它是一个继承自 yii\web\Response 的实例 然而,Yii 也允许你创建自己的响应对象并发送给终端用户
Cookie 验证
在上两节中,当通过 request
和 response
组件读取和发送 cookie 时, 你会喜欢扩展的 cookie 验证的保障安全功能,它能 使 cookie 不被客户端修改。该功能通过给每个 cookie 签发一个哈希字符串来告知服务端 cookie 是否在客户端被修改, 如果被修改,通过 request
组件的 cookie collection cookie 集合访问不到该 cookie。
注意: Cookie 验证只保护 cookie 值被修改,如果一个 cookie 验证失败, 仍然可以通过 $_COOKIE
来访问该 cookie, 因为这是第三方库对未通过 cookie 验证自定义的操作方式。
Cookie 验证默认启用,可以设置 yii\web\Request::$enableCookieValidation 属性为 false 来禁用它, 尽管如此,我们强烈建议启用它。
注意: 直接通过 $_COOKIE
和 setcookie()
读取和发送的 Cookie 不会被验证。
当使用 cookie 验证时,必须指定 yii\web\Request::$cookieValidationKey,它是用来生成上述的哈希值, 可通过在应用配置中配置 request
组件。
错误处理
Yii 内置了一个error handler错误处理器,它使错误处理更方便, Yii错误处理器做以下工作来提升错误处理效果:
- 所有非致命PHP错误(如,警告,提示)会转换成可获取异常;
- 异常和致命的PHP错误会被显示, 在调试模式会显示详细的函数调用栈和源代码行数。
- 支持使用专用的 控制器操作 来显示错误;
- 支持不同的错误响应格式;
error handler 错误处理器默认启用, 可通过在应用的入口脚本中定义常量YII_ENABLE_ERROR_HANDLER
来禁用。
使用错误处理器
error handler 注册成一个名称为errorHandler
应用组件, 可以在应用配置中配置它类似如下:
自定义错误显示
error handler错误处理器根据常量YII_DEBUG
的值来调整错误显示, 当YII_DEBUG
为 true (表示在调试模式), 错误处理器会显示异常以及详细的函数调用栈和源代码行数来帮助调试, 当YII_DEBUG
为 false,只有错误信息会被显示以防止应用的敏感信息泄漏。
使用错误动作
使用指定的错误操作 来自定义错误显示更方便, 为此,首先配置errorHandler
组件的 errorAction 属性, 类似如下:
日志
Yii提供了一个强大的日志框架,这个框架具有高度的可定制性和可扩展性。使用这个框架, 你可以轻松地记录各种类型的消息,过滤它们, 并且将它们收集到不同的目标,诸如文件,数据库,邮件。
使用Yii日志框架涉及下面的几个步骤:
- 在你代码里的各个地方记录 log messages;
- 在应用配置里通过配置 log targets 来过滤和导出日志消息;
- 检查由不同的目标导出的已过滤的日志消息(例如:Yii debugger)。
日志消息
记录日志消息就跟调用下面的日志方法一样简单:
- Yii::trace():记录一条消息去跟踪一段代码是怎样运行的。这主要在开发的时候使用。
- Yii::info():记录一条消息来传达一些有用的信息。
- Yii::warning():记录一个警告消息用来指示一些已经发生的意外。
- Yii::error():记录一个致命的错误,这个错误应该尽快被检查。
日志目标
一个日志目标是一个 yii\log\Target 类或者它的子类的实例。 它将通过他们的严重层级和类别来过滤日志消息,然后将它们导出到一些媒介中。
Yii配备了以下的内建日志目标。请参考关于这些类的API文档, 并且学习怎样配置和使用他们。
- yii\log\DbTarget:在数据库表里存储日志消息。
- yii\log\EmailTarget:发送日志消息到预先指定的邮箱地址。
- yii\log\FileTarget:保存日志消息到文件中.
- yii\log\SyslogTarget:通过调用PHP函数
syslog()
将日志消息保存到系统日志里。
消息过滤
对于每一个日志目标,你可以配置它的 levels 和 categories 属性来指定哪个消息的严重程度和分类目标应该处理。
如果你没有指定 levels 的属性, 那就意味着目标将处理 任何 严重程度的消息。
categories 属性是一个包含消息分类名称或者模式的数组。 一个目标将只处理那些在这个数组中能够找到对应的分类或者其中一个相匹配的模式的消息。 一个分类模式是一个以星号 *
结尾的分类名前缀。假如一个分类名与分类模式具有相同的前缀, 那么该分类名将和分类模式相匹配。例如, yii\db\Command::execute
和 yii\db\Command::query
都是作为分类名称运用在 yii\db\Command 类来记录日志消息的。 它们都是匹配模式 yii\db\*
。
假如你没有指定 categories 属性, 这意味着目标将会处理 任何 分类的消息。
消息格式化
日志目标以某种格式导出过滤过的日志消息。例如, 假如你安装一个 yii\log\FileTarget 类的日志目标, 你应该能找出一个日志消息类似下面的 runtime/log/app.log
文件:
2014-10-04 18:10:15 [::1][][-][trace][yii\base\Module::getModule] Loading module: debug
日志目标也可以追加一些上下文信息到每组日志消息中。 默认情况下,这些全局的PHP变量的值被包含在:$_GET
,$_POST
,$_FILES
,$_COOKIE
,$_SESSION
和 $_SERVER
中。 你可以通过配置 yii\log\Target::$logVars 属性适应这个行为, 这个属性是你想要通过日志目标包含的全局变量名称。 举个例子,下面的日志目标配置指明了只有 $_SERVER
变量的值将被追加到日志消息中。
消息跟踪级别
在开发的时候,通常希望看到每个日志消息来自哪里。这个是能够被实现的,通过配置 log
组件的 traceLevel 属性
消息刷新和导出
如上所述,通过 logger object 对象,日志消息被保存在一个数组里。 为了这个数组的内存消耗,当数组积累了一定数量的日志消息, 日志对象每次都将刷新被记录的消息到 log targets 中。 你可以通过配置 log
组件的 flushInterval 属性来自定义数量:
切换日志目标
你可以通过配置 enabled 属性来开启或者禁用日志目标。 你可以通过日志目标配置去做,或者是在你的代码中放入下面的PHP申明:
性能分析
性能分析是一个特殊的消息记录类型,它通常用在测量某段代码块的时间, 并且找出性能瓶颈是什么。举个例子,yii\db\Command 类 使用性能分析找出每个数据库查询的时间。
为了使用性能分析,首先确定需要进行分析的代码块。 然后像下面这样围住每个代码块:
这里的 myBenchmark 代表一个唯一标记来标识一个代码块。之后当你检查分析结果的时候, 你将使用这个标记来定位对应的代码块所花费的时间。
对于确保 beginProfile 和 endProfile 对能够正确地嵌套,这是很重要的。 例如,