Yii2 应用结构全景解析:从 MVC 骨架到入口脚本、应用对象与模块化协作
2026/9/23 2:31:55 网站建设 项目流程

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):

  1. 定义全局常量;
  2. 注册 Composer 自动加载器;
  3. 引入 Yii 类文件(本仓库即 framework/Yii.php);
  4. 加载应用配置;
  5. 创建并配置应用实例;
  6. 调用\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)强制要求配置中必须包含idbasePath,否则抛出InvalidConfigException,这印证了文档中"任何应用至少配置idbasePath两个属性"的说法。

必须配置的属性
  • id:唯一标识应用,主要用于程序化区分,官方建议仅使用字母数字字符以保证互操作性;
  • basePath:应用根目录,即包含modelsviewscontrollers等受保护源码的目录。可配置为目录路径或路径别名,目录必须真实存在,最终会经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\PostControlleradmin/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 应用在此基础上追加requestresponsesessionusererrorHandler(framework/web/Application.php)。常用的包括:

组件 ID职责
assetManageryii\web\AssetManager资源包管理与发布
dbyii\db\Connection数据库连接与查询
errorHandleryii\web\ErrorHandlerPHP 错误与异常处理
formatteryii\i18n\Formatter面向用户的格式化
i18nyii\i18n\I18N消息翻译与格式化
logyii\log\Dispatcher日志目标管理
request/responseyii\web\Request/yii\web\Response请求采集与响应输出
session/useryii\web\Session/yii\web\User会话与用户认证(仅 Web)
urlManageryii\web\UrlManagerURL 解析与创建
viewyii\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()方法可以看到一次请求处理的完整流程:

  1. 入口脚本把应用配置加载为数组;
  2. 创建应用实例:preInit()配置高优先级属性(如basePath)→ 注册错误处理器 → 配置应用属性 →init()内部调用bootstrap()运行引导组件;
  3. 调用run()
    • 触发EVENT_BEFORE_REQUEST
    • 处理请求:把请求解析为路由与参数,按路由创建模块、控制器与动作对象并执行动作;
    • 触发EVENT_AFTER_REQUEST
    • 发送响应给终端用户;
  4. 入口脚本接收退出状态码并结束处理。

框架为这一生命周期定义了六个状态常量(STATE_BEGINSTATE_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_REQUESTbeforeRequest):应用处理请求之前触发,此时应用已配置并初始化完毕,适合动态调整language等属性以拦截请求处理;
  • EVENT_AFTER_REQUESTafterRequest):应用完成请求处理但尚未发送响应时触发,可做请求后处理或定制响应;
  • EVENT_BEFORE_ACTIONbeforeAction):每个控制器动作运行之前触发,事件参数为yii\base\ActionEvent,将isValid置为false可中止动作执行。触发顺序为应用 → 模块 → 控制器,任何一层中止后后续事件不再触发;
  • EVENT_AFTER_ACTIONafterAction):每个动作运行之后触发,可通过$event->result读取或修改动作结果,触发顺序与beforeAction相反(控制器 → 模块 → 应用)。

小结:一次请求在 Yii 2 中的完整协作

把以上实体串起来,一次典型请求的路径是:入口脚本定义常量、加载自动加载器与配置 → 创建应用对象并完成引导(实例化bootstrap组件、注册@app等别名、启用错误处理器)→ 触发beforeRequest→ 经urlManager/request组件解析出路由 → 按路由实例化模块(若有)与控制器→ 执行过滤器链 → 运行控制器动作,动作调用模型取数并渲染视图(含部件)→ 触发afterRequestresponse 组件发送响应。所有环节都由应用对象这个全局协调中枢统一调度,这正是 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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询