Yii 2 升级实战指南:从 Yii 1.1 迁移到 2.x 的核心差异与代码改造方案
2026/9/23 13:11:01 网站建设 项目流程

Yii 2 升级实战指南:从 Yii 1.1 迁移到 2.x 的核心差异与代码改造方案

【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址: https://gitcode.com/gh_mirrors/yi/yii2

Yii 2 是一次对 Yii 1.1 的完全重写,两个版本之间的差异远大于普通的小版本升级。本文以官方意大利语升级指南(docs/guide-it/intro-upgrade-from-v1.md)为骨架,结合当前仓库(Yii 2.0.56-dev)的源码实现,系统梳理从 1.1 迁移到 2.x 时需要掌握的全部关键差异:从安装方式、PHP 语言特性、命名空间、对象模型,到视图、模型、控制器、Active Record、权限、URL 管理等每一个被重写的子系统。读完本文,你将能够评估既有 1.1 代码的迁移工作量,并按照 2.x 的规范完成每一类代码的逐项改造。

提示:本文引用的官方文档均以仓库根目录为起点给出相对路径;与英文指南存在翻译差异时,以仓库实际存在的 英文权威指南 为准。如果你从未使用过 Yii 1.1,可以跳过本文,直接阅读 安装指南。

写在前面:为什么这不是一次"小版本升级"

Yii 1.1 与 2.0 之间没有平滑的升级路径,因为框架的核心架构被完全重写。升级动作不能像从 1.1.x 升级到 1.1.y 那样原地替换,而是需要按照新框架的约定逐类改造你的业务代码。与此同时,Yii 2.0 引入了远超本文综述的新功能——很多过去需要自己动手实现的功能(例如自动加载、依赖注入容器、表单字段、行为与过滤器体系)如今已经在框架核心中内置,迁移过程中应当顺手移除旧的自制代码。

本节对应官方指南正文,详细差异见 英文版指南 的对应章节,各子系统均有独立专题文档,本文会在每节末尾给出延伸阅读链接。

安装方式:全面切换到 Composer

Yii 2.0 的安装与扩展管理完全基于 Composer——PHP 生态的事实标准包管理器。无论是最小化骨架(basic)还是高级项目模板(advanced),安装、更新、扩展依赖都由 Composer 统一驱动。

  • 框架核心与扩展统一通过composer.json声明依赖;
  • 创建新扩展、或将 1.1 扩展改造为 2.0 扩展时,需遵循 创建扩展 一节中约定的目录结构与包规范。

具体安装步骤(包括 Composer 安装、create-project用法、目录结构说明)请参阅 安装 Yii。

PHP 版本要求与语言层面的差异

Yii 2.0 将最低 PHP 版本从 1.1 时代的PHP 5.2大幅提升到PHP 5.4+。这一跃迁直接带来了如下语言特性红利,也是迁移时最容易出现语法错误的地方:

特性说明在 Yii 2 中的体现
命名空间(Namespace)类/函数/常量的逻辑分组全部核心类均位于yii\*命名空间下
匿名函数(Closure)回调、事件处理器事件on()、URL 规则回调等大量使用
短数组语法[...]取代array(...)配置数组、规则声明全面采用短语法
短 echo 标签<?=在视图中安全可用视图模板中的标准输出写法
SPL 接口与类迭代器、异常体系异常层次、集合类的基础
后期静态绑定(Late Static Binding)static::Active Record 的find()等静态方法可被子类正确解析
日期时间类DateTime家族格式化、时区处理的基础
Trait横向复用Yii 2 多处内置 trait 复用代码
intl 扩展国际化能力2.0 的日期/数字格式化与翻译均依赖它

需要提醒的是:PHP 5.4 是撰写官方升级指南时(Yii 2.0 早期)的最低门槛;当前仓库的框架版本为 2.0.56-dev(见 framework/BaseYii.php 中的getVersion()返回值),随着版本演进,对 PHP 运行时版本的要求也在逐步提高,实际部署请以你所安装版本的 framework/composer.json 中的require.php约束为准。

命名空间:类名"去 C 化"与目录结构对齐

Yii 2.0 最直观的变化是全面引入命名空间,几乎所有核心类都被放入命名空间,例如yii\web\Request。同时,1.1 时代的 "C" 前缀命名惯例(如CControllerCComponent)被彻底废弃。

命名规则与目录结构严格对齐:yii\web\Request表示该类定义在框架根目录的web/Request.php文件中(即 framework/web/Request.php)。得益于 Yii 内置的类加载器(autoloader),你无需手工require任何核心类文件,直接引用类名即可完成加载。

