从 Yii 1.1 升级到 Yii 2.0:核心差异、迁移清单与源码级对照指南

原创2026-09-23 12:57:501,424 阅读
文章标签:后端Web框架

从 Yii 1.1 升级到 Yii 2.0:核心差异、迁移清单与源码级对照指南

Yii 2.0 对框架进行了彻底重写,从 Yii 1.1 升级并非简单的版本号切换,而是一次涉及命名空间、对象模型、视图渲染、数据库层与扩展生态的全方位重构。本文以官方《Definitive Guide》的 Upgrading from Version 1.1 为骨架,逐项梳理两个版本间的重大差异,并结合当前仓库(gh_mirrors/yi/yii2)的框架源码(framework/ 目录)给出实现层面的印证,帮助你建立一份可执行的迁移清单。

如果你从未使用过 Yii 1.1,可以直接跳过本节,从 开始使用 Yii 2.0 读起。另外请注意,本文只覆盖了 2.0 引入的新特性中与升级最相关的一部分,完整能力请通读整个 Definitive Guide 目录——很多 1.1 时代需要自己手写的能力,如今已内置在核心代码中。

安装方式:全面拥抱 Composer

Yii 1.1 时代通常以压缩包或 SVN 方式部署框架;Yii 2.0 则完全基于 Composer(PHP 生态的事实标准包管理器)进行安装。核心框架本体与所有扩展都通过 Composer 管理,安装步骤请参考 安装 Yii

对于扩展开发者,迁移意味着:

  • 1.1 扩展若要在 2.0 中使用,需要按 2.0 的规范重新发布;
  • 创建新扩展或改造旧扩展的具体流程,参考指南中的 Creating Extensions 章节;
  • 扩展的自动加载依赖 Composer 的 autoload 配置,或通过路径别名与 Yii 自带类加载器协作(见下文"路径别名")。

当前仓库根目录的 composer.jsonautoload 部分即体现了 Yii 2.0 自身的类加载约定:框架核心类以 yii\ 为命名空间前缀映射到 framework/ 目录,这正是"命名空间即目录结构"约定的落地。

PHP 版本要求:从 5.2 到 5.4+

Yii 2.0 要求 PHP 5.4 或更高版本,相比 1.1 所需的 PHP 5.2 是一次巨大的进步。这一版本提升带来了语言层面的多项能力,迁移代码时需要特别注意:

  • 命名空间(Namespaces):类与函数按命名空间组织,彻底取代 1.1 的全局类名约定;
  • 匿名函数(Closures):事件处理器、回调、验证器配置中大量使用 function ($event) { ... } 语法;
  • 短数组语法:使用 [...] 取代 array(...),这在配置数组、查询条件、规则声明中会频繁出现;
  • 短输出标签 <?=:视图文件中可直接使用,从 PHP 5.4 起可安全使用;
  • SPL 类与接口:如 ArrayObjectCountable 等;
  • 后期静态绑定(Late Static Bindings):框架内部 static::className()get_called_class() 的实现基础;
  • DateTime 相关扩展
  • Traits:Yii 2.0 核心大量使用 trait 复用代码(例如 framework/db/ActiveQueryTrait.php 就是 ActiveQuery 关系查询能力的 trait 实现);
  • intl 扩展:Yii 2.0 的国际化(I18N)功能依赖 PECL intl 模块,用于日期、数字格式化等。

命名空间:告别 C 前缀

Yii 2.0 最直观的变化是全面使用命名空间。几乎所有核心类都带上了命名空间前缀,例如 yii\web\Requestyii\base\Model。"C" 前缀(如 CWebUserCController)已彻底取消,命名规范改为与目录结构一一对应

  • yii\web\Request 对应框架目录下的 web/Request.php
  • yii\base\Model 对应 base/Model.php
  • yii\db\ActiveRecord 对应 db/ActiveRecord.php

这一约定与 Yii 类加载器配合,让你无需显式 include 任何核心类文件即可直接使用(参见 类自动加载)。

Component 与 Object:一个类拆成两个

Yii 1.1 的 CComponent 在 2.0 中被拆分为两个基类:

基类 能力 适用场景
yii\base\BaseObject 通过 getter/setter 定义对象属性 不需要事件/行为的类,如纯数据结构类
yii\base\Component 继承 BaseObject,额外支持事件行为 需要事件或行为能力的类

