Define behavior
To define behavior, create a class by inheriting yiibaseBehavior or its subclasses. Such as:
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() { // ... } }
The above code defines the behavior class appcomponentsMyBehavior and provides two properties prop1, prop2 and a method foo() for the component to which the behavior is to be attached. Note that the property prop2 is defined through the getter getProp2() and the setter setProp2(). It can be used in this way because yiibaseObject is the ancestor class of yiibaseBehavior. This ancestor class supports defining properties using getter and setter methods
Tip: Inside the behavior, you can access the component to which the behavior has been attached through the yiibaseBehavior::owner attribute.
Static method binding behavior
To bind behaviors statically, you only need to overload yiibaseComponent::behaviors(). This method is used to describe the behavior of the class. How to describe it? Use configuration to describe, which can be the Behavior class name or the configuration array of the Behavior class:
namespace app\models; use yii\db\ActiveRecord; use app\Components\MyBehavior; class User extends ActiveRecord { public function behaviors() { return [ // 匿名的行为,仅直接给出行为的类名称 MyBehavior::className(), // 名为myBehavior2的行为,也是仅给出行为的类名称 'myBehavior2' => MyBehavior::className(), // 匿名行为,给出了MyBehavior类的配置数组 [ 'class' => MyBehavior::className(), 'prop1' => 'value1', 'prop3' => 'value3', ], // 名为myBehavior4的行为,也是给出了MyBehavior类的配置数组 'myBehavior4' => [ 'class' => MyBehavior::className(), 'prop1' => 'value1', 'prop3' => 'value3', ] ]; } }
There is also a static binding method, which is to bind through the configuration file:
[ 'as myBehavior2' => MyBehavior::className(), 'as myBehavior3' => [ 'class' => MyBehavior::className(), 'prop1' => 'value1', 'prop3' => 'value3', ], ]
Dynamic method binding behavior
Dynamic binding behavior needs to call yiibaseComponent::attachBehaviors():
$Component->attachBehaviors([ 'myBehavior1' => new MyBehavior, // 这是一个命名行为 MyBehavior::className(), // 这是一个匿名行为 ]);
This method accepts an array parameter, and the meaning of the parameter is the same as the static binding behavior above.
In the above examples, the key of the array is used as the name of the behavior, and the behavior that does not provide a key name is an anonymous behavior.
For named behaviors, you can call yiibaseComponent::getBehavior() to get the bound behavior:
$behavior = $Component->getBehavior('myBehavior2');
For anonymous actions, there is no way to directly quote them. However, you can get all the bound behaviors:
$behaviors = $Component->getBehaviors();
Internal principles of binding
Just overloading a yiibaseComponent::behaviors() can you use behaviors so magically? This is just the tip of the iceberg. It is actually related to the binding process. The relevant aspects are:
yii\base\Component::behaviors() yii\base\Component::ensureBehaviors() yii\base\Component::attachBehaviorInternal() yii\base\Behavior::attach()
Behavior only accounts for one of the four methods, and more code is completed in Component.
yiibaseComponent::behaviors() As mentioned above when talking about static method binding behavior, it returns an array to describe the behavior. What about yiibaseComponent::ensuerBehaviors()?
This method will call __get() __set() __isset() __unset() __call() canGetProperty() hasMethod() hasEventHandlers() on() off() etc. when used in many places in the Component. See this Is it a headache? It's not complicated at all. In a word, this function will be called as long as it involves the attributes, methods, and events of the class.
Who are the ensureBehaviors() that are needed by so many mortals? As the name suggests, his role is to "ensure". In fact, it is just to ensure that the behaviors described in behaviors() have been bound:
public function ensureBehaviors() { // 为null表示尚未绑定 // 多说一句,为空数组表示没有绑定任何行为 if ($this->_behaviors === null) { $this->_behaviors = []; // 遍历 $this->behaviors() 返回的数组,并绑定 foreach ($this->behaviors() as $name => $behavior) { $this->attachBehaviorInternal($name, $behavior); } } }
This method is mainly used for subclasses. yiibaseComponent does not have any pre-injection behavior, so this call is useless. But for subclasses, you may overload yiibaseComponent::behaviros() to pre-inject some behavior. Then, this function will inject these behaviors first.
From the above code, you can naturally see the third thing to talk about next, yiibaseComponentattachBehaviorInternal():
private function attachBehaviorInternal($name, $behavior) { // 不是 Behavior 实例,说是只是类名、配置数组,那么就创建出来吧 if (!($behavior instanceof Behavior)) { $behavior = Yii::createObject($behavior); } // 匿名行为 if (is_int($name)) { $behavior->attach($this); $this->_behaviors[] = $behavior; // 命名行为 } else { // 已经有一个同名的行为,要先解除,再将新的行为绑定上去。 if (isset($this->_behaviors[$name])) { $this->_behaviors[$name]->detach(); } $behavior->attach($this); $this->_behaviors[$name] = $behavior; } return $behavior; }
The first thing to note is that this is a private member. In fact, in Yii, all methods with the suffix *Internal are private. This method does the following things:
If the $behavior parameter is not a Behavior instance, use it as a parameter and use Yii::createObject() to create it.
If you bind a behavior as an anonymous behavior, attach the behavior directly to this class.
If it is a named behavior, first check if there is a behavior with the same name already bound to this class. If so, replace the previous behavior with the later behavior.
In yiibaseComponent::attachBehaviorInternal(), yiibaseBehavior::attach() is called with $this as the parameter. Thus, the last thing related to binding is introduced, yiibaseBehavior::attach(), which is what we didn’t finish talking about when we talked about the elements of behavior earlier. Let’s take a look at the code first:
public function attach($owner) { $this->owner = $owner; foreach ($this->events() as $event => $handler) { $owner->on($event, is_string($handler) ? [$this, $handler] : $handler); } }
The above code does two things:
Summary
Having said so much, let me make a summary about binding: