yii请求生命周期是可观察干预的线性链条:从index.php创建application实例并调用run(),经初始化、onbeginrequest、handlerequest(含路由/控制器/动作)、onendrequest,最终response::send()发送响应;支持事件、行为扩展,无中间件概念。

Yii 的请求生命周期不是黑盒,而是可观察、可干预的线性链条——入口 index.php 触发,最终由 Response 发送 HTTP 响应,中间每个环节都支持事件监听或行为注入。
入口文件做了什么:从 index.php 到应用实例
所有请求都从 index.php 开始,它只干三件事:引入 autoload.php、创建 Application 实例、调用 run()。没有路由解析,没有中间件调度,这些全在 run() 内部发生。
-
index.php里不能直接写业务逻辑,否则会绕过 Yii 的整个生命周期(比如跳过beforeRequest事件) - 如果手动 new
Application后没调用run(),请求就卡住——不会自动执行控制器,也不会输出响应 - CLI 应用(如
yii命令)走的是yii\console\Application,生命周期不经过Request/Response的 Web 流程
Application::run() 内部发生了什么
run() 是真正的主干,它按固定顺序触发核心流程:初始化组件 → 触发 onBeginRequest → 处理请求 → 触发 onEndRequest → 返回响应内容。关键点在于“处理请求”这一步本身又嵌套了一层流程。
- Web 应用中,“处理请求”实际是调用
handleRequest(),它内部完成路由匹配(UrlManager)、控制器实例化、动作执行 - 若路由未匹配,
handleRequest()抛出NotFoundHttpException,此时仍会走到onEndRequest,但响应状态码已是 404 -
run()返回的是响应内容字符串(非Response对象),真正发送 HTTP 头和 body 是在Response::send()被隐式调用时发生的
哪些环节能插手:事件 vs 行为 vs 中间件
Yii 2 没有“中间件”概念,扩展点靠事件和行为。事件是广播式的(如 onBeforeRequest),行为是面向对象的增强(给 Application 或 Controller 动态加方法)。
- 全局拦截建议用
onBeforeRequest事件,比如检查维护模式;但别在里面exit,要用$event->isValid = false阻断流程 - 想改控制器行为(比如统一记录参数),优先用行为(
Behavior),而不是重写Controller::beforeAction()—— 后者对继承链敏感,且无法跨模块复用 -
Response::on beforeSend是最后修改响应体的机会,但此时 HTTP 头可能已发送(取决于 SAPI),所以不要在这里设Content-Type
常见生命周期陷阱:缓存、异常、CLI 和子请求
很多问题不是流程没走完,而是某些环节被跳过或提前终止。
- 启用
PageCache时,Controller::beforeAction()和视图渲染可能完全不执行——缓存命中后直接从存储读取并发送响应 - 未捕获的异常会触发
onError事件,但若在onBeginRequest里抛异常,onEndRequest仍会触发,只是响应内容为空 - 子请求(
Controller::renderPartial()或Yii::$app->runAction())不走完整生命周期,不触发onBeginRequest/onEndRequest,也不重新初始化组件 - 开发环境里关掉
debug模块,Debugger和Toolbar就不会注入,但生命周期本身不受影响
真正难调试的,往往不是流程断在哪,而是某个事件监听器悄悄修改了 Request 或 Response 对象的状态,而你查日志时只盯着控制器和视图。