判断标准很简单:如果类不需要事件或行为特性,就用 BaseObject 作为基类。在源码层面,framework/base/Component.php 第 105 行明确写着 class Component extends BaseObject,它正是在父类属性特性之上叠加了 $_events$_eventWildcards$_behaviors 等成员;而 framework/base/BaseObject.php 只负责属性访问与对象初始化生命周期。

对象配置:统一构造器约定与 init() 生命周期

BaseObject 引入了一套统一的对象配置方式。任何 BaseObject 子类若需声明构造器,必须遵守以下约定——构造器最后一个参数必须是配置数组 $config,并在构造器末尾调用 parent::__construct($config)

class MyClass extends \yii\base\BaseObject
{
    public function __construct($param1, $param2, $config = [])
    {
        // ... 配置应用之前的初始化

        parent::__construct($config);
    }

    public function init()
    {
        parent::init();

        // ... 配置应用之后的初始化
    }
}

这套约定的背后是 BaseObject 构造器的标准流程(framework/base/BaseObject.php):

  1. 调用类构造器;
  2. $config 非空,则通过 Yii::configure($this, $config) 将配置中的名值对批量写入对象属性;
  3. 调用 init() 方法。

因此,需要"配置已就绪"的初始化逻辑应写在 init() 中,而不是构造器里。遵循此约定后,你可以用配置数组统一创建与配置对象:

$object = Yii::createObject([
    'class' => 'MyClass',
    'property1' => 'abc',
    'property2' => 'cde',
], [$param1, $param2]);

Yii::createObject()framework/BaseYii.php)在底层委托给 DI 容器 Yii::$container->get(),这也是为什么 2.0 的所有组件、widget、行为都能通过配置数组实例化。更多细节见 配置(Configurations)

事件:任意事件名 + trigger()/on()/off()

Yii 1.1 中事件通过定义 on 前缀方法(如 onBeforeSave)实现;2.0 中你可以使用任意事件名。触发事件使用 trigger()

$event = new \yii\base\Event;
$component->trigger($eventName, $event);

绑定事件处理器使用 on(),解绑使用 off()

$component->on($eventName, $handler);
// 解绑:
// $component->off($eventName, $handler);

事件能力还大幅增强(见 Events):

  • 处理器支持匿名函数、对象方法、静态类方法、全局函数四种形式(framework/base/Component.php 的文档注释有完整说明);
  • 可通过 on() 的第三个参数为处理器附加额外数据,在回调中经 $event->data 读取;
  • 支持在组件配置数组中用 'on add' => ... 语法直接声明事件;
  • 从 2.0.14 起支持通配符事件($_eventWildcards);
  • 行为(Behavior)可通过重写 events() 方法声明需要响应的事件(见下文 Active Record 行为一节)。

路径别名:@ 前缀 + 别名即自动加载

Yii 2.0 将路径别名的适用范围扩展为"文件/目录路径 + URL",并且强制要求别名以 @ 开头,以便与普通路径/URL 区分。例如 @yii 指向 Yii 安装目录。

路径别名在核心代码中被广泛支持,例如 yii\caching\FileCachecachePath 属性既可以传普通目录路径,也可以传路径别名。

别名与类命名空间密切相关:建议为每个根命名空间定义一个路径别名,这样无需任何额外配置即可让 Yii 类自动加载器工作。例如:

  • @yii 指向 Yii 安装目录 → yii\web\Request 可被自动加载;
  • 使用第三方库(如 Zend Framework)时,定义 @Zend 指向其安装目录 → Zend 的类也能被自动加载。

详细机制见 Aliases类自动加载

视图(Views):$this 变成 View 对象

2.0 中视图最显著的变化是:视图内的 $this 不再指向当前控制器或 widget,而是指向一个"视图对象"——yii\web\View 类型的实例,代表 MVC 中的视图部分。若要在视图中访问控制器或 widget,请使用 $this->context

渲染子视图的方式也变了:

// 1.1: $this->renderPartial('_item', ...);
// 2.0: 使用 $this->render(),且必须显式 echo(render() 返回渲染结果而非直接输出)
echo $this->render('_item', ['item' => $item]);

模板引擎方面:

  • 除 PHP 作为主模板语言外,2.0 官方支持 SmartyTwig 两套模板引擎;
  • Prado 模板引擎已不再支持
  • 使用这些引擎需通过 view 应用组件的 View::$renderers 属性进行配置。

详见 模板引擎

模型(Models):scenarios() 取代 unsafe

2.0 使用 yii\base\Model 作为模型基类(对应 1.1 的 CModel)。CFormModel 已被彻底移除,表单模型直接继承 yii\base\Model

