ARTICLE DETAIL

资讯详情

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

Yii 2 组件(Component)完全指南:掌握属性、事件与行为三大特性的基石机制

Yii 2 组件(Component)完全指南:掌握属性、事件与行为三大特性的基石机制 后端Web框架【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址https://gitcode.com/gh_mirrors/yi/yii2点击查看免费下载本文围绕 Yii 2 框架的核心抽象——组件Component展开系统讲解其定义、三大特性属性、事件、行为、与普通对象的取舍、编写规范、创建方式及对象生命周期并结合本仓库framework/base/Component.php、framework/base/BaseObject.php等源码给出底层实现证据。读完本文你将能够按照官方规范编写可配置、可复用、支持事件与行为的自定义组件并理解 Yii 应用中一切对象应用、控制器、小部件、ActiveRecord背后统一的对象模型。什么是组件Yii 应用的主要基石在 Yii 2 中组件是 Yii 应用的主要基石。组件是yii\base\Component类或其子类的实例。Yii 2 框架中几乎所有的核心类——应用Application、控制器Controller、小部件Widget、数据库连接Connection、日志目标LogTarget、ActiveRecord 等——都直接或间接继承自yii\base\Component。yii\base\Component通过三个主要功能让普通类变得更加灵活、可定制、易于使用属性Property基于 getter/setter 的类成员访问机制让属性读写可以附加逻辑事件Event允许在特定执行点“注入”自定义代码行为Behavior无须改变继承关系即可为组件增强功能mixin 模式。这三个特性可以单独使用也可以彼此配合使用是理解 Yii 2 一切高层机制配置、依赖注入、服务定位器、ActiveRecord 生命周期钩子等的底层基础。一个直观的例子是日期选择器小部件yii\jui\DatePicker本仓库框架中对应的 UI 组件类它继承自yii\base\Component体系。它可以在视图中生成一个交互式日期选择器各项属性因为继承自 Component 而可以轻松改写use yii\jui\DatePicker; echo DatePicker::widget([ language zh-CN, name country, clientOptions [ dateFormat yy-mm-dd, ], ]);在这里language、name、clientOptions都是小部件的属性。它们之所以能通过配置数组被直接赋值正是因为底层有一套统一的“配置数组 → 属性/事件/行为”的初始化机制这正是组件模型带来的核心能力。组件的代价为什么有时选择 BaseObject 而非 Component组件的三大特性固然强大但并非没有代价。由于要为事件和行为功能提供支持yii\base\Component比普通对象要稍微“重”一些——它需要额外的内存和 CPU 时间例如维护事件处理器列表_events、行为列表_behaviors以及在每次属性/方法访问时做行为查找。因此官方建议如果你的类不需要事件和行为这两项功能可以继承yii\base\BaseObject旧版本中为yii\base\Object自 2.0.13 起引入BaseObject作为新基类名而不是yii\base\Component。这样做之后组件可以像普通 PHP 对象一样高效同时仍然支持属性Property功能。从源码结构看这个层次关系非常清晰yii\base\BaseObjectframework/base/BaseObject.php实现属性机制与统一的对象初始化生命周期并实现yii\base\Configurable接口framework/base/Configurable.phpyii\base\Componentframework/base/Component.php继承自BaseObject在其上叠加事件与行为两大特性。在挑选基类时可以遵循如下决策只需要属性 → 用 BaseObject需要属性 事件 行为 → 用 Component。大多数业务类尤其是要参与 Yii 事件机制、被配置系统管理的类都应继承Component。编写组件的编码规范务必遵守当你的类继承yii\base\Component或yii\base\BaseObject时官方强烈推荐遵循以下三条约定这是组件能够被正确配置和初始化的前提如果你需要重写构造方法Constructor请把$config参数放在构造器方法的最后一个参数位置然后把它传递给父类的构造方法永远在你重写的构造方法结尾处调用父类的构造方法如果你重写了yii\base\BaseObject::init()方法请确保在init方法的开头处调用父类的init()方法。一个标准写法示例如下?php namespace yii\components\MyClass; use yii\base\BaseObject; class MyClass extends BaseObject { public $prop1; public $prop2; public function __construct($param1, $param2, $config []) { // ... 在应用配置之前初始化 parent::__construct($config); } public function init() { parent::init(); // ... 应用配置后进行初始化 } }为什么要这样约定看 framework/base/BaseObject.php 中构造方法的实现就明白了public function __construct($config []) { if (!empty($config)) { Yii::configure($this, $config); } $this-init(); }也就是说父类构造方法会依次完成两件事把$config配置应用到对象上Yii::configure()然后调用init()。如果你在子类构造方法中没有把$config传给父类或在结尾处忘了调用parent::__construct()那么配置将永远不会生效如果你在init()中没有在开头调用parent::init()父类或其它父类链上的类的初始化逻辑会被跳过。此外BaseObject实现了Configurable接口framework/base/Configurable.php该接口不声明任何方法只要求实现类把构造方法的最后一个参数声明为配置数组。依赖注入容器正是依赖这一约定才能把对象配置作为最后一个参数传给类的构造方法。创建组件的两种方式new 与 Yii::createObject为了让组件在创建实例时能被正确配置有两种实例化方式// 方式一直接 new把配置数组作为最后一个参数传入 $component new MyClass(1, 2, [prop1 3, prop2 4]); // 方式二使用 Yii::createObject配置数组作为第一个参数构造参数作为第二个参数 $component \Yii::createObject([ class MyClass::class, prop1 3, prop2 4, ], [1, 2]);Info: 尽管调用Yii::createObject()的方法看起来更加复杂但它的优势在于更灵活、更强大——它是基于依赖注入容器实现的支持默认配置、单例singleton、依赖自动注入等能力。从源码看Yii::createObject()定义于 framework/BaseYii.php 第 348 行附近的逻辑是如果传入的是字符串则视为类名交给static::$container-get($type, $params)从容器中解析如果传入的是可调用对象则用容器invoke()调用如果传入的是数组则取出其中的class或__class键作为类名把剩余键值作为对象配置交给容器创建实例。public static function createObject($type, array $params []) { if (is_string($type)) { return static::$container-get($type, $params); } // ... if (isset($type[class])) { $class $type[class]; unset($type[class]); return static::$container-get($class, $params, $type); } // ... }而Yii::configure()framework/BaseYii.php 第 560 行附近则是逐项把配置数组的键值对赋值给对象的属性public static function configure($object, $properties) { foreach ($properties as $name $value) { $object-$name $value; } return $object; }注意$object-$name $value这种写法会触发对象的魔术方法__set()如果属性不是真实成员变量。因此配置数组不仅可以初始化普通成员变量还能初始化 getter/setter 定义的属性、附加事件处理器on xxx键和附加行为as xxx键——这正是配置Configurations章节描述的配置格式的基础。BaseObject 的对象生命周期四个阶段yii\base\BaseObject强制执行如下对象生命周期构造方法内的预初始化过程在这里可以给各属性设置缺省值通过$config配置对象配置的过程可能会覆盖掉先前在构造方法内设置的默认值在init()方法内进行初始化后的收尾工作可以通过重写此方法进行良品检验、属性归一化等操作对象方法调用此后对象进入正常使用阶段。关键在于前三步都发生在对象的构造方法内。这意味着一旦你获得了一个对象实例它就已经初始化就绪、处于可靠的可用状态无需再额外调用任何初始化方法。在实际开发中这个生命周期带来的直接实践是把“必须的默认值”放在构造方法里设置把“依赖完整配置才能做的校验和派生逻辑”放在init()中因为此时$config已经应用完毕永远不要在构造方法里调用可能被子类重写且依赖配置的方法而是把它们放到init()。源码级解读Component 如何实现三大特性yii\base\Componentframework/base/Component.php的核心是四个 PHP 魔术方法与三个私有存储结构。理解它们就理解了组件模型的底层原理。私有存储结构$_events事件名 事件处理器列表每项为[handler, data]$_eventWildcards通配符模式 处理器列表自 2.0.14 起支持通配符事件$_behaviors行为名 行为对象null表示尚未初始化懒加载。属性访问__get()/__set()Component::__get($name)的查找顺序是查找getXxx()getter存在则调用它确保行为已附加ensureBehaviors()遍历所有行为若某个行为能读取该属性则返回行为属性值若存在setXxx()但无 getter抛出InvalidCallException写属性却被读否则抛出UnknownPropertyException。Component::__set($name, $value)的查找顺序是存在setXxx()setter调用它键名以on开头则视为“附加事件处理器”调用$this-on(trim(substr($name, 3)), $value)键名以as开头则视为“附加行为”根据值的类型Behavior 对象、闭包、类名字符串或配置数组调用attachBehavior()创建并附加行为遍历行为若某个行为能写该属性则写入若存在 getter 而无 setter抛InvalidCallException只读属性被赋值否则抛UnknownPropertyException。这就是为什么在配置Configurations中可以使用on eventName $handler和as behaviorName $behaviorConfig这两种特殊键名——它们最终都经由__set()的第二步、第三步被转换为on()/attachBehavior()调用。同理__isset()、__unset()、__call()调用行为的方法、__clone()克隆时清空事件与行为避免行为指向旧对象也都遵循类似的行为感知逻辑。事件机制on()/off()/trigger()on($name, $handler, $data null, $append true)附加处理器。支持appendfalse把新处理器插入队列头部自 2.0.14 起事件名支持*通配符模式存储到_eventWildcards。off($name, $handler null)移除处理器。$handler为null时移除该事件全部处理器移除通配符处理器时只移除该通配符注册的处理器普通名称注册的处理器即使匹配模式也会保留。trigger($name, ?Event $event null)触发事件。会先收集所有匹配的通配符处理器与精确名称的处理器按附加顺序调用如果某处理器把$event-handled置为true则停止后续处理器最后还会调用类级别的处理器Event::trigger()。关于事件处理器的完整类型全局函数、对象方法、静态方法、匿名函数、$event携带的name/sender/data信息、类级别事件与接口事件、全局事件等详见事件Events章节。行为机制behaviors()与懒加载附加behaviors()是子类可重写的钩子方法默认返回[]。返回数组中每个元素可以是行为类名字符串、配置数组或 Behavior 对象键名即行为名数字键为匿名行为。ensureBehaviors()实现懒加载仅在第一次真正需要访问行为属性访问、方法调用、getBehaviors()等时才把behaviors()声明的行为真正附加进来避免无谓的开销。attachBehaviorInternal($name, $behavior)非 Behavior 对象一律通过Yii::createObject()创建命名行为若已存在同名行为会先 detach 旧的再附加新的。通过getBehavior($name)/getBehaviors()获取已附加行为通过detachBehavior($name)/detachBehaviors()移除行为。关于行为的定义继承yii\base\Behavior、事件响应覆写events()、静态/动态附加方式、命名行为与匿名行为的区别、与 PHP traits 的对比等详见行为Behaviors章节。实战要点让组件可配置的完整流程把前面所有内容串起来一个“规范、可配置、可扩展”的组件编写流程如下选择基类需要事件/行为用yii\base\Component仅需要属性用yii\base\BaseObject声明属性用公共成员变量或 getter/setter 定义属性属性名不区分大小写只读/只写属性会抛异常详见属性Properties遵守构造约定$config放最后并传给父类init()开头调parent::init()按需重写init()在配置应用完成之后做校验与派生初始化对外提供创建方式支持new MyClass(..., $config)也可通过Yii::createObject()或直接放在应用components配置数组中由容器创建获得依赖注入、默认配置等高级能力。举一个真实场景在应用配置中注册一个自定义组件参见配置Configurations中的应用配置小节底层正是由容器解析配置数组、创建Component实例并完成上述生命周期$config [ id basic, basePath dirname(__DIR__), components [ myComponent [ class app\components\MyClass, prop1 3, prop2 4, ], ], ];延伸阅读组件是 Yii 2 对象模型的枢纽与之紧密相关的概念还包括属性Propertiesgetter/setter 定义属性的规则与限制事件Events事件处理器、触发、移除、类级别与全局事件、通配符事件行为Behaviors行为定义、附加、使用与 traits 对比配置Configurations配置格式、配置文件、默认配置与环境常量依赖注入容器Yii::createObject()的底层实现基础。相关源码文件可在本仓库直接查阅framework/base/Component.php、framework/base/BaseObject.php、framework/base/Configurable.php 与 framework/BaseYii.php。总结组件是 Yii 2 一切高级能力的底座。掌握“Component 与 BaseObject 的取舍、构造与 init 的规范、配置数组如何被解析、属性/事件/行为三大特性的底层实现”这四件事你就掌握了 Yii 2 对象模型的精髓——无论是自定义业务组件、理解框架内置类还是阅读第三方扩展源码都将事半功倍。赞分享后端Web框架【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址https://gitcode.com/gh_mirrors/yi/yii2点击查看免费下载相关推荐Yii 2 组件Component机制完全指南属性、事件与行为的基石Yii 2 组件Component机制完全指南属性、事件与行为的基石 组件Component是 Yii 2 框架应用程序中最主要的构建单元几乎框架中后端Web框架Yii 2 组件Component机制全解属性、事件与行为三大特性及对象生命周期Yii 2 组件Component机制全解属性、事件与行为三大特性及对象生命周期 Yii 2 的整个框架都构建在「组件」这一基石之上——无论是 Appli后端Web框架Yii2 组件Component机制完全指南属性、事件与行为的基石Yii2 组件Component机制完全指南属性、事件与行为的基石 导读 组件Component是 Yii 2 框架应用最基本的构建单元。本篇文章基于后端Web框架上一篇Rogue Mysql Server蜜罐部署指南开启账号密码验证的安全实践下一篇Granian内存管理深度解析jemalloc与mimalloc性能对比创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表