组件与对象:CComponent 一分为二

1.1 中包罗万象的CComponent在 2.0 中被拆分为两个职责清晰的基类:

  • [[yii\base\BaseObject]]:轻量级基类,通过 getter/setter 提供对象属性(property)机制。适合"纯数据结构"类,例如参数载体、配置包装。
  • [[yii\base\Component]]:继承BaseObject,在属性机制之上叠加了事件(event)与行为(behavior)能力。

从 framework/base/BaseObject.php 的源码可以看到,BaseObject通过__get()/__set()魔术方法实现了"属性即 getter/setter 调用"的约定:读取$object->label等价于调用$object->getLabel(),写入等价于调用setLabel();只有 getter 没有 setter 的属性是只读的,尝试写入会抛出InvalidCallException

选型建议:如果类不需要事件或行为,优先继承BaseObject(或直接实现属性约定),保持轻量;需要事件、行为、生命周期钩子(如init())的组件则继承Component

对象配置:统一的构造约定与 Yii::createObject

BaseObject引入了统一的"配置数组驱动对象初始化"约定,这是理解 Yii 2 一切配置(组件配置、DI 容器、行为声明)的钥匙。源码中 BaseObject::__construct() 的执行顺序是:

  1. 调用类的构造函数;
  2. 用传入的$config配置数组初始化对象属性(内部调用Yii::configure());
  3. 调用init()方法。

因此,子类若需要自定义构造函数,必须遵循如下约定——最后一个参数必须是$config = [],并且在构造函数的末尾调用parent::__construct($config)

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

其中init()的设计意图是:属性配置已在构造阶段全部生效,因此"依赖配置结果的初始化"都应放进init()而不是构造函数。

遵循上述约定后,即可用配置数组创建并初始化任意对象:

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

从 BaseYii::createObject() 的实现可以看到,createObject()对字符串类型会委托给依赖注入容器Yii::$container->get(),对配置数组则通过容器创建并应用配置;而 Yii::configure() 的本质就是把配置数组的每个键值对赋值给对象的对应属性。这套约定是整个框架"配置驱动"的基础,深入内容见 对象配置。

事件:从 onXxx 命名法到任意事件名 + trigger/on/off

1.1 中事件只能通过onBeforeSave这类固定命名方法定义;2.0 打破了这一限制,事件名可以是任意字符串,并通过三个显式方法管理:

// 触发事件:创建事件对象,交给 trigger() $event = new \yii\base\Event; $component->trigger($eventName, $event); // 绑定事件处理器 $component->on($eventName, $handler); // 解绑事件处理器 $component->off($eventName, $handler);

结合 framework/base/Component.php 的源码(_events属性存储事件名到处理器列表的映射)可以确认:多个处理器按绑定顺序依次执行;事件名区分大小写;处理器支持匿名函数、对象方法、静态类方法、全局函数四种形式。

2.0 还对事件体系做了大量增强——例如通配符事件(自 2.0.14 起支持*匹配,见_eventWildcards属性)、在配置数组中用'on add' => $handler直接绑定事件、用$event->data传递附加数据等。详见 事件专题。

路径别名:必须带 @ 前缀,与命名空间深度绑定

2.0 将路径别名(Path Alias)的应用范围从本地目录扩展到本地文件、远程 URL,并规定别名必须以@开头,以便与普通路径/URL 明确区分。例如@yii指向 Yii 框架安装目录,@webroot@app等则在入口脚本中注册。

别名在框架大部分核心代码中被原生支持,例如[[yii\caching\FileCache::cachePath]]既可直接接收目录路径,也可接收别名。

从 BaseYii::getAlias() 的实现可以看出别名解析的两条关键规则:

  • 最长匹配优先:注册了@foo@foo/bar后,解析@foo/bar/config会用@foo/bar(而不是@foo)替换,因为setAlias()内部对同一根别名下的子别名做了krsort排序;
  • /是边界字符:解析@foo/barbar/config时只能命中@foo,不会误伤@foo/bar

更重要的约定是别名与命名空间强关联:为每个根命名空间注册一个别名后,就能零配置享受 Yii 的自动加载。例如@yii对应框架安装目录,yii\web\Request就能被自动加载;使用 Zend 等第三方库时,只需注册@Zend指向其安装目录,Yii 即可自动加载该库的全部类。详见 别名专题。

视图:$this 从"当前控制器"变为 View 对象

