Yii2 Applications 详解:应用生命周期、组件配置与多入口实战

Yii2 Applications 详解:应用生命周期、组件配置与多入口实战

Yii2 项目里经常出现 Yii::$app->request、Yii::$app->db、Yii::$app->user。这些对象都由 Application 统一管理。理解 Application,才能看明白 Yii 从入口脚本启动、加载配置、创建组件,到解析请求并发送响应的完整过程。

本文以 Yii 2.0 为例,讲解 Web Application 和 Console Application 的区别、入口脚本、应用配置、组件、模块、启动组件和生命周期,并用一套小型 Web/Console 配置示例把概念连起来。

Application 是什么?

Application 是 Yii 应用运行时的总协调对象。每个应用系统通常创建一个 Application 实例,用来管理当前运行环境、组件、模块、事件和请求生命周期。

Yii 有两种常见 Application:

类 处理内容 常见入口
yii\web\Application HTTP 请求 web/index.php
yii\console\Application 命令行任务 项目根目录的 yii

两者都继承 yii\base\Application。Application 又继承 Module 和 Component,因此也拥有模块、组件、行为和事件等能力。

text 复制代码
yii\base\Component
        ↑
yii\base\Module
        ↑
yii\base\Application
     ↙          ↘
Web Application  Console Application

Yii::$app 是访问当前 Application 的全局入口。Web 请求中通常指向 yii\web\Application;执行控制台命令时则指向 yii\console\Application。它是静态访问方式,不意味着所有运行场景都共享同一个对象。

Application 负责哪些事情?

Application 的主要工作包括:

  • 读取并应用启动配置;
  • 注册和管理应用组件,例如 request、db、cache、user;
  • 管理模块和控制器路由;
  • 运行启动阶段需要执行的模块或组件;
  • 触发应用级事件;
  • Web 场景下处理 HTTP 请求并发送响应,Console 场景下解析并运行命令。

应用结构可以这样理解:

text 复制代码
Entry Script
    ↓ 创建 Application
Application
    ├── Components:db、cache、request、user、log...
    ├── Modules:admin、api、debug...
    ├── Params / Aliases / Environment
    └── Lifecycle:启动、处理请求、发送响应

从入口脚本启动 Web Application

标准 Yii2 Web 入口通常是 web/index.php:

php 复制代码
<?php

defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');

require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../vendor/yiisoft/yii2/Yii.php';

$config = require __DIR__ . '/../config/web.php';

(new yii\web\Application($config))->run();

入口脚本主要做四件事:

  1. 定义调试和环境常量;
  2. 加载 Composer 自动加载器和 Yii 核心类;
  3. 读取 Web Application 配置;
  4. 创建 Application 并调用 run()。

Web 服务器的 DocumentRoot 应指向 web/,这样配置、源码、运行文件和依赖目录不会直接暴露在公网。

创建一份 Web Application 配置

配置通常放在 config/web.php。下面是一个精简示例:

php 复制代码
<?php

use yii\caching\FileCache;
use yii\db\Connection;

$params = require __DIR__ . '/params.php';

return [
    'id' => 'shop-web',
    'basePath' => dirname(__DIR__),
    'controllerNamespace' => 'app\controllers',
    'language' => 'zh-CN',
    'timeZone' => 'Asia/Shanghai',

    'aliases' => [
        '@uploads' => '@app/web/uploads',
    ],

    'bootstrap' => ['log'],

    'components' => [
        'request' => [
            // 本地开发示例值;正式环境应使用独立随机密钥并从环境配置读取。
            'cookieValidationKey' => 'replace-with-a-long-random-secret',
            'parsers' => [
                'application/json' => 'yii\web\JsonParser',
            ],
        ],
        'response' => [
            'format' => yii\web\Response::FORMAT_HTML,
            'charset' => 'UTF-8',
        ],
        'cache' => [
            'class' => FileCache::class,
        ],
        'db' => [
            'class' => Connection::class,
            'dsn' => 'mysql:host=127.0.0.1;dbname=shop',
            'username' => 'app',
            'password' => 'change-me',
            'charset' => 'utf8mb4',
        ],
        'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'enableStrictParsing' => false,
            'rules' => [],
        ],
        'user' => [
            'identityClass' => 'app\models\User',
            'enableAutoLogin' => true,
        ],
        'errorHandler' => [
            'errorAction' => 'site/error',
        ],
    ],

    'modules' => [
        'admin' => [
            'class' => 'app\modules\admin\Module',
        ],
    ],

    'params' => $params,
];