新引入的 scenarios() 方法用于声明支持的场景,并指明每个属性在场景下是否参与校验、是否安全(可批量赋值):

public function scenarios()
{
    return [
        'backend' => ['email', 'role'],
        'frontend' => ['email', '!role'],
    ];
}

上例声明了两个场景:

  • backendemailrole 均安全,可被批量赋值;
  • frontendemail 可批量赋值,role 不可(! 前缀表示不安全);
  • 两个属性都应通过 rules() 中的规则参与校验。

rules() 方法仍然用来声明校验规则。由于 scenarios() 的引入,不再存在 unsafe 校验器。大多数情况下,如果 rules() 已经完整覆盖了所有场景、且无需声明 unsafe 属性,就不必重写 scenarios()——因为 Model::scenarios() 的默认实现(framework/base/Model.php)会自动从 rules() 声明中提取场景:默认场景 SCENARIO_DEFAULT 包含 rules() 中出现的所有属性,校验器上的 on/except 声明也会生成对应场景。

更深入的内容见 Models校验输入

控制器(Controllers):action 返回内容而非 echo

2.0 以 yii\web\Controller 作为控制器基类(对应 CController),yii\base\Action 是动作类基类。对你代码影响最直接的一点是:控制器动作应返回要渲染的内容,而不是直接 echo

public function actionView($id)
{
    $model = \app\models\Post::findOne($id);
    if ($model) {
        return $this->render('view', ['model' => $model]);
    } else {
        throw new \yii\web\NotFoundHttpException;
    }
}

注意 findOne() 的返回值需要判空,未找到时抛出 NotFoundHttpException。详见 Controllers

Widgets:begin()/end()/widget() 静态方法

2.0 以 yii\base\Widget 作为 widget 基类(对应 CWidget)。为获得更好的 IDE 支持,引入了新的静态方法语法 begin()end()widget()framework/base/Widget.php 中这三个静态方法通过 Yii::createObject() 创建实例,widget() 内部用输出缓冲捕获并返回渲染结果):

use yii\widgets\Menu;
use yii\widgets\ActiveForm;

// 注意:必须 "echo" 结果才能显示
echo Menu::widget(['items' => $items]);

// 传入数组初始化对象属性
$form = ActiveForm::begin([
    'options' => ['class' => 'form-horizontal'],
    'fieldConfig' => ['inputOptions' => ['class' => 'input-xlarge']],
]);
... 表单输入字段 ...
ActiveForm::end();

详见 Widgets

主题(Themes):基于路径映射,不再有 CThemeManager

2.0 的主题机制完全重写,基于路径映射:把源视图文件路径映射到主题视图文件路径。例如主题路径映射为 ['/web/views' => '/web/themes/basic'],则视图文件 /web/views/site/index.php 的主题版本为 /web/themes/basic/site/index.php。因此,主题现在可以作用于任意视图文件,即使是控制器或 widget 上下文之外渲染的视图。

同时,CThemeManager 组件不复存在,theme 变成 view 应用组件的可配置属性。详见 主题化

控制台应用:控制器化 + 自动帮助信息

控制台应用现在像 Web 应用一样以控制器组织。控制台控制器继承自 yii\console\Controller(对应 1.1 的 CConsoleCommand)。

运行控制台命令的语法为:

yii <route>

其中 <route> 是控制器路由(如 sitemap/index)。额外的匿名参数作为对应 action 方法的参数传入,具名参数则按照 Controller::options() 中的声明解析。

此外,Yii 2.0 支持从注释块自动生成命令帮助信息。详见 控制台命令。仓库根目录的 framework/yii 就是控制台入口脚本。

国际化(I18N):移除内置格式化器,改用 intl

2.0 移除了内置的日期格式化器与数字格式化器,转而使用 PECL intl 模块

消息翻译改为通过 i18n 应用组件完成。该组件管理一组消息源(message sources),允许你根据消息分类使用不同的消息源(如数据库消息源、文件消息源等)。详见 国际化

动作过滤器(Action Filters):行为化实现

2.0 中动作过滤器通过**行为(behavior)**实现:

  • 自定义过滤器:继承 yii\base\ActionFilter
  • 使用过滤器:将过滤器类作为行为附加到控制器。

例如在控制器中使用 yii\filters\AccessControl