2.0 视图系统最重要的变化:视图中的$this不再指向当前控制器或 widget,而是指向新引入的视图对象[[yii\web\View]](MVC 中 V 的载体)。想从视图访问控制器或 widget,通过$this->context获取。

渲染行为的两个关键调整:

  1. 在视图中渲染另一个视图(局部视图)使用$this->render()而不是 1.1 的renderPartial()
  2. render()现在返回渲染结果字符串,必须显式echo输出:
echo $this->render('_item', ['item' => $item]);

模板引擎方面,PHP 仍是默认模板语言;2.0 官方支持 Smarty 与 Twig 两个备选引擎,通过配置view组件的 [[yii\base\View::$renderers|View::$renderers]] 属性启用;1.1 时代的 Prado 模板引擎不再受支持。详见 模板引擎。

模型:scenarios() 取代 unsafe 声明

2.0 以 [[yii\base\Model]] 作为所有模型基类(对应 1.1 的CModel),并移除了CFormModel——表单模型同样继承Model即可。

新增的 [[yii\base\Model::scenarios()|scenarios()]] 方法用于集中声明:支持的场景有哪些、每个场景下哪些属性参与验证、哪些属性是安全的(可批量赋值):

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

语义解读:backend场景下emailrole都是安全的,可被批量赋值;frontend场景下email安全,role!前缀标记为unsafe(不可批量赋值)。两个字段仍需在rules()中声明验证规则。

关于scenarios()rules()的关系,源码 Model::scenarios() 的默认实现揭示了一个便利规则:默认情况下scenarios()会根据rules()中每个验证器的on/except自动推导场景集合SCENARIO_DEFAULT场景包含rules()中的所有属性。因此:

  • 如果rules()已覆盖全部场景,且不需要 unsafe 属性,就无需重写scenarios()
  • 只有需要精细控制属性安全性时才需要显式重写。

模型细节见 模型专题。

控制器:动作必须 return 而不是 echo

控制器基类从CController变为 [[yii\web\Controller]],动作类基类为 [[yii\base\Action]]。对既有代码影响最直接的一条:动作方法必须把要展示的内容 return 给框架,而不是直接 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; } }

注意render()返回字符串、由框架统一输出,配合异常体系(如NotFoundHttpException)即可优雅处理 404。详见 控制器专题。

Widget:begin() / end() / widget() 三段式 API

Widget 基类从CWidget变为 [[yii\base\Widget]]。为获得更好的 IDE 支持与更清晰的代码结构,2.0 引入了begin()end()widget()三个静态方法:

use yii\widgets\Menu; use yii\widgets\ActiveForm; // 简单渲染型 widget:注意需要 echo 输出结果 echo Menu::widget(['items' => $items]); // 内容包裹型 widget:begin/end 成对出现,可传入配置数组初始化属性 $form = ActiveForm::begin([ 'options' => ['class' => 'form-horizontal'], 'fieldConfig' => ['inputOptions' => ['class' => 'input-xlarge']], ]); ... 表单输入字段 ... ActiveForm::end();

从 Widget::begin() 的实现可以看到,begin()会把get_called_class()写进配置并调用Yii::createObject()创建实例,随后压入静态栈self::$stackend()再从栈顶弹出并渲染——这就是"begin/end 必须正确嵌套"的底层原因;Widget::widget() 则用输出缓冲(ob_start())捕获渲染结果并返回字符串。详见 Widget 专题。

主题:基于路径映射,CThemeManager 退场

2.0 的主题机制与 1.1 完全不同,改为路径映射:把"源视图路径"映射到"主题视图路径"。例如映射['/web/views' => '/web/themes/basic']时,源视图/web/views/site/index.php的主题化版本就是/web/themes/basic/site/index.php

由此带来的能力提升:主题现在可以作用于任意视图文件,包括不在控制器或 widget 上下文内渲染的视图。CThemeManager组件被移除,取而代之的是view组件上一个可配置的theme属性。详见 主题专题。

控制台应用:yii 与注解式帮助

控制台应用与 Web 应用结构统一——都是 controller 模型。命令控制器继承 [[yii\console\Controller]](对应 1.1 的CConsoleCommand)。

命令行执行方式为yii <route><route>即控制器路由(例如sitemap/index)。参数传递规则:

  • 匿名参数按顺序传给动作方法的对应参数;
  • 命名参数按 [[yii\console\Controller::options()]] 的声明处理。

2.0 还能从命令控制器方法的 PHPDoc 注释块中自动提取并生成命令行帮助信息。详见 控制台命令专题。

I18N:intl 取代内置格式化,i18n 组件管理翻译