实际项目中,数据库账号、Cookie 验证密钥等不应使用示例里的占位值。可以拆到 db.php、环境变量或部署系统的密钥配置中,再由 web.php 合并配置。

常见 Application 配置项

属性 作用
id 应用标识,通常用于区分不同 Application
basePath 应用根目录,很多默认别名和路径从这里派生
controllerNamespace 默认控制器命名空间
components 注册数据库、缓存、请求等应用服务
modules 注册带独立 MVC 结构的功能模块
bootstrap 指定启动时立即初始化的组件、模块或类
params 保存普通应用参数,通过 Yii::$app->params 读取
aliases 定义路径别名,例如 @app、@runtime、@uploads
defaultRoute 未指定路由时使用的默认控制器/动作
catchAll Web 维护模式下把所有请求转到指定路由

Web Application 至少需要 id 和 basePath。Console Application 也需要自己的标识和根路径,但组件和默认控制器命名空间可能不同。

Yii::$app->db:Application Component 和 Service Locator

components 是 Application 最常用的配置区域。每个键都是组件 ID,值可以是类名或配置数组:

php 复制代码
'components' => [
    'cache' => [
        'class' => yii\caching\FileCache::class,
    ],
    'db' => [
        'class' => yii\db\Connection::class,
        'dsn' => 'mysql:host=127.0.0.1;dbname=shop',
        'username' => 'app',
        'password' => 'change-me',
    ],
],

之后可通过组件 ID 访问:

php 复制代码
$db = Yii::$app->db;
$cache = Yii::$app->cache;
$request = Yii::$app->request;

应用组件通常是按需创建 :第一次访问 Yii::$app->db 时,Yii 根据配置创建 Connection;同一个 Application 后续访问会复用该实例。需要在启动阶段就初始化的组件,可以放进 bootstrap。

Application 在这里充当 Service Locator(服务定位器):根据 ID 返回对应服务。使用全局定位器很方便,但业务类大量直接读取 Yii::$app 会让依赖藏在方法内部,不利于单元测试。复用性高的服务可以通过构造函数显式接收依赖;控制器和少量框架入口代码使用 Yii::$app 则很常见。

组件、模块和参数有什么区别?

  • Component 提供一项服务,例如数据库连接、缓存、邮件发送器。
  • Module 是一块相对独立的应用结构,通常有自己的控制器、模型、视图和子模块,例如 admin、api。
  • Params 是一组普通配置值,不是组件,也不会提供服务方法。

示例:

php 复制代码
$language = Yii::$app->language;
$pageSize = Yii::$app->params['pageSize'];
$cache = Yii::$app->cache;
$adminModule = Yii::$app->getModule('admin');

模块可以通过路由前缀访问,例如 admin/user/index。是否提前加载模块取决于它是否需要参与应用启动阶段;只有需要注册 URL 规则、事件监听器等启动行为时才放进 bootstrap。

bootstrap:哪些内容需要应用启动时就加载?

bootstrap 列表中的组件或模块会在 Application 初始化阶段被创建。常见用途是注册路由规则、事件监听器或全请求都需要的基础设施。

php 复制代码
'bootstrap' => [
    'log',
    'admin',
    app\bootstrap\AppBootstrap::class,
],

自定义启动类可以实现 yii\base\BootstrapInterface:

php 复制代码
<?php

namespace app\bootstrap;

use Yii;
use yii\base\BootstrapInterface;

class AppBootstrap implements BootstrapInterface
{
    public function bootstrap($app)
    {
        $app->on(\yii\base\Application::EVENT_BEFORE_REQUEST, function () {
            Yii::info('开始处理请求', 'application');
        });
    }
}

BootstrapInterface 的 bootstrap() 会在每次应用实例启动时调用。Web PHP-FPM 传统模式下一次请求通常创建一次 Application;常驻应用服务器中则可能复用进程,因此启动阶段和请求阶段的状态边界需要格外清楚。

启动列表不是"所有组件清单"。过多的启动组件会让每次请求都多做初始化工作;没有必须提前运行的服务,应保持懒加载。

Web Application 生命周期

调用 (new yii\web\Application($config))->run() 后,主要过程如下:

text 复制代码
应用对象创建
   ↓