public function behaviors()
{
    return [
        'access' => [
            'class' => 'yii\filters\AccessControl',
            'rules' => [
                ['allow' => true, 'actions' => ['admin'], 'roles' => ['@']],
            ],
        ],
    ];
}

上面规则的含义:允许已登录用户(@)访问 admin 动作。完整的过滤器体系见 过滤器

资源(Assets):Asset Bundle 取代脚本包

2.0 引入**资源包(asset bundle)**概念,取代 1.1 的脚本包(script packages)概念:

  • 资源包是某个目录下资源文件(JS、CSS、图片等)的集合;
  • 每个资源包用一个继承 yii\web\AssetBundle 的类表示(AssetBundle 本身继承自 BaseObject,见 framework/web/AssetBundle.php);
  • 通过 AssetBundle::register() 注册后,资源即通过 Web 可访问;
  • 与 1.1 不同,注册资源包的页面会自动包含该包声明的 JS/CSS 引用

详见 资源管理

助手类(Helpers):大量静态工具类

2.0 提供了许多常用的静态助手类,包括:

  • yii\helpers\Html:HTML 生成与转义;
  • yii\helpers\ArrayHelper:数组操作(getValuemapindexmerge 等);
  • yii\helpers\StringHelper:字符串工具;
  • yii\helpers\FileHelper:文件系统操作;
  • yii\helpers\Json:JSON 编解码。

这些类在 framework/helpers/ 目录中均有对应实现。详见 助手类概览

表单(Forms):field 概念让表单更简洁

2.0 基于 yii\widgets\ActiveForm 引入了 field(字段) 概念。一个 field 是"标签 + 输入控件 + 错误消息 + 提示文本"的容器,由 yii\widgets\ActiveField 对象表示。使用 field 构建表单比 1.1 更干净:

<?php $form = yii\widgets\ActiveForm::begin(); ?>
    <?= $form->field($model, 'username') ?>
    <?= $form->field($model, 'password')->passwordInput() ?>
    <div class="form-group">
        <?= Html::submitButton('Login') ?>
    </div>
<?php yii\widgets\ActiveForm::end(); ?>

field 会依据模型的校验规则自动输出对应的错误消息与提示。详见 创建表单

查询构建器(Query Builder):统一 Query 对象

1.1 中查询构建分散在 CDbCommandCDbCriteriaCDbCommandBuilder 等多个类中;2.0 用一个 yii\db\Query 对象表示数据库查询,底层由 yii\db\QueryBuilder 将其转换为 SQL 语句:

$query = new \yii\db\Query();
$query->select('id, name')
      ->from('user')
      ->limit(10);

$command = $query->createCommand();
$sql = $command->sql;
$rows = $command->queryAll();

最关键的是:这些查询构建方法同样适用于 Active Record(见下节)。详见 查询构建器

Active Record:ActiveQuery 取代 CDbCriteria

2.0 对 Active Record 做了大量改动,最明显的是查询构建关系查询两方面。

查询:find() + ActiveQuery

1.1 的 CDbCriteriayii\db\ActiveQuery 取代。ActiveQuery 继承自 yii\db\Query,因此继承了全部查询构建方法。通过 ActiveRecord::find() 开始构建查询(framework/db/ActiveRecord.phpfind() 会创建 ActiveQuery 实例并传入当前 AR 类名):

// 取出所有 status 为 $active 的客户并按 ID 排序
$customers = Customer::find()
    ->where(['status' => $active])
    ->orderBy('id')
    ->all();

关系:getter 方法即关系

声明关系只需定义一个返回 ActiveQuery 对象的 getter 方法,getter 定义的属性名即关系名。1.1 需要在集中的 relations() 中声明,2.0 改为:

class Customer extends \yii\db\ActiveRecord
{
    public function getOrders()
    {
        return $this->hasMany('Order', ['customer_id' => 'id']);
    }
}

之后即可用 $customer->orders 访问该客户的订单,也可动态定制关系查询条件:

$orders = $customer->getOrders()->andWhere('status=1')->all();

预加载:两条 SQL 取代 JOIN

预加载(eager loading)关系时,2.0 与 1.1 做法不同:

  • 1.1:创建一条 JOIN 查询同时选取主记录与关系记录;
  • 2.0:执行两条不带 JOIN 的 SQL——第一条取回主记录,第二条用主记录的主键过滤取回关系记录(通过 with() 指定,见 framework/db/ActiveQueryTrait.php 中的 with() 方法)。

asArray():大批量数据的内存优化

