ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

Yii 2 升级实战指南:从 Yii 1.1 迁移到 2.x 的核心差异与代码改造方案 Yii 2 升级实战指南从 Yii 1.1 迁移到 2.x 的核心差异与代码改造方案【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址: https://gitcode.com/gh_mirrors/yi/yii2Yii 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 引入了远超本文综述的新功能——很多过去需要自己动手实现的功能例如自动加载、依赖注入容器、表单字段、行为与过滤器体系如今已经在框架核心中内置迁移过程中应当顺手移除旧的自制代码。本节对应官方指南正文详细差异见 英文版指南 的对应章节各子系统均有独立专题文档本文会在每节末尾给出延伸阅读链接。安装方式全面切换到 ComposerYii 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 Bindingstatic::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 前缀命名惯例如CController、CComponent被彻底废弃。命名规则与目录结构严格对齐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::createObjectBaseObject引入了统一的配置数组驱动对象初始化约定这是理解 Yii 2 一切配置组件配置、DI 容器、行为声明的钥匙。源码中 BaseObject::__construct() 的执行顺序是调用类的构造函数用传入的$config配置数组初始化对象属性内部调用Yii::configure()调用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/off1.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获取。渲染行为的两个关键调整在视图中渲染另一个视图局部视图使用$this-render()而不是 1.1 的renderPartial()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场景下email、role都是安全的可被批量赋值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。详见 控制器专题。Widgetbegin() / end() / widget() 三段式 APIWidget 基类从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]); // 内容包裹型 widgetbegin/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 routeroute即控制器路由例如sitemap/index。参数传递规则匿名参数按顺序传给动作方法的对应参数命名参数按 [[yii\console\Controller::options()]] 的声明处理。2.0 还能从命令控制器方法的 PHPDoc 注释块中自动提取并生成命令行帮助信息。详见 控制台命令专题。I18Nintl 取代内置格式化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动作。详见 过滤器专题。AssetAssetBundle 取代脚本包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 classform-group ? Html::submitButton(Login) ? /div ?php yii\widgets\ActiveForm::end(); ?$form-field()自动完成标签、输入控件、错误消息的装配.passwordInput()等链式方法则调整输入类型。详见 表单专题。Query Builder统一 Query 对象1.1 中查询构建分散在CDbCommand、CDbCriteria、CDbCommandBuilder等多个类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 RecordActiveQuery、关系 getter 与两条 SQL2.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]] 的 gettergetter 属性名即关系名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(stato1)-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 行为直接继承 Behavior1.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 取代 CUserIdentity1.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/popolari与post/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 时可按下述顺序推进每一行都能在本文对应小节找到改造样例与源码依据基础设施改用 Composer 管理依赖与安装见安装方式确认 PHP 版本满足当前发行版要求并开启intl见PHP 版本要求。类结构类名去 C 前缀并放入命名空间目录与命名空间对齐见命名空间区分BaseObject与Component的继承选择见组件与对象。对象创建构造函数统一为末尾$config [] 末尾调parent::__construct($config)用Yii::createObject()创建对象见对象配置。事件与过滤器改用trigger()/on()/off()自定义过滤器改继承ActionFilter并挂载到behaviors()见事件动作过滤器。路径与资源别名统一加前缀脚本包改造成AssetBundle见路径别名Asset。视图与控制器$this改为视图对象render()结果需echo控制器动作改为return渲染结果见视图控制器。模型与表单用scenarios()替代 unsafe 声明表单改用ActiveFormActiveField见模型表单。数据层CDbCriteria换成ActiveQueryfind()关系改为 getter 返回ActiveQuery默认值移入init()行为直接继承Behavior见Query BuilderActive RecordAR 行为。用户体系实现IdentityInterface接入yii\web\User见用户与身份。外围主题改路径映射、控制台命令用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),仅供参考
返回列表