后端Web框架【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址https://gitcode.com/gh_mirrors/yi/yii2点击查看免费下载导读本文以 Yii2 官方指南《Comportements》行为章节为骨架结合当前仓库framework/base/与framework/behaviors/的真实源码实现系统讲解 Yii2 行为机制——包括行为的定义、事件响应、静态/动态附着、使用与解绑并深入实战TimestampBehavior与其他内置行为。读完本文你将掌握如何在不修改类继承结构的前提下为任意Component组件注入方法与属性、拦截组件事件并能在 ActiveRecord 模型中落地常见横切需求时间戳、作者标记、URL slug 等。什么是行为Behavior行为behavior是 yii\base\Behavior 类或其子类的实例。行为又被称为 mixin 组件的功能。将行为附着到一个组件上会把该行为的方法和属性注入到组件中使其就像在组件类自身定义的一样可以直接访问。更进一步行为还可以响应组件触发的事件从而定制组件代码的正常执行流程。从源码结构看Behavior继承自BaseObject见 framework/base/Behavior.php 第 26 行因此行为本身也是配置型对象支持通过 getter/setter 定义属性。而Component类则是 Yii2 中几乎所有类模型、控制器、应用组件、widget 等的基类行为机制正是建立在Component的魔术方法体系之上——这一点在后面的底层实现原理小节会详细展开。定义行为要定义一个行为你需要创建一个继承自yii\base\Behavior或其子类的类。例如namespace app\components; use yii\base\Behavior; class MyBehavior extends Behavior { public $prop1; private $_prop2; public function getProp2() { return $this-_prop2; } public function setProp2($value) { $this-_prop2 $value; } public function foo() { // ... } }上述代码定义了行为类app\components\MyBehavior它有两个属性prop1、prop2和一个方法foo()。注意prop2是通过 getter 方法getProp2()和 setter 方法setProp2()定义的这是因为yii\base\Behavior继承自yii\base\BaseObject支持通过 getter/setter 定义属性。由于这是一个行为类当它附着到组件上时该组件就获得了prop1、prop2属性以及foo()方法。提示在行为内部可以通过owner属性访问行为所附着到的组件。owner是Behavior基类公开声明的成员变量见 framework/base/Behavior.php 第 31 行由attach()方法在附着时赋值。注意如果行为重写了__get()和/或__set()方法那么你也必须同时重写canGetProperty()和/或canSetProperty()方法否则组件将无法正确探测到行为提供的属性Component正是通过调用这些方法来判断属性归属的见下文原理小节。响应组件事件如果行为需要响应所附着组件触发的事件就必须重写 Behavior::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()方法返回一个事件名 → 事件处理器的映射数组。上面的例子声明当 ActiveRecord 触发EVENT_BEFORE_VALIDATE验证前事件时调用行为的beforeValidate()方法。事件处理器的书写格式有以下三种字符串指向行为类中某个方法的名字如上面的beforeValidate数组由对象/类名与方法名字符串组成不带括号如[$object, methodName]匿名函数闭包function ($event) { ... }。事件处理器的签名应为function ($event) { }其中$event是事件参数对象。更多细节请参考事件章节。从源码角度印证Behavior::attach()在附着时遍历events()返回的映射逐一调用$owner-on($event, ...)注册处理器字符串形式的处理器会被转换为[$this, $handler]数组回调。当行为被解绑时Behavior::detach()会调用$owner-off(...)将这些处理器全部移除见 framework/base/Behavior.php。也就是说事件处理器的生命周期与行为绑定——无需手动清理。附着行为你可以将行为附着到Component组件上方式有两种静态与动态。静态方式更常见。静态附着静态附着需要重写组件类的 behaviors() 方法。该方法返回一个行为配置列表每个行为可以是一个行为类名字符串也可以是一个配置数组namespace app\models; use yii\db\ActiveRecord; use app\components\MyBehavior; class User extends ActiveRecord { public function behaviors() { return [ // 匿名行为只给行为类名 MyBehavior::class, // 命名行为只给行为类名 myBehavior2 MyBehavior::class, // 匿名行为配置数组 [ class MyBehavior::class, prop1 value1, prop2 value2, ], // 命名行为配置数组 myBehavior4 [ class MyBehavior::class, prop1 value1, prop2 value2, ] ]; } }通过在数组键上指定名字可以为行为命名——这样的行为称为命名行为named behavior。上面例子中有两个命名行为myBehavior2和myBehavior4。未指定名字的行为称为匿名行为anonymous behavior。从源码看behaviors()声明在基类Component中默认返回空数组framework/base/Component.phpensureBehaviors()方法第 763-771 行会在首次访问行为相关功能时被自动调用将所有behaviors()中声明的行为批量附着。该方法是惰性的——只有真正用到行为时才实例化避免不必要的开销。动态附着动态附着通过调用组件的 attachBehavior() 方法完成use app\components\MyBehavior; // 附着行为对象 $component-attachBehavior(myBehavior1, new MyBehavior()); // 附着行为类名 $component-attachBehavior(myBehavior2, MyBehavior::class); // 附着配置数组 $component-attachBehavior(myBehavior3, [ class MyBehavior::class, prop1 value1, prop2 value2, ]);一次性附着多个行为使用 attachBehaviors()$component-attachBehaviors([ myBehavior1 new MyBehavior(), // 命名行为 MyBehavior::class, // 匿名行为 ]);从源码看attachBehavior()内部调用私有方法attachBehaviorInternal()framework/base/Component.php如果传入的不是Behavior实例则通过Yii::createObject($behavior)创建行为对象若名字是整数即匿名则直接追加到行为数组若是指定名字则先解绑同名旧行为再附着新行为。因此同名行为重复附着时旧行为会被自动替换。通过配置附着行为也可以通过配置数组附着键名以as前缀开头[ as myBehavior2 MyBehavior::class, as myBehavior3 [ class MyBehavior::class, prop1 value1, prop2 value2, ], ]这种方式非常适合在应用配置、组件配置或Yii::createObject()创建对象时注入行为。源码印证Component::__set()在设置属性时会识别以as开头的键将其余部分作为行为名并支持行为对象、闭包、配置数组、类名字符串四种取值见 framework/base/Component.php。更多细节请参考配置章节中的配置格式。使用行为使用行为前先按上述方式将行为附着到组件。附着后使用方法非常直观可以直接通过组件访问行为定义的公共成员变量或由 getter/setter 定义的属性// prop1 是行为类中定义的属性 echo $component-prop1; $component-prop1 $value;也可以像调用组件自身方法一样调用行为的公共方法// foo() 是行为类中定义的公共方法 $component-foo();可以看到虽然$component自身并没有定义prop1和foo()但由于附着了行为它们可以被当作组件定义的一部分来使用。冲突解决规则如果两个行为定义了同名的属性或方法并且同时附着到同一个组件那么先附着的那个行为在访问时胜出。按名访问行为对象命名行为可以通过名字取回行为对象本身$behavior $component-getBehavior(myBehavior);获取全部行为$behaviors $component-getBehaviors();底层实现原理组件如何看见行为行为注入之所以像魔法一样其实全部由Component的魔术方法承担其查找顺序可以从 framework/base/Component.php 的源码中确认属性读取__get()第 138-159 行先查找组件自身的 getter找不到则调用ensureBehaviors()后遍历行为只要某个行为canGetProperty($name)为真就返回$behavior-$name属性写入__set()第 179-226 行先处理 setter、on前缀事件、as前缀行为再遍历行为canSetProperty($name)并写入方法调用__call()第 308-317 行组件自身不存在该方法时遍历行为若$object-hasMethod($name)则转发调用探测辅助hasProperty()/canGetProperty()/canSetProperty()/hasMethod()同样会递归检查所有已附着行为第 347-442 行这也正是Component系列方法可以透明感知行为存在的原因。这也解释了为什么原文档特别强调若行为重写了__get()/__set()必须同步重写canGetProperty()/canSetProperty()——否则Component无法通过探测方法感知到这些属性魔术转发就会失效。解绑行为解绑单个行为调用 detachBehavior()传入行为名$component-detachBehavior(myBehavior1);解绑全部行为$component-detachBehaviors();源码印证detachBehavior()会从行为数组移除行为并调用其detach()而Behavior::detach()framework/base/Behavior.php会负责移除该行为注册的全部事件处理器并清空owner引用避免内存泄漏。实战TimestampBehavior自动时间戳让我们直接看一个最常用的内置行为 yii\behaviors\TimestampBehavior。它会在 ActiveRecord 通过insert()、update()或save()保存时自动更新模型的 timestamp 属性。首先将行为附着到你想要使用的 ActiveRecord 类上namespace app\models; use yii\db\ActiveRecord; use yii\behaviors\TimestampBehavior; class User extends ActiveRecord { // ... public function behaviors() { return [ [ class TimestampBehavior::class, attributes [ ActiveRecord::EVENT_BEFORE_INSERT [created_at, updated_at], ActiveRecord::EVENT_BEFORE_UPDATE [updated_at], ], // 如果你使用 datetime 而不是 UNIX 时间戳 // value new Expression(NOW()), ], ]; } }上面的行为配置声明记录插入时把当前 UNIX 时间戳赋给created_at和updated_at记录更新时把当前 UNIX 时间戳赋给updated_at。注意上述实现若配合 MySQL 使用需要将created_at、updated_at列声明为int(11)类型才能存放 UNIX 时间戳。配置好后保存User对象时created_at与updated_at会被自动填充$user new User; $user-email testexample.com; $user-save(); echo $user-created_at; // 输出当前时间戳TimestampBehavior还提供实用的 touch() 方法将当前时间戳赋给指定属性并立即保存到数据库$user-touch(login_time);深入 TimestampBehavior 的实现细节从 framework/behaviors/TimestampBehavior.php 源码可确认以下事实默认情况下不配置attributesinit()会自动设置EVENT_BEFORE_INSERT [createdAtAttribute, updatedAtAttribute]与EVENT_BEFORE_UPDATE updatedAtAttribute第 100-110 行即默认属性名是created_at与updated_atcreatedAtAttribute/updatedAtAttribute可分别设为false以关闭创建时间或更新时间记录第 82-87 行value属性为null时getValue()直接返回 PHPtime()的结果第 118-125 行若设置为new Expression(NOW())等Expression对象则使用数据库表达式取值注意使用Expression时保存后模型内的属性存放的是表达式对象而非实际值如需数据库中的真实值需要调用 ActiveRecord 的refresh()方法重新加载touch()内部调用updateAttributes()绕过常规保存流程直接更新数据库并且对尚未保存的新记录会抛出InvalidCallException第 140-142 行自 2.0.6 起。上述行为逻辑在仓库测试中均有覆盖例如 tests/framework/behaviors/TimestampBehaviorTest.phptestNewRecord()验证插入时created_at、updated_at被自动填充第 75-87 行testUpdateRecord()验证更新时created_at不被改写而updated_at被更新第 92-110 行。时间戳字段与验证规则的配合因为时间戳属性由行为自动赋值通常不属于用户输入因此不应出现在模型的rules()验证规则中在源码类注释中也有明确说明见 framework/behaviors/TimestampBehavior.php 第 34-35 行。同理也适用于 BlameableBehavior、SluggableBehavior 等自动填充行为。其他内置行为Yii2 框架自带多个行为位于framework/behaviors/目录yii\behaviors\BlameableBehavior —— 自动将指定属性填充为当前用户 ID。默认在插入时填充created_by与updated_by更新时填充updated_by可通过createdByAttribute/updatedByAttribute改名如author_id/updater_id通过defaultValue指定游客未登录时的默认值。从源码看其取值逻辑为value属性为null时取Yii::$app-user-id见第 106-120 行。yii\behaviors\SluggableBehavior —— 自动将指定属性填充为可在 URL 中使用的纯 ASCII 字符串slug。它依赖php-intl扩展做音译未安装时回退到yii\helpers\Inflector::$transliteration中的替换规则支持slugAttribute默认slug、immutableslug 已生成后不再改变、ensureUnique保证 slug 全局唯一冲突时自动追加后缀、skipOnEmpty源属性为空时跳过生成等配置项。yii\behaviors\AttributeBehavior —— 上述行为Timestamp、Blameable、Sluggable的共同基类。它根据attributes配置把value支持闭包在指定事件发生时赋给一个或多个属性还提供skipUpdateOnClean记录未修改时跳过更新默认开启与preserveNonEmptyValues保留已非空的属性值两个开关。yii\behaviors\AttributesBehavior —— 支持针对不同事件分别指定不同属性-值映射的增强版 AttributeBehavior。yii\behaviors\AttributeTypecastBehavior —— 在模型验证或保存时自动对属性值做类型转换。yii\behaviors\OptimisticLockBehavior —— 配合 ActiveRecord 的乐观锁版本号字段使用。yii\behaviors\CacheableWidgetBehavior —— 为 widget 提供缓存能力。此外还有社区扩展行为可供选用例如yii2tech\ar\softdelete\SoftDeleteBehavior —— 提供软删除与软恢复方法即通过设置标记/状态字段将记录标记为已删除yii2tech\ar\position\PositionBehavior —— 通过整数字段管理记录的排序提供上移/下移等重排方法。行为与 Traits 的对比行为与 PHP traits 相似——都能向宿主类注入属性和方法——但二者在诸多方面存在差异各有优劣更多是互补关系而非替代关系。使用行为的理由支持继承行为类与普通类一样支持继承而 traits 本质是语言层面的复制粘贴不支持继承。可动态装卸行为可以在不改动组件类代码的情况下动态附着/解绑而使用 trait 必须修改使用它的类代码。可配置行为是可配置对象可通过配置数组注入属性trait 不可配置。可响应事件行为可以通过events()响应组件事件从而定制组件的执行流程trait 做不到。命名冲突自动解决多个行为附着到同一组件发生命名冲突时自动以先附着者优先的规则解决而多个 trait 造成的命名冲突必须手动重命名相关属性/方法来解决。使用 Traits 的理由性能更高行为是对象需要额外的 CPU 与内存开销trait 在运行时没有对象成本。IDE 支持更好trait 是语言原生构造主流 IDE 的补全、跳转、重构体验更友好行为的方法/属性来自运行期注入IDE 通常无法静态感知。总结与最佳实践行为机制是 Yii2 组合优于继承思想的核心体现通过Component的魔术方法体系 Behavior::attach()/detach()生命周期管理实现跨类复用、运行期装配、事件拦截三大能力。实际项目中以下场景最适合使用行为横切关注点如自动时间戳TimestampBehavior、审计人BlameableBehavior、URL slugSluggableBehavior——这些逻辑与每次保存都要做强相关用行为附着到模型即可无需污染每个模型类第三方扩展能力为某个组件按需注入额外方法如软删除、排序无需继承或修改组件运行期策略切换同一组件在不同运行场景附着不同行为实现动态行为组合。编写行为时请记住几个要点通过$this-owner访问宿主组件重写__get()/__set()时同步重写canGetProperty()/canSetProperty()自动填充的字段不要放进rules()验证规则善用配置数组与as前缀让行为像组件一样可装配。赞分享后端Web框架【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址https://gitcode.com/gh_mirrors/yi/yii2点击查看免费下载相关推荐Yii2 行为Behavior机制完全指南向组件注入方法属性、响应事件与 TimestampBehavior 实战Yii2 行为Behavior机制完全指南向组件注入方法属性、响应事件与 TimestampBehavior 实战 行为Behavior是 Yii2后端Web框架NSwag终极指南3步轻松在React应用中自动生成TypeScript Fetch客户端NSwag终极指南3步轻松在React应用中自动生成TypeScript Fetch客户端 还在为前后端API对接烦恼吗每次API变更都要手动更新TypeS后端Web框架Yii 2 事件系统完全指南基于 Component 与 Event 的代码注入机制Yii 2 事件系统完全指南基于 Component 与 Event 的代码注入机制 本文以 Yii 2 官方西班牙语指南 concept events.md后端Web框架创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
Cadence Virtuoso稳定性仿真指南:stb分析、相位裕度与Miller补偿实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/23 7:24:45
LLC架构性能优化避坑指南与选型对比 LLC架构性能优化避坑指南与选型对比 昨天还在跑顺手的代码,今天一升级依赖,满屏报错红字,API 接口全变了,心里是不是在滴血?别急,这种“版本地狱”在 llc (Limited Liability… · 2026/9/23 7:24:45
C++ volatile与atomic关键字深度解析与应用实践 1. volatile 关键字深度解析1.1 volatile 的本质与编译器行为volatile 是 C 中最容易被误解的关键字之一。它的核心作用是告诉编译器:"这个变量可能会在你不知道的情况下被改变"。这种改变可能来自硬件设备、其他线程,甚至是信号处理程序。编译… · 2026/9/23 7:24:39
Open CoDesign五大导出格式全解析:HTML、PDF、PPTX、ZIP与Markdown实用指南 Open CoDesign五大导出格式全解析:HTML、PDF、PPTX、ZIP与Markdown实用指南 【免费下载链接】open-codesign Open-source Claude Design alternative. One-click import your Claude Code / Codex API key. Prompt → prototype / slides / PDF. Multi-model (Claud… · 2026/9/23 9:51:21
VNote 的 GUI 服务层架构:src/gui 设计与实现指南 桌面应用知识管理 【免费下载链接】vnote A pleasant note-taking platform in native C. 项目地址: https://gitcode.com/gh_mirrors/vn/vnote 点击查看 免费下载 src/gui/AGENTS.md 是 VNote(原生 C 笔记平台)中 GUI 层服务与工具的设计契… · 2026/9/23 9:51:21
上海帝舵售后服务中心丨门店位置和咨询电话权威查询(2026年最新) 上海帝舵售后服务中心丨门店位置和咨询电话权威查询(2026年最新),现将上海本地直营维保网点完整信息、全渠道正规咨询预约方式、标准化维保服务权益统一公示,为上海全域及长三角周边城市帝舵表主扫清售后查询壁垒。用户可通过帝舵直营售后统一服务电话40… · 2026/9/23 9:51:21
物联网技术工程师培训机构推荐:从报名学习到考试拿证,报考全攻略 万物互联的时代已经到来,从智能家居到工业物联网,物联网技术工程师是连接物理世界与数字世界的工程师。物联网技术人才需求持续增长。本文给你一份完整的物联网技术工程师报考全攻略。
一、物联网技术工程师是做什么的?
物联网技术工程师是从… · 2026/9/23 9:51:21
基于Django的养老社区查询预约系统设计与实现 1. 项目概述与核心价值养老社区查询预约系统是一个典型的B/S架构应用,旨在为老年人及其家属提供便捷的养老服务查询与预约功能。这个毕设项目完整实现了从前端展示到后端业务逻辑的全套解决方案,特别适合计算机相关专业学生作为毕业设计选题。我在实际开… · 2026/9/23 9:51:20
一文搞懂下载华为手机助手 华为手机助手下载避坑指南与源码级速查手册 面对满屏的 java.lang.NullPointerException 和看不懂的 StackTrace,你是不是只想摔键盘?别急,这份 速查手册 能救你的命。… · 2026/9/23 9:51:14
3招搞定手机怎么下载微信面试难题实战项目解析 3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29