构建查询时链式调用 asArray()framework/db/ActiveQueryTrait.php),可使查询结果以数组而非 ActiveRecord 对象返回。当记录数量很大时,可显著降低 CPU 与内存消耗:

$customers = Customer::find()->asArray()->all();

属性默认值:init() 而非公共属性

另一个变化是不能再通过公共属性定义属性默认值。如果需要默认值,请在记录类的 init() 方法中设置:

public function init()
{
    parent::init();
    $this->status = self::STATUS_NEW;
}

构造器与 instantiate()

1.1 中重写 ActiveRecord 构造器存在一些已知问题,2.0 已解决。注意:当为构造器新增参数时,可能需要重写 ActiveRecord::instantiate()

Active Record 还有大量其他变化与增强,详见 Active Record

Active Record 行为:直接继承 Behavior

2.0 移除了基类 CActiveRecordBehavior。要创建 Active Record 行为,需直接继承 yii\base\Behavior;若行为需要响应属主(owner)的某些事件,重写 events() 方法:

namespace app\components;

use yii\db\ActiveRecord;
use yii\base\Behavior;

class MyBehavior extends Behavior
{
    // ...

    public function events()
    {
        return [
            ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',
        ];
    }

    public function beforeValidate($event)
    {
        // ...
    }
}

events() 返回"事件名 => 行为内方法"的映射,行为附加到组件后,这些处理器即自动挂载(行为机制详见 framework/base/Component.php行为)。

用户与身份:User + IdentityInterface

1.1 的 CWebUseryii\web\User 取代,CUserIdentity 类不复存在,取而代之的是实现 yii\web\IdentityInterfaceframework/web/IdentityInterface.php),使用起来直观得多——只需实现 findIdentity()getId()getAuthKey()validateAuthKey()(以及可选 findIdentityByAccessToken())等方法。高级项目模板(advanced project template)中提供了完整的实现示例。

相关章节见 认证授权

URL 管理:可选参数 + 驼峰转连字符

URL 管理机制与 1.1 类似,一个重要增强是支持可选参数。例如下面的规则可以同时匹配 post/popularpost/1/popular,而在 1.1 中你需要两条规则:

[
    'pattern' => 'post/<page:\d+>/<tag>',
    'route' => 'post/index',
    'defaults' => ['page' => 1],
]

详见 URL 管理

另一个命名约定变化:控制器与动作的驼峰命名会转换为小写加连字符,例如 CamelCaseController 的控制器 ID 为 camel-case。参见 controller IDsaction IDs

旧代码共存:Yii 1.1 与 2.x 一起使用

如果存在遗留的 Yii 1.1 代码需要与 Yii 2.0 共存使用,请参考 Using Yii 1.1 and 2.0 Together 章节,其中说明了如何在 2.0 应用中加载并运行 1.1 代码。

迁移要点速查表

主题 Yii 1.1 Yii 2.0
PHP 版本 5.2 5.4+(推荐更高版本)
安装 压缩包/手工部署 Composer
类命名 C 前缀(如 CController 命名空间 + 目录映射(如 yii\web\Controller
基类 CComponent yii\base\BaseObject(轻量) / yii\base\Component(事件+行为)
对象配置 构造器 $config 参数 + init() 生命周期 + Yii::createObject()
事件 on 前缀方法 trigger() / on() / off(),任意事件名
别名 无强制格式 必须以 @ 开头
视图 $this 是控制器/widget $this 是 View 对象,用 $this->context 访问控制器
表单模型 CFormModel 直接继承 yii\base\Model
场景 scenarios() 声明,!attr 表示 unsafe
控制器动作 echo 输出 return 渲染结果
Widget CWidget begin()/end()/widget() 静态方法
主题 CThemeManager view 组件上的路径映射 theme 属性
控制台 CConsoleCommand yii\console\Controller + yii <route>
过滤器 过滤器类 yii\base\ActionFilter 行为化
资源 脚本包 yii\web\AssetBundle 资源包
查询 CDbCommand/CDbCriteria/CDbCommandBuilder yii\db\Query + QueryBuilder
关系 集中的 relations() getter 方法返回 ActiveQuery
预加载 JOIN 单查询 两条 SQL(无 JOIN)
属性默认值 公共属性 init() 中设置
用户身份 CWebUser + CUserIdentity yii\web\User + 实现 IdentityInterface
路由命名 原样 驼峰转连字符(camel-case

进一步阅读

迁移完成后,建议按顺序通读以下指南以充分利用 2.0 的新能力:

登录后查看全文
yii2