- 后端
- Web框架
【免费下载链接】yii2
Yii 2: The Fast, Secure and Professional PHP Framework
Yii 2 框架基于经典的模型-视图-控制器(MVC)架构模式组织应用,但其完整应用结构远不止 MVC 三层:入口脚本(Entry Scripts)、应用(Applications)、应用组件(Application Components)、模块(Modules)、过滤器(Filters)与小部件(Widgets)共同构成了一个完整、可扩展的应用体系。本文以 structure-overview.md 为骨架,结合框架源码逐层拆解 Yii 2 的静态应用结构,帮助你建立对应用"全局地图"的清晰认知,并为后续阅读 MVC 各层、路由、缓存与 REST 开发文档打下基础。
Yii 应用的静态结构:入口脚本启动应用,应用聚合模块、应用组件与控制器,控制器再协调模型、视图、过滤器完成请求处理(图源:docs/guide/images/application-structure.png)
MVC 模式:应用组织的基石
Yii 应用按照 模型-视图-控制器(MVC) 架构模式进行组织,三者各司其职:
- 模型(Models):代表数据、业务逻辑与规则。模型是应用处理数据的核心对象,通常通过继承
yii\base\Model或其子类创建。 - 视图(Views):模型在输出端的呈现形式。视图负责把模型数据渲染成用户可见的 HTML 或其他格式。
- 控制器(Controllers):接收输入,并将输入转化为对模型和视图的命令。控制器是请求处理流程的"调度中心"。
三者的协作关系在框架源码中有明确体现:控制器在 actionView() 中通过Post::findOne($id)加载模型,再调用$this->render('view', ['model' => $model])将模型注入视图;在 actionCreate() 中则先$model->load(Yii::$app->request->post())接收用户输入、$model->save()保存数据,成功后重定向、失败则渲染表单视图。这一"控制器取数→模型处理→视图呈现"的闭环正是 MVC 在 Yii 2 中的标准落地形态。
设计取向:Yii 官方推荐"胖模型、瘦控制器"(Fat Models, Thin Controllers)。模型是业务数据、规则与逻辑的集中地,而控制器应当保持精简,每个 action 只包含少量协调性代码。
六大核心实体:MVC 之外的架构部件
除了 MVC 三要素,Yii 应用还包含以下六类核心实体,它们共同支撑起完整的应用骨架:
| 实体 | 作用 | 关键类 / 文档 |
|---|---|---|
| 入口脚本(Entry Scripts) | 用户可直接访问的 PHP 脚本,负责启动一次请求处理周期 | yii\web\Application、structure-entry-scripts.md |
| 应用(Applications) | 全局可访问的对象,管理应用组件并协调它们完成请求 | yii\base\Application、structure-applications.md |
| 应用组件(Application Components) | 注册在应用上的对象,为处理请求提供各类服务 | yii\base\Application::$components、structure-application-components.md |
| 模块(Modules) | 自包含的软件单元,内部拥有完整的 MVC 结构;应用可由多个模块组成 | yii\base\Module、structure-modules.md |
| 过滤器(Filters) | 在控制器处理每个请求前后被调用的代码 | yii\base\ActionFilter、structure-filters.md |
| 小部件(Widgets) | 可嵌入视图的对象,可能包含控制器逻辑,可在不同视图中复用 | yii\base\Widget、structure-widgets.md |
如上图所示,这些实体之间存在清晰的层级与包含关系:入口脚本 1:1 关联应用;应用可以聚合 0..n 个模块、0..n 个应用组件,且必须包含至少 1 个控制器;控制器包含过滤器、视图与模型;视图内部又可以嵌入小部件并注册资源包(Asset Bundles)。下面逐一深入。
入口脚本:请求周期的起点
入口脚本是应用引导(bootstrapping)过程的第一步。无论 Web 应用还是控制台应用,都只有一个入口脚本,最终用户通过请求入口脚本来实例化应用对象并把请求转交给它。
Web 应用入口脚本必须存放在 Web 可访问目录下,通常命名为index.php;控制台应用入口脚本通常存放在应用根目录下、命名为yii(无.php后缀)并设置为可执行,以便通过./yii <route> [arguments] [options]运行控制台命令。
入口脚本的核心工作可归纳为六步:
- 定义全局常量;
- 注册 Composer 自动加载器;
- 引入
Yii类文件; - 加载应用配置;
- 创建并配置应用实例;
- 调用
yii\base\Application::run()处理传入请求。
Basic 项目模板的 Web 入口脚本完整代码如下:
<?php defined('YII_DEBUG') or define('YII_DEBUG', true); defined('YII_ENV') or define('YII_ENV', 'dev'); // register Composer autoloader require __DIR__ . '/../vendor/autoload.php'; // include Yii class file require __DIR__ . '/../vendor/yiisoft/yii2/Yii.php'; // load application configuration $config = require __DIR__ . '/../config/web.php'; // create, configure and run application (new yii\web\Application($config))->run();控制台入口脚本与之类似,但以#!/usr/bin/env php开头、使用yii\console\Application,并通过exit($exitCode)把运行退出码返回给操作系统:
#!/usr/bin/env php <?php defined('YII_DEBUG') or define('YII_DEBUG', true); defined('YII_ENV') or define('YII_ENV', 'dev'); // register Composer autoloader require __DIR__ . '/vendor/autoload.php'; // include Yii class file require __DIR__ . '/vendor/yiisoft/yii2/Yii.php'; // load application configuration $config = require __DIR__ . '/config/console.php'; $application = new yii\console\Application($config); $exitCode = $application->run(); exit($exitCode);全局常量:调试与环境的开关
入口脚本是定义全局常量的最佳位置。Yii 支持以下三个关键常量:
| 常量 | 含义 | 默认值 |
|---|---|---|
YII_DEBUG | 是否处于调试模式;开启后保留更多日志信息,异常时展示详细调用栈,仅建议开发环境开启 | false |
YII_ENV | 应用运行环境(如dev/prod),详见 Configurations | 'prod' |
YII_ENABLE_ERROR_HANDLER | 是否启用 Yii 内置的错误处理器 | true |
定义常量时常用defined('YII_DEBUG') or define('YII_DEBUG', true);这种简洁写法,等价于if (!defined('YII_DEBUG')) { define('YII_DEBUG', true); }。常量定义必须放在入口脚本最开头,以便在引入其他 PHP 文件时生效。
应用(Application):全局协调者
每个 Yii 应用系统包含唯一一个应用对象,它在入口脚本中被创建,并可通过\Yii::$app表达式全局访问。Yii 提供两种应用类型:处理 Web 请求的 yii\web\Application 和处理控制台命令的 yii\console\Application。
应用配置:一切初始化的源头
入口脚本创建应用时,会加载一份配置并应用到应用对象上。由于应用配置通常非常复杂,一般保存在配置文件中(如web.php):
require __DIR__ . '/../vendor/autoload.php'; require __DIR__ . '/../vendor/yiisoft/yii2/Yii.php'; // load application configuration $config = require __DIR__ . '/../config/web.php'; // instantiate and configure the application (new yii\web\Application($config))->run();必配属性:id 与 basePath
任何应用都至少需要配置两个属性:
- id:应用的唯一标识,主要用于程序内部使用;推荐仅使用字母数字字符以保证最佳互操作性。
- basePath:应用根目录,包含全部受保护源码(
models、views、controllers等 MVC 子目录)。可配置为目录路径或路径别名,对应目录必须存在,否则抛出异常,路径会经realpath()规范化。Yii 会基于它预定义@app别名,并由此推导出运行目录等其它重要路径(如@app/runtime)。
重要属性速览
除必配属性外,以下属性在不同应用间差异较大,通常需要显式配置:
| 属性 | 作用 | 典型配置 |
|---|---|---|
aliases | 以数组形式定义路径别名 | 'aliases' => ['@name1' => 'path/to/path1'] |
bootstrap | 指定应在引导过程中运行的组件列表 | 'bootstrap' => ['log', 'debug'] |
catchAll(Web 专用) | 让一个指定控制器动作接管所有请求,常用于维护模式 | 'catchAll' => ['offline/notice', 'param1' => 'value1'] |
components | 注册应用组件,最重要的属性 | 'components' => ['cache' => ['class' => 'yii\caching\FileCache']] |
controllerMap | 将控制器 ID 映射到任意控制器类,突破命名约定限制 | 'controllerMap' => ['account' => 'app\controllers\UserController'] |
controllerNamespace | 控制器类所在默认命名空间,默认app\controllers | 'controllerNamespace' => 'app\controllers' |
language | 应用向用户展示内容所用的语言,默认en | 'language' => 'zh-CN' |
modules | 应用包含的模块列表 | 'modules' => ['booking' => 'app\modules\booking\BookingModule'] |
name | 应用名称,用于展示,无需唯一 | 'name' => 'My App' |
params | 全局可访问的应用参数数组 | 'params' => ['thumbnail.size' => [128, 128]] |
sourceLanguage | 应用代码书写语言,默认'en-US' | 'sourceLanguage' => 'en-US' |
timeZone | 设置 PHP 运行时默认时区(等价于date_default_timezone_set()) | 'timeZone' => 'America/Los_Angeles' |
version | 应用版本号,默认'1.0' | 'version' => '2.0.0' |
另有按约定即可、通常无需改动但可打破约定的"实用属性":charset(默认UTF-8)、defaultRoute(Web 默认site,控制台默认help)、extensions(默认取自@vendor/yiisoft/extensions.php)、layout(默认main)、layoutPath(默认@app/views/layouts)、runtimePath(默认@app/runtime,必须可写且需防止终端用户访问)、viewPath(默认@app/views)、vendorPath(默认@app/vendor)、以及控制台专用的enableCoreCommands(默认true)。
bootstrap 属性的底层实现
bootstrap属性值得特别关注。从 Application::bootstrap() 源码可以看到其完整执行逻辑:首先加载@vendor/yiisoft/extensions.php中声明的扩展别名并实例化扩展引导类,然后遍历$this->bootstrap列表——每个条目可以是应用组件 ID、模块 ID、类名、配置数组或匿名函数。条目解析后,若组件类实现了 BootstrapInterface,其bootstrap($app)方法会被调用;若使用匿名函数且返回值为假(如不返回实例),则该条目被跳过。
官方对bootstrap的使用有明确警告:放太多组件会拖慢应用性能,因为每个请求都要运行同一批组件,应谨慎使用。Basic 模板在开发环境下把debug与gii模块加入 bootstrap 就是典型场景:
if (YII_ENV_DEV) { // configuration adjustments for 'dev' environment $config['bootstrap'][] = 'debug'; $config['modules']['debug'] = 'yii\debug\Module'; $config['bootstrap'][] = 'gii'; $config['modules']['gii'] = 'yii\gii\Module'; }应用生命周期:一次请求的完整旅程
应用在响应一次请求时,会经历如下生命周期(对应 Application::run() 的实现流程):
- 入口脚本将应用配置作为数组加载;
- 入口脚本创建应用实例:先调用
preInit()配置高优先级属性(如basePath),随后注册错误处理器、配置应用属性,再调用init(),init()内部进一步调用bootstrap()运行引导组件; - 入口脚本调用
run()运行应用:- 触发
EVENT_BEFORE_REQUEST事件; - 处理请求:把请求解析为路由及参数,按路由创建模块、控制器与动作对象并执行动作;
- 触发
EVENT_AFTER_REQUEST事件; - 向终端用户发送响应;
- 触发
- 入口脚本接收退出状态码,完成请求处理。
与之配套,应用在生命周期中会触发四个可挂接的事件:EVENT_BEFORE_REQUEST、EVENT_AFTER_REQUEST、EVENT_BEFORE_ACTION、EVENT_AFTER_ACTION。前两者分别在应用处理请求前后触发;后两者在每次执行控制器动作前后触发,可通过 ActionEvent::isValid 置为false来终止动作执行(beforeAction场景),或通过ActionEvent::result读取/修改动作结果(afterAction场景)。事件按"应用 → 模块 → 控制器"顺序触发beforeAction,而afterAction按相反顺序触发。
应用组件:可插拔的服务仓库
应用本质上是服务定位器(Service Locator),承载着一组提供不同服务的应用组件。例如urlManager负责把 Web 请求路由到正确的控制器,db组件提供数据库服务。
每个应用组件都有唯一 ID,可通过\Yii::$app->componentID访问,例如\Yii::$app->db获取数据库连接、\Yii::$app->cache获取主缓存。组件在首次访问时才被实例化,后续访问返回同一实例(惰性加载)。应用组件可以是任意对象,通过配置components属性注册,支持类名、配置数组、匿名函数三种写法:
[ 'components' => [ // register "cache" component using a class name 'cache' => 'yii\caching\ApcCache', // register "db" component using a configuration array 'db' => [ 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host=localhost;dbname=demo', 'username' => 'root', 'password' => '', ], // register "search" component using an anonymous function 'search' => function () { return new app\components\SolrService; }, ], ]注意:应用组件如同全局变量,注册过多会让代码难以测试和维护。很多场景下创建局部组件按需使用即可。
核心应用组件清单
Yii 预定义了一组 ID 固定、带默认配置的核心应用组件,正因它们的存在,Yii 应用才能处理用户请求:
| 组件 ID | 类 | 职责 |
|---|---|---|
assetManager | yii\web\AssetManager | 管理资源包与资源发布,见 Assets |
db | yii\db\Connection | 数据库连接(配置时须同时指定类与dsn等必需属性),见 Database Access Objects |
errorHandler | yii\web\ErrorHandler | 处理 PHP 错误与异常,见 Handling Errors |
formatter | yii\i18n\Formatter | 向用户展示数据时的格式化(数字千分位、日期长格式等),见 Data Formatting |
i18n | yii\i18n\I18N | 消息翻译与格式化,见 Internationalization |
log | yii\log\Dispatcher | 管理日志目标,见 Logging |
mailer | yii\swiftmailer\Mailer | 邮件撰写与发送,见 Mailing |
response | yii\web\Response | 表示发送给用户的响应,见 Responses |
request | yii\web\Request | 表示接收到的用户请求,见 Requests |
session | yii\web\Session | 会话信息(仅 Web 应用可用),见 Sessions and Cookies |
urlManager | yii\web\UrlManager | URL 解析与创建,见 Routing and URL Creation |
user | yii\web\User | 用户认证信息(仅 Web 应用可用),见 Authentication |
view | yii\web\View | 视图渲染,见 Views |
模块(Modules):自包含的迷你应用
模块是自包含的软件单元,内部包含模型、视图、控制器及其它支撑组件,常被视为"迷你应用"。与应用的差别在于:模块不能独立部署,必须寄居于应用之中。
目录结构与模块类
模块组织为一个目录(即模块的basePath),内部结构与应用类似:
forum/ Module.php the module class file controllers/ containing controller class files DefaultController.php the default controller class file models/ containing model class files views/ containing controller view and layout files layouts/ containing layout view files default/ containing view files for DefaultController index.php the index view file每个模块必须有唯一的模块类(继承yii\base\Module),位于模块basePath根目录下且可自动加载。模块被访问时创建唯一实例,用于在模块代码间共享数据与组件:
namespace app\modules\forum; class Module extends \yii\base\Module { public function init() { parent::init(); $this->params['foo'] = 'bar'; // ... other initialization code ... } }若init()初始化代码较多,可用\Yii::configure($this, require __DIR__ . '/config.php')从配置文件加载(配置内容与应用配置结构类似,可含components与params)。
在模块中组织 MVC
- 控制器:按惯例放在模块类命名空间的
controllers子命名空间下(如app\modules\forum\controllers\PostController),文件位于模块basePath/controllers目录。可通过配置 Module::controllerNamespace 自定义;命名空间外的控制器可用controllerMap接入(与应用级用法一致)。 - 视图:放在模块
basePath/views目录下,控制器渲染的视图位于views/ControllerID。模块可设置自己的layout(默认放在views/layouts),未配置则沿用应用的布局。 - 控制台命令:模块可在控制台模式下暴露命令。做法是在模块
init()中检测Yii::$app instanceof \yii\console\Application时把controllerNamespace指向命令命名空间(如app\modules\forum\commands),之后即可用yii <module_id>/<command>/<sub_command>调用。
使用、访问与嵌套
在应用配置中列出模块即可启用:
[ 'modules' => [ 'forum' => [ 'class' => 'app\modules\forum\Module', // ... other configurations for the module ... ], ], ]模块内控制器的路由以模块 ID 开头:forum/post/index表示forum模块中post控制器的index动作;路由只含模块 ID 时由Module::defaultRoute(默认default)决定,因此路由forum表示该模块的default控制器。模块 URL 规则应在urlManager解析请求(parseRequest())之前、即引导阶段添加(init()中无效,因为模块初始化晚于路由处理),并建议用yii\web\GroupUrlRule包裹。
获取模块实例有三种方式:MyModuleClass::getInstance()(当前请求的模块实例)、\Yii::$app->getModule('forum')(已知模块 ID)、\Yii::$app->controller->module(当前控制器所属模块)。拿到实例后可访问其参数与组件,如$module->params['maxPostCount']。
模块支持无限层级嵌套(子模块声明在父模块的modules属性中),嵌套模块内控制器的路由需包含所有祖先模块 ID,如forum/admin/dashboard/index。自 2.0.13 起模块支持服务定位器的树遍历,模块开发者应优先使用$module->get('db')而非Yii::$app->get('db'),这样模块使用者可以为模块指定独立的组件配置(例如为模块配置带module_表前缀的独立db组件)。
最佳实践:模块最适合大型应用中可按功能组划分的场景,每个功能组由专人/团队开发维护;用户管理、评论管理等通用功能也可做成模块以便跨项目复用。
过滤器(Filters):动作前后的钩子
过滤器是在控制器动作运行前后执行的对象。例如访问控制过滤器在动作前检查当前用户是否有权访问;内容压缩过滤器在动作后压缩响应内容再发送。过滤器由前置过滤(pre-filter)与后置过滤(post-filter)组成。
使用过滤器
过滤器本质上是特殊形态的行为(Behaviors),因此用法与行为一致——在控制器中重写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排除。过滤器也可声明在模块或应用中,此时作用于其下全部控制器动作(注意此时only/except中应使用路由而非动作 ID)。
多个过滤器叠加时按如下顺序执行:前置阶段按"应用 → 模块 → 控制器"顺序应用,任一过滤器取消执行则其后过滤器(含后置)不再运行;动作通过前置过滤后执行;后置阶段按"控制器 → 模块 → 应用"的逆序应用。
创建自定义过滤器
继承yii\base\ActionFilter并重写beforeAction()和/或afterAction()即可。beforeAction()返回false将跳过后续过滤器并取消动作执行。下面的例子记录动作执行耗时:
namespace app\components; use Yii; use yii\base\ActionFilter; class ActionTimeFilter extends ActionFilter { private $_startTime; public function beforeAction($action) { $this->_startTime = microtime(true); return parent::beforeAction($action); } public function afterAction($action, $result) { $time = microtime(true) - $this->_startTime; Yii::debug("Action '{$action->uniqueId}' spent $time second."); return parent::afterAction($action, $result); } }内置核心过滤器
Yii 在yii\filters命名空间下提供了丰富的内置过滤器,实现类可在 framework/filters/ 目录中查阅:
- AccessControl:基于规则集合的简单访问控制,按顺序匹配第一条规则决定允许/拒绝;规则可用
roles(如@表示已认证用户)、IP、动作等方法匹配。无规则匹配时默认拒绝访问。 - 认证方法过滤器(
yii\filters\auth命名空间):如HttpBasicAuth、HttpBearerAuth、QueryParamAuth等,常用于 RESTful API 认证(需身份类实现findIdentityByAccessToken())。 - ContentNegotiator:支持响应格式协商与应用语言协商,通过
GET参数和Accept头决定;也可作为引导组件在应用生命周期早期生效(见 structure-applications.md)。 - HttpCache:利用
Last-Modified与ETag头实现客户端缓存。 - PageCache:服务端整页缓存,支持
duration时长、dependency依赖(如基于DbDependency的 SQL 变更检测)与variations(如按语言缓存不同版本)。 - RateLimiter:基于漏桶算法的限流器,主要用于 RESTful API。
- VerbFilter:校验请求的 HTTP 方法是否允许,不允许时抛出 405 异常。
- Cors:跨域资源共享过滤器,应放在认证/授权过滤器之前以保证 CORS 头始终发送;可通过
cors属性按动作精细调优Origin、Access-Control-Request-Method、Access-Control-Request-Headers、Access-Control-Allow-Credentials、Access-Control-Max-Age(默认 86400)等头。
小部件(Widgets):视图中的可复用积木
小部件是可嵌入视图的对象,可能包含控制器逻辑,能在不同视图中复用,是构建视图的"积木"。Yii 内置了大量实用小部件,实现类位于 framework/widgets/,例如:
ActiveForm与ActiveField:生成带模型绑定与验证的表单;Breadcrumbs:面包屑导航;LinkPager/ListView/GridView:分页与数据列表/表格展示;Menu:菜单;DetailView:单条记录的键值展示;Block/ContentDecorator/FragmentCache:视图内容装饰与片段缓存。
小部件的使用方式(以表单为例,见 structure-views.md):
<?php $form = ActiveForm::begin(); ?> <?= $form->field($model, 'username') ?> <?= $form->field($model, 'password')->passwordInput() ?> <?= Html::submitButton('Login') ?> <?php ActiveForm::end(); ?>小部件通常继承yii\base\Widget,通过init()初始化属性、run()渲染输出。自定义小部件时可调用$this->render()渲染关联视图(视图默认放在小部件类文件所在目录的views子目录下)。
一张图看懂整体架构
回顾开篇的静态结构图与以上各节,可以总结出 Yii 2 应用结构的两条主线:
- 启动链:入口脚本(
index.php)→ 加载配置 → 创建应用实例 → 引导组件/模块 → 解析路由 → 创建控制器与动作 → 执行动作 → 发送响应。 - 组合链:应用聚合模块与应用组件;控制器包含过滤器、模型与视图;视图内嵌小部件并注册资源包(Asset Bundles)。过滤器贯穿"请求前/后",小部件复用视图逻辑,资源包统一管理 CSS/JS。
进阶阅读路线
本文是"结构"系列的总览,以下文档可帮助你把各个部件落到实际编码中:
- MVC 三件套:Models(属性、场景、校验、批量赋值、数据导出)、Views(渲染、布局、嵌套布局、块)、Controllers(动作、路由、动作参数、控制器生命周期);
- 运行时基础:Runtime Routing、Requests、Responses、Runtime Bootstrapping;
- 组件机制:Components、Service Locator、Configurations、Behaviors;
- 实战延伸:Widgets、Assets、Extensions、RESTful Web Services。
动手验证建议:在本地完成 Start Installation 后,打开 Basic 模板的web/index.php(对照本文入口脚本一节)、config/web.php(对照应用配置与bootstrap一节),再用 Gii 生成一个 CRUD 并跟踪其控制器、模型、视图与过滤器声明,即可把本文的静态结构图转化为切身的运行时体验。
- 后端
- Web框架
【免费下载链接】yii2
Yii 2: The Fast, Secure and Professional PHP Framework
相关推荐
Yii 2 应用结构全景解析:从 MVC 骨架到入口脚本、应用组件、模块与过滤器
Yii 2 应用结构全景解析:从 MVC 骨架到入口脚本、应用组件、模块与过滤器 本文以 Yii 2 官方指南的"应用结构总览"章节为骨架,系统梳理 Yii 2
后端Web框架Yii 2 应用结构详解:从 MVC 架构到六大核心组成实体
Yii 2 应用结构详解:从 MVC 架构到六大核心组成实体 Yii 2 框架将整个应用按照经典的 model view controller(MVC)架构模式
后端Web框架Yii 2 应用结构全景:MVC 架构与六大核心组成实体详解
Yii 2 应用结构全景:MVC 架构与六大核心组成实体详解 本文基于 Yii 2 官方指南的《应用结构总览》( docs/guide pl/structure
后端Web框架
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考