应用配置写入对象属性
   ↓
初始化扩展和 bootstrap 组件
   ↓
触发 EVENT_BEFORE_REQUEST
   ↓
解析路由并执行 Controller Action
   ↓
触发 EVENT_AFTER_REQUEST
   ↓
发送 Response

Application 事件包括 EVENT_BEFORE_REQUEST 和 EVENT_AFTER_REQUEST。可以通过事件处理器记录请求级信息或执行轻量的全局处理:

php 复制代码
use yii\base\Application;

$config['on ' . Application::EVENT_BEFORE_REQUEST] = function ($event) {
    Yii::info('请求开始', 'application');
};

$config['on ' . Application::EVENT_AFTER_REQUEST] = function ($event) {
    Yii::info('请求处理结束', 'application');
};

事件处理器也可以绑定到 Application 实例:

php 复制代码
$app = new yii\web\Application($config);
$app->on(yii\base\Application::EVENT_BEFORE_REQUEST, function ($event) {
    Yii::info('请求开始', 'application');
});
$app->run();

入口脚本中绑定的事件必须在 run() 之前完成。若处理器需要参与早期启动,例如注册 URL 规则,应该使用 BootstrapInterface,而不是等请求开始事件才处理。

Console Application:另一套入口和配置

Yii 的命令行入口通常是项目根目录的 yii:

php 复制代码
#!/usr/bin/env php
<?php

defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');

require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/vendor/yiisoft/yii2/Yii.php';

$config = require __DIR__ . '/config/console.php';

$application = new yii\console\Application($config);
exit($application->run());

config/console.php 通常复用 Web 配置中的数据库和业务参数,但不会配置 request、response、user 这类 Web 专属服务:

php 复制代码
<?php

$db = require __DIR__ . '/db.php';
$params = require __DIR__ . '/params.php';

return [
    'id' => 'shop-console',
    'basePath' => dirname(__DIR__),
    'controllerNamespace' => 'app\commands',
    'components' => [
        'db' => $db,
        'cache' => [
            'class' => yii\caching\FileCache::class,
        ],
    ],
    'params' => $params,
];

因此,同一个进程入口不会同时把 Yii::$app 当作 Web 和 Console Application。高级模板会拆出 frontend、backend 和 console 多个应用,它们可共享 common 目录中的代码,但各自拥有独立配置和 Application 实例。

环境配置:把共同配置和差异拆开

项目配置可以组合多个 PHP 数组,减少 Web、Console 和环境之间的重复:

php 复制代码
<?php

$common = require __DIR__ . '/common.php';
$web = require __DIR__ . '/web-local.php';

return yii\helpers\ArrayHelper::merge($common, $web);

常见拆分方式:

text 复制代码
config/
├── common.php       # Web 和 Console 共用的组件与参数
├── web.php          # Web Application 配置
├── console.php      # Console Application 配置
├── web-local.php    # 本机或部署环境的 Web 覆盖项
├── console-local.php
├── db.php           # 数据库连接配置
└── params.php       # 普通应用参数

基础模板和高级模板的目录安排不同,配置合并方式也会随模板变化。环境差异应放在本地配置或环境变量中;密码、密钥等敏感值不应提交到公共版本库。

params、aliases 和环境常量

params

params 用于保存一般配置值,例如分页大小、客服邮箱或业务开关:

php 复制代码
// config/params.php
return [
    'pageSize' => 20,
    'supportEmail' => 'support@example.com',
];

读取方式:

php 复制代码
$pageSize = Yii::$app->params['pageSize'];

参数不是密钥保险箱。数据库密码、签名密钥等敏感内容应从部署环境的秘密配置中读取。

aliases

路径别名让代码不必依赖绝对路径:

php 复制代码
'aliases' => [
    '@uploads' => '@app/web/uploads',
],

后续可使用 Yii::getAlias('@uploads') 解析为真实路径。框架内置 @app、@vendor、@runtime 等常用别名。

YII_DEBUG 和 YII_ENV

入口脚本通常定义环境常量:

php 复制代码
defined('YII_DEBUG') or define('YII_DEBUG', false);
defined('YII_ENV') or define('YII_ENV', 'prod');

开发环境可启用详细错误和调试模块;生产环境应关闭调试信息,避免堆栈和配置细节暴露。YII_ENV 常用于区分配置分支,例如只在开发环境加载 Gii 和 Debug 模块。

