Yii2 应用结构全景解析:从 MVC 骨架到入口脚本、应用对象与模块化协作
【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址: https://gitcode.com/gh_mirrors/yi/yii2
Yii 2 应用遵循经典的模型-视图-控制器(MVC)设计模式组织业务代码,同时在其之上定义了入口脚本、应用对象、应用组件、模块、过滤器与部件(Widget)等六大结构实体,共同支撑一次完整请求的处理周期。本篇指南以官方法文版《Vue d'ensemble》(docs/guide-fr/structure-overview.md)为骨架,结合本仓库框架源码(framework/目录)与英文版结构指南(docs/guide/),系统讲解 Yii 应用的静态结构与运行生命周期,帮助你掌握如何把控制器、模型、视图、模块、过滤器组织成一套可维护、可扩展的应用系统。
MVC:Yii 应用的业务骨架
Yii 应用按照 模型-视图-控制器(MVC) 架构模式组织:
- 模型(Models):代表数据、业务逻辑与校验规则,对应 docs/guide/structure-models.md;
- 视图(Views):是模型的输出呈现形式,对应 docs/guide/structure-views.md;
- 控制器(Controllers):接收用户输入并将其转换为对模型和视图的命令,对应 docs/guide/structure-controllers.md。
在典型的 Basic 项目模板中,models/、views/、controllers/三个子目录直接对应这三类角色。控制器通过action方法暴露路由入口(例如SiteController::actionIndex()),读取模型数据后渲染视图并返回响应;模型负责数据校验、数据库访问与业务规则;视图只做展示逻辑。三者各司其职,是理解后面所有结构实体的前提。
六大结构实体:一次请求周期的完整参与者
除 MVC 之外,Yii 应用还定义了以下六类实体,它们共同构成上图所示的静态结构:
| 实体 | 职责 | 参考文档 |
|---|---|---|
| 入口脚本(Entry Scripts) | 用户直接访问的 PHP 脚本,负责启动一次请求处理周期 | docs/guide-fr/structure-entry-scripts.md |
| 应用对象(Applications) | 全局可访问的对象,管理应用组件并协调它们完成请求 | docs/guide-fr/structure-applications.md |
| 应用组件(Application Components) | 注册到应用上的对象,为完成请求提供各类服务 | docs/guide-fr/structure-application-components.md |
| 模块(Modules) | 自包含的软件包,内部拥有完整 MVC,可被应用组织为多个模块 | docs/guide-fr/structure-modules.md |
| 过滤器(Filters) | 在控制器实际处理每个请求之前和之后被调用的代码 | docs/guide-fr/structure-filters.md |
| 部件(Widgets) | 可嵌入视图的对象,可包含控制器逻辑并在不同视图中复用 | docs/guide-fr/structure-widgets.md |
下面逐一深入剖析每一类实体。
入口脚本:请求处理周期的起点
入口脚本是整个启动流程的第一步,一个应用(无论是 Web 应用还是控制台应用)拥有且仅有一个入口脚本。Web 入口脚本必须存放在 Web 可访问目录下(通常命名为index.php);控制台入口脚本通常存放在应用根目录下并命名为yii,可通过./yii <route> [arguments] [options]执行。
入口脚本主要完成六件事(见 docs/guide/structure-entry-scripts.md):
- 定义全局常量;
- 注册 Composer 自动加载器;
- 引入 Yii 类文件(本仓库即 framework/Yii.php);
- 加载应用配置;
- 创建并配置应用实例;
- 调用
\yii\base\Application::run()处理请求。
Basic Web 项目模板的入口脚本完整代码如下:
<?php defined('YII_DEBUG') or define('YII_DEBUG', true); defined('YII_ENV') or define('YII_ENV', 'dev'); // 注册 Composer 自动加载器 require __DIR__ . '/../vendor/autoload.php'; // 引入 Yii 类文件 require __DIR__ . '/../vendor/yiisoft/yii2/Yii.php'; // 加载应用配置 $config = require __DIR__ . '/../config/web.php'; // 创建、配置并运行应用 (new yii\web\Application($config))->run();入口脚本还负责定义三个全局常量,其默认行为已固化在框架约定中:
YII_DEBUG:是否开启调试模式。开启后保留更多日志、抛出异常时显示详细调用栈,默认false;YII_ENV:应用运行环境,默认'prod'(生产环境),可设为'dev'、'test'等,详见 docs/guide/concept-configurations.md;YII_ENABLE_ERROR_HANDLER:是否启用 Yii 提供的错误处理器,默认true。
常量定义必须放在入口脚本最开头,以保证在引入其他 PHP 文件时即刻生效。
应用对象:全局协调中枢
应用对象掌控 Yii 应用系统的整体结构与生命周期。每个 Yii 应用系统只包含一个应用对象,它在入口脚本中被创建,并通过表达式\Yii::$app全局访问。在源码中,yii\base\Application是所有应用类的抽象基类,直接继承自模块基类yii\base\Module(framework/base/Application.php),并提供两种具体形态:
- Web 应用
yii\web\Application:主要处理 Web 请求(framework/web/Application.php); - 控制台应用
yii\console\Application:处理控制台命令请求。
从源码可以看出,应用对象构造时(framework/base/Application.php)会依次完成:把自身注册为Yii::$app与模块实例、初始化state = STATE_BEGIN、调用preInit()预处理、注册错误处理器。而preInit()(framework/base/Application.php)强制要求配置中必须包含id与basePath,否则抛出InvalidConfigException,这印证了文档中"任何应用至少配置id与basePath两个属性"的说法。
必须配置的属性
id:唯一标识应用,主要用于程序化区分,官方建议仅使用字母数字字符以保证互操作性;basePath:应用根目录,即包含models、views、controllers等受保护源码的目录。可配置为目录路径或路径别名,目录必须真实存在,最终会经realpath()规范化。设置basePath的同时会预定义别名@app(framework/base/Application.php),后续许多派生路径(如@app/runtime)都基于它构建。
常用重要属性
aliases:以数组形式定义别名(键为别名名,值为路径),等价于调用Yii::setAlias();bootstrap:指定在应用启动阶段(bootstrapping)必须运行的组件列表。支持五种指定方式:应用组件 ID、模块 ID、类名、配置数组、匿名函数。若组件类实现了yii\base\BootstrapInterface,其bootstrap()方法也会被调用。Basic 模板在开发环境中的典型用法如下:
if (YII_ENV_DEV) { // 针对 'dev' 环境的配置调整 $config['bootstrap'][] = 'debug'; $config['modules']['debug'] = 'yii\debug\Module'; $config['bootstrap'][] = 'gii'; $config['modules']['gii'] = 'yii\gii\Module'; }注意:
bootstrap中放入过多组件会拖慢性能,因为每次请求都会运行同一批组件,务必克制使用。
catchAll(仅 Web 应用):把全部用户请求交给指定控制器动作处理,常用于维护模式。配置数组首元素为动作路由,其余元素为绑定参数:
[ 'catchAll' => [ 'offline/notice', 'param1' => 'value1', 'param2' => 'value2', ], ]在源码 framework/web/Application.php 中可以看到,handleRequest()首先检查catchAll:为空则调用$request->resolve()解析路由,否则直接使用catchAll[0]作为路由;解析失败时抛出NotFoundHttpException(即"Page not found")。
components:最重要的属性,用于注册命名组件(应用组件),例如:
[ 'components' => [ 'cache' => [ 'class' => 'yii\caching\FileCache', ], 'user' => [ 'identityClass' => 'app\models\User', 'enableAutoLogin' => true, ], ], ]controllerMap:把控制器 ID 映射到任意控制器类,打破默认命名约定。键为控制器 ID,值为类名或配置数组:
[ 'controllerMap' => [ 'account' => 'app\controllers\UserController', 'article' => [ 'class' => 'app\controllers\PostController', 'enableCsrfValidation' => false, ], ], ]controllerNamespace:控制器类默认命名空间,默认app\controllers。ID 为post时按约定对应app\controllers\PostController,admin/post对应app\controllers\admin\PostController。命名空间与类必须可自动加载,否则访问时会出现"Page Not Found";language:面向终端用户的内容语言,默认en;推荐使用 IETF 语言标签(如en-US),影响消息翻译、日期数字格式化等国际化行为;sourceLanguage:应用代码书写语言,默认en-US;modules:应用包含的模块,键为模块 ID,值为模块类名或配置数组;name:展示给用户的名称,无需唯一;version:应用版本号,默认'1.0';params:全局可访问的参数数组,用于集中管理常量配置,例如'thumbnail.size' => [128, 128],代码中通过\Yii::$app->params['thumbnail.size']读取;timeZone:设置 PHP 运行时默认时区,本质是对date_default_timezone_set()的封装(framework/base/Application.php)。若 php.ini 与应用配置均未设置,框架默认回退到UTC(见 preInit)。
按约定取值的属性
以下属性默认值来自通用约定,仅在需要打破约定时才配置:
charset:默认UTF-8;defaultRoute:未指定路由时使用的路由。Web 应用默认'site'(即SiteController及其默认动作),控制台应用默认'help'(直接运行yii显示帮助信息);layout/layoutPath:默认布局'main',布局目录默认@app/views/layouts;runtimePath:临时文件(日志、缓存)目录,默认@app/runtime,必须可写且应对终端用户隐藏(可能含敏感信息),同时预定义别名@runtime;viewPath:视图根目录,默认@app/views;vendorPath:Composer 管理的第三方库目录,默认@app/vendor,同时预定义别名@vendor、@bower、@npm(framework/base/Application.php);extensions:已安装扩展列表,默认读取@vendor/yiisoft/extensions.php(由 Composer 自动生成维护),一般无需手动配置;enableCoreCommands(仅控制台应用):是否启用 Yii 内置核心命令,默认true。
核心应用组件
yii\base\Application::coreComponents()(framework/base/Application.php)预置了一批带固定 ID 与默认配置的核心组件,Web 应用在此基础上追加request、response、session、user、errorHandler(framework/web/Application.php)。常用的包括:
| 组件 ID | 类 | 职责 |
|---|---|---|
assetManager | yii\web\AssetManager | 资源包管理与发布 |
db | yii\db\Connection | 数据库连接与查询 |
errorHandler | yii\web\ErrorHandler | PHP 错误与异常处理 |
formatter | yii\i18n\Formatter | 面向用户的格式化 |
i18n | yii\i18n\I18N | 消息翻译与格式化 |
log | yii\log\Dispatcher | 日志目标管理 |
request/response | yii\web\Request/yii\web\Response | 请求采集与响应输出 |
session/user | yii\web\Session/yii\web\User | 会话与用户认证(仅 Web) |
urlManager | yii\web\UrlManager | URL 解析与创建 |
view | yii\web\View | 视图渲染 |
完整说明见 docs/guide/structure-application-components.md。
应用组件:按需实例化的服务提供者
应用本质上是服务定位器,承载一组提供各种服务的应用组件。每个组件有唯一 ID,通过\Yii::$app->componentID访问,例如\Yii::$app->db获取数据库连接、\Yii::$app->cache获取主缓存。组件在首次被访问时才实例化,之后复用同一实例——这意味着一大优势:请求期间未被访问的组件不会被创建,从而节省资源。
注册方式支持类名、配置数组与匿名函数三种:
[ 'components' => [ 'cache' => 'yii\caching\ApcCache', // 类名 'db' => [ // 配置数组 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host=localhost;dbname=demo', 'username' => 'root', 'password' => '', ], 'search' => function () { // 匿名函数 return new app\components\SolrService; }, ], ]提示:应用组件如同全局变量,注册过多会降低代码的可测试性与可维护性,应谨慎使用。若想让某个组件在每次请求时都被实例化,把它加入
bootstrap列表即可(docs/guide-fr/structure-application-components.md)。
模块:自包含的迷你应用
模块是由模型、视图、控制器及其支撑组件构成的自包含软件单元,常被视为"迷你应用"。与应用的差异在于:模块不能独立部署,必须驻留在应用内部。一个典型模块的目录结构如下:
forum/ Module.php 模块类文件 controllers/ 控制器类文件 DefaultController.php 默认控制器类文件 models/ 模型类文件 views/ 控制器视图与布局文件 layouts/ 布局视图文件 default/ DefaultController 的视图文件 index.php index 视图文件模块类需继承yii\base\Module并放在模块根目录下(docs/guide/structure-modules.md)。在应用配置中通过modules属性挂载:
[ 'modules' => [ 'forum' => [ 'class' => 'app\modules\forum\Module', // ... 模块的其他配置 ... ], ], ]模块内控制器的路由必须以模块 ID 开头,例如forum/post/index表示forum模块中post控制器的index动作;只写模块 ID(如forum)时由模块的defaultRoute(默认default)决定使用哪个控制器。模块支持无限层级嵌套,子模块必须声明在父模块的modules属性中。若某些模块需要每次请求都运行(如debug模块),将其 ID 加入应用的bootstrap属性即可。
一个值得注意的实践:模块的 URL 规则应在UrlManager::parseRequest()触发之前(即引导阶段)注册,因为模块在路由解析后才被初始化,放在模块init()中无效;可用yii\web\GroupUrlRule包装模块规则。自 2.0.13 起模块支持服务定位器树遍历,模块内优先使用$module->get('db')而非Yii::$app->get('db'),便于模块级组件定制。
过滤器:动作执行前后的拦截代码
过滤器是在控制器动作之前和/或之后运行的代码对象。例如访问控制过滤器在动作前检查用户权限,内容压缩过滤器在动作后压缩响应内容。过滤器本质上是行为的一种特例,在控制器中重写behaviors()方法声明:
public function behaviors() { return [ [ 'class' => 'yii\filters\HttpCache', 'only' => ['index', 'view'], 'lastModified' => function ($action, $params) { $q = new \yii\db\Query(); return $q->from('user')->max('updated_at'); }, ], ]; }默认过滤器作用于控制器全部动作,可用only/except限定作用范围(在模块或应用级声明过滤器时需使用路由而非动作 ID)。多个过滤器按"应用 → 模块 → 控制器"的顺序执行前置过滤,动作执行后按相反顺序执行后置过滤;任一层前置过滤返回false会中断后续过滤与动作执行(docs/guide/structure-filters.md)。
Yii 在yii\filters命名空间下(本仓库 framework/filters/)提供了常用过滤器:AccessControl(基于规则表的访问控制)、yii\filters\auth下的认证方法过滤器(HTTP Basic、OAuth2 等)、ContentNegotiator(响应格式与语言协商)、HttpCache(客户端 HTTP 缓存)、PageCache(整页服务端缓存)、RateLimiter(漏桶算法限流)、VerbFilter(HTTP 方法校验)与Cors(跨域资源共享)。ContentNegotiator既可作过滤器,也可作为引导组件在应用生命周期早期确定响应格式与语言。
部件(Widgets):可复用的视图组件
部件是可嵌入视图的对象,可包含控制器逻辑并在不同视图间复用(docs/guide-fr/structure-widgets.md)。本仓库内置部件位于 framework/widgets/,例如ActiveForm(表单构建)、Breadcrumbs(面包屑导航)、LinkPager(分页)、Menu(菜单)、DetailView/ListView(数据展示)等。部件通常通过<?= \yii\widgets\ActiveForm::begin() ?>这类形式在视图中调用,其渲染逻辑封装在类内部,从而避免在多个视图间复制粘贴相同的展示代码。
应用生命周期与事件:请求如何在 Yii 中流转
从源码 framework/base/Application.php 的run()方法可以看到一次请求处理的完整流程:
- 入口脚本把应用配置加载为数组;
- 创建应用实例:
preInit()配置高优先级属性(如basePath)→ 注册错误处理器 → 配置应用属性 →init()内部调用bootstrap()运行引导组件; - 调用
run():- 触发
EVENT_BEFORE_REQUEST; - 处理请求:把请求解析为路由与参数,按路由创建模块、控制器与动作对象并执行动作;
- 触发
EVENT_AFTER_REQUEST; - 发送响应给终端用户;
- 触发
- 入口脚本接收退出状态码并结束处理。
框架为这一生命周期定义了六个状态常量(STATE_BEGIN到STATE_END,见 framework/base/Application.php),run()依次推进状态机并在关键节点触发事件。开发者可在应用配置中用on eventName语法挂接事件处理器,也可以在应用实例创建后通过\Yii::$app->on(...)挂接:
\Yii::$app->on(\yii\base\Application::EVENT_BEFORE_REQUEST, function ($event) { // ... });四个核心事件(常量定义见 framework/base/Application.php):
EVENT_BEFORE_REQUEST(beforeRequest):应用处理请求之前触发,此时应用已配置并初始化完毕,适合动态调整language等属性以拦截请求处理;EVENT_AFTER_REQUEST(afterRequest):应用完成请求处理但尚未发送响应时触发,可做请求后处理或定制响应;EVENT_BEFORE_ACTION(beforeAction):每个控制器动作运行之前触发,事件参数为yii\base\ActionEvent,将isValid置为false可中止动作执行。触发顺序为应用 → 模块 → 控制器,任何一层中止后后续事件不再触发;EVENT_AFTER_ACTION(afterAction):每个动作运行之后触发,可通过$event->result读取或修改动作结果,触发顺序与beforeAction相反(控制器 → 模块 → 应用)。
小结:一次请求在 Yii 2 中的完整协作
把以上实体串起来,一次典型请求的路径是:入口脚本定义常量、加载自动加载器与配置 → 创建应用对象并完成引导(实例化bootstrap组件、注册@app等别名、启用错误处理器)→ 触发beforeRequest→ 经urlManager/request组件解析出路由 → 按路由实例化模块(若有)与控制器→ 执行过滤器链 → 运行控制器动作,动作调用模型取数并渲染视图(含部件)→ 触发afterRequest→response 组件发送响应。所有环节都由应用对象这个全局协调中枢统一调度,这正是 Yii 2"快速、安全、专业"的架构基础。
若要继续深入,推荐按以下顺序阅读:docs/guide/structure-entry-scripts.md(入口脚本)→ docs/guide/structure-applications.md(应用对象与属性)→ docs/guide/structure-application-components.md(应用组件)→ docs/guide/structure-modules.md(模块)→ docs/guide/structure-filters.md(过滤器)→ docs/guide/structure-widgets.md(部件);底层实现可对照 framework/base/Application.php 与 framework/web/Application.php 逐行研读。
【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址: https://gitcode.com/gh_mirrors/yi/yii2
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考