2.0 移除了 1.1 内置的日期/数字格式化逻辑,全面改用 PHP 的 PECL intl 模块(这也是 PHP 5.4+ 特性清单中要求 intl 扩展的原因)。

消息翻译由i18n组件承担:该组件管理一组消息源(message source),从而可以按分类(category)使用不同的消息源(如数据库、PHP 文件、gettext PO 文件等)。详见 国际化专题。

动作过滤器:基于行为实现

2.0 的动作过滤器(action filter)本身就是行为(behavior)。自定义过滤器需继承 [[yii\base\ActionFilter]](其源码 framework/base/ActionFilter.php 继承自Behavior,并支持only/except限定过滤范围、自 2.0.9 起支持site/*通配符);使用过滤器则把过滤器类挂到控制器的behaviors()上。例如接入 [[yii\filters\AccessControl]]:

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

roles => ['@']表示仅已登录用户可访问admin动作。详见 过滤器专题。

Asset:AssetBundle 取代脚本包

2.0 引入asset bundle概念,取代 1.1 的脚本包(script packages)。asset bundle 是位于同一目录下的一组资源文件(JS、CSS、图片等)的集合,每个 bundle 由继承 [[yii\web\AssetBundle]] 的类表示;调用 [[yii\web\AssetBundle::register()]] 即可把该包资源发布为可通过 Web 访问的 URL。

与 1.1 的关键差异:注册 bundle 的页面会自动包含其中声明的 JS 与 CSS 引用,无需再手工输出<script>/<link>标签。详见 资源管理专题。

Helper:一批高价值静态工具类

2.0 内置了大量常用静态辅助类,迁移时优先用它们替换手写逻辑:

  • [[yii\helpers\Html]]——HTML 生成与转义
  • [[yii\helpers\ArrayHelper]]——数组操作
  • [[yii\helpers\StringHelper]]——字符串处理
  • [[yii\helpers\FileHelper]]——文件系统操作
  • [[yii\helpers\Json]]——JSON 编解码

完整列表与用法见 Helper 概览。

表单:ActiveField 字段模型

2.0 的表单构建围绕**字段(field)**概念展开:一个字段 = 标签 + 输入控件 + 错误信息 + 提示文本,由 [[yii\widgets\ActiveField]] 对象表示。配合 [[yii\widgets\ActiveForm]] 可以让表单代码显著更简洁:

<?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(); ?>

$form->field()自动完成标签、输入控件、错误消息的装配,.passwordInput()等链式方法则调整输入类型。详见 表单专题。

Query Builder:统一 Query 对象

1.1 中查询构建分散在CDbCommandCDbCriteriaCDbCommandBuilder等多个类;2.0 统一由 [[yii\db\Query]] 对象表达查询,内部由 [[yii\db\QueryBuilder]] 在幕后生成 SQL:

$query = new \yii\db\Query(); $query->select('id, nome') ->from('user') ->limit(10); $command = $query->createCommand(); $sql = $command->sql; $rows = $command->queryAll();

更大的红利:同一套查询构建方法可以无缝用于 Active Record。详见 Query Builder 专题。

Active Record:ActiveQuery、关系 getter 与两条 SQL

2.0 对 Active Record 的改动众多,最直观的是查询构建关系管理两大块。

CDbCriteria 退场,ActiveQuery 登场

1.1 的CDbCriteria被 [[yii\db\ActiveQuery]] 取代,后者继承 [[yii\db\Query]],因此继承了全部查询构建方法;查询从调用 [[yii\db\ActiveRecord::find()]] 开始:

// 获取所有"启用"状态的客户并按 id 排序 $clienti = Clienti::find() ->where(['stato' => $attivo]) ->orderBy('id') ->all();

关系 = getter 返回 ActiveQuery

声明关系只需定义一个返回 [[yii\db\ActiveQuery]] 的 getter,getter 属性名即关系名(1.1 时代需要在relations()方法中声明):

class Cliente extends \yii\db\ActiveRecord { public function getOrdini() { return $this->hasMany('Ordine', ['cliente_id' => 'id']); } }

使用方式:$cliente->ordini直接访问关联记录;也可以链式追加查询条件实现"带条件的关联查询":

$ordini = $cliente->getOrdini()->andWhere('stato=1')->all();

预加载(eager loading)从 JOIN 变为两条 SQL

预加载关系时,2.0 与 1.1 行为不同:1.1 生成一条包含 JOIN 的 SQL 同时取主记录与关联记录;2.0 改为执行两条独立 SQL——第一条加载主表行,第二条基于主表行取回的关键字加载关联表行。

大数据量:asArray()

对返回大量结果的查询,可用 [[yii\db\ActiveQuery::asArray()|asArray()]] 让结果以数组形式返回,避免实例化 ActiveRecord 对象,从而节省 CPU 与内存:

$clienti = Cliente::find()->asArray()->all();

默认值移到 init()

2.0 不再支持通过公共属性声明属性默认值;需要默认值就在 ActiveRecord 的init()中设置:

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

构造函数可以安全覆写

1.1 中覆写 ActiveRecord 构造函数容易出问题,2.0 已修复。注意:若需要给构造函数增加参数,通常应覆写 [[yii\db\ActiveRecord::instantiate()]] 来完成。更多变化见 Active Record 专题。

Active Record 行为:直接继承 Behavior

1.1 的CActiveRecordBehavior基类被移除,2.0 中自定义行为直接继承yii\base\Behavior;若需要响应 owner 的事件,覆写events()方法返回事件映射即可:

namespace app\components; use yii\db\ActiveRecord; use yii\base\Behavior; class MioBehavior extends Behavior { // ... public function events() { return [ ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate', ]; } public function beforeValidate($event) { // ... } }

用户与身份:IdentityInterface 取代 CUserIdentity

1.1 的CWebUser由 [[yii\web\User]] 取代,CUserIdentity被移除。2.0 的身份认证要求你为 User 模型实现 [[yii\web\IdentityInterface]],接口比旧的身份类直观得多(只需实现findIdentity()getId()getAuthKey()validateAuthKey()等少数方法)。高级应用模板(advanced template)中提供了现成的实现范例。详见 认证、授权 两节。

URL 管理:规则支持参数与默认值

2.0 的 URL 管理机制与 1.1 相似,但一个显著增强是规则支持参数与默认值。下面这条规则可以同时匹配post/popolaripost/1/popolari(在 1.1 中需要两条规则):

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

<page:\d+>是带正则约束的参数,defaults为缺省参数提供默认值。详见 URL 管理(注意:意大利语版指南中此处的链接写作runtime-url-handling.md,仓库中对应权威文档为docs/guide/runtime-routing.md)。

新旧共存:Yii 1.1 与 2.x 混用

如果希望保留一部分 1.1 旧代码并在 2.0 项目中继续使用,官方提供了"在同一应用中同时运行 Yii 1.1 与 2.0"的集成方案,具体做法(如各自初始化两套框架、共享会话与数据库连接等)请参阅 同时使用 Yii 1.1 与 2.0。

结语:一份可执行的迁移检查清单

综合全文,从 1.1 迁移到 2.x 时可按下述顺序推进,每一行都能在本文对应小节找到改造样例与源码依据:

  1. 基础设施:改用 Composer 管理依赖与安装(见"安装方式");确认 PHP 版本满足当前发行版要求并开启intl(见"PHP 版本要求")。
  2. 类结构:类名去 "C" 前缀并放入命名空间,目录与命名空间对齐(见"命名空间");区分BaseObjectComponent的继承选择(见"组件与对象")。
  3. 对象创建:构造函数统一为"末尾$config = []+ 末尾调parent::__construct($config)";用Yii::createObject()创建对象(见"对象配置")。
  4. 事件与过滤器:改用trigger()/on()/off();自定义过滤器改继承ActionFilter并挂载到behaviors()(见"事件""动作过滤器")。
  5. 路径与资源:别名统一加@前缀;脚本包改造成AssetBundle(见"路径别名""Asset")。
  6. 视图与控制器$this改为视图对象,render()结果需echo;控制器动作改为return渲染结果(见"视图""控制器")。
  7. 模型与表单:用scenarios()替代 unsafe 声明;表单改用ActiveForm+ActiveField(见"模型""表单")。
  8. 数据层CDbCriteria换成ActiveQuery+find();关系改为 getter 返回ActiveQuery;默认值移入init();行为直接继承Behavior(见"Query Builder""Active Record""AR 行为")。
  9. 用户体系:实现IdentityInterface接入yii\web\User(见"用户与身份")。
  10. 外围:主题改路径映射、控制台命令用yii <route>、格式化依赖intl、URL 规则支持参数默认值(见对应章节)。

迁移完成后,建议通读英文权威指南的其余章节(从 安装 Yii 开始),因为 2.0 引入的很多新能力(DI 容器、行为系统、控制台、REST、测试等)在 1.1 中并不存在,值得在迁移过程中一并纳入架构设计。

【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址: https://gitcode.com/gh_mirrors/yi/yii2

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询