Yii2 Advanced Template 的多个 Application

高级模板通常包含 frontend、backend、console 三个应用:

  • frontend 面向普通访问者;
  • backend 面向管理后台;
  • console 负责计划任务、队列和部署命令;
  • common 放多个应用共享的 Model、组件或配置。

这不是一个 Application 根据 URL 自动切换模式,而是多份入口脚本分别创建不同 Application。Web 前端、管理后台、Console 可以共享业务代码,同时分别配置路由、认证、组件和环境参数。

一个控制器中的 Application 使用示例

Application 提供的组件在控制器和服务中都可以访问:

php 复制代码
<?php

namespace app\controllers;

use Yii;
use yii\web\Controller;

class SiteController extends Controller
{
    public function actionStatus()
    {
        $db = Yii::$app->db;
        $cache = Yii::$app->cache;
        $requestId = Yii::$app->request->get('requestId');

        Yii::info('状态接口被调用', 'application');

        return $this->asJson([
            'application' => Yii::$app->id,
            'environment' => YII_ENV,
            'request_id' => $requestId,
            'database_ready' => $db->isActive,
            'cache_enabled' => $cache !== null,
        ]);
    }
}

这里展示的是组件入口,不代表控制器应当直接承担所有服务编排。复杂业务可以拆到 Service,再通过应用容器配置或构造函数注入维护依赖边界。

常见问题

为什么 Yii::$app->db 第一次访问比较慢?

应用组件默认延迟创建。首次读取组件时,Yii 要根据配置创建对象并完成初始化;后续访问同一个 Application 中的该组件会复用实例。数据库连接也可能在第一次真正执行 SQL 时才建立。

为什么 bootstrap 里不建议放很多组件?

启动组件会在 Application 初始化时提前创建或运行。Web 请求通常每次都要创建 Application,启动列表越重,请求开销越大。只把需要参与整个应用生命周期的模块或组件放入列表。

Yii::$app->params 和 Yii::$app->components 有什么区别?

params 是普通键值配置;components 是按 ID 注册的服务对象,可能有初始化过程、方法和状态。

Web 和 Console 能使用同一个配置吗?

可以共享数据库连接、缓存、日志和业务参数等共同配置,但 Web 专属组件和 Console 专属组件应分别配置。通常做法是拆出公共配置,再由各自入口组合。

Advanced Template 为什么有多个 Yii::$app?

frontend、backend 和 console 是不同入口创建的不同 Application。每个进程执行时只有当前入口对应的 Application 作为 Yii::$app。

小结

Yii2 Application 管理应用配置、组件、模块和生命周期。Web 与 Console 使用不同 Application 类和入口脚本;Yii::$app 提供当前 Application 的访问入口;components 按需提供服务;bootstrap 负责少量必须在启动时运行的内容。

掌握这几个部分后,应用如何从 index.php 进入 Controller、如何读取全局服务、如何拆分环境配置,以及 Advanced Template 为何有多个应用入口,就能串成一条完整链路。

参考资料

相关推荐
我是唐青枫1 小时前
Yii2 Controller 详解:路由、Action、过滤器与 CRUD 实战
php
Nathan__274 小时前
PHP面试题——情景应用
开发语言·php
北京盛世宏博6 小时前
异地机房采集踩坑笔记:以太网温湿度记录仪跨网段通信调试实战
开发语言·笔记·单片机·php·传感器·温湿度
盛世宏博智慧档案9 小时前
IP 温湿度设备频繁断连?TCP 心跳保活机制调试与参数设置指南
网络·网络协议·tcp/ip·php·传感器·温湿度
Soari9 小时前
交换机转发模式
开发语言·网络·php
ESDWAN11 小时前
海外股票网络环境优化与交易场景应用:从行情获取到交易系统稳定性设计
开发语言·网络·php
WeiXin_DZbishe20 小时前
基于springboot宠物领养系统-计算机毕设 附源码【课程设计】94217
vue.js·spring boot·python·django·php·课程设计
WeiXin_DZbishe1 天前
基于springboot城市公交实时运营与调度系统-计算机毕设 附源码【课程设计】66173
java·vue.js·spring boot·后端·python·php·课程设计
WeiXin_DZbishe1 天前
基于Springboot+AI的kuall音乐广场的设计与实现-计算机毕设 附源码【课程设计】12715
vue.js·人工智能·spring boot·后端·python·php·课程设计