Yii源碼解讀-行爲

Yii基礎

行爲(Behavior)

行爲(behavior)能夠在不修改現有類的狀況下,對類的功能進行擴充。 經過將行爲綁定到一個類,可使類具備行爲自己所定義的屬性和方法,就好像類原本就有這些屬性和方法同樣。 並且不須要寫一個新的類去繼承或包含現有類。html

Yii中的行爲,是yii\base\Behavior中的實例,只要將Behavior實例綁定到Component實例上便可。可是,Behavior只能與Component類綁定。數組

若是你寫了一個類,須要用到行爲,那麼必需要繼承自yii\base\Componentapp

使用行爲

Demo:yii

// 定義一個將綁定行爲的類
class MyClass extends yii\base\Component{}

//定義一個行爲類,他將綁定到MyClass上
class MyBehavior extends yii\base\Behavior{
    public $property1 = 'This is property in MyBehavior.';
    
    public function method1(){
        return 'Method in MyBehavior is called.';
    }
}

$myClass = new MyClass();
$myBehavior = new MyBehavior();

// Bind
$myClass->attachBehavior('myBehavior', $myBehavior);

// 訪問
echo $myClass->property1;
echo $myClass->method1();

使用行爲的大體流程:函數

  • yii\base\Component派生本身的類,以便使用行爲。this

  • yii\base\Behavior派生本身的行爲類,定義屬性和方法。spa

  • 將Component和Behavior綁定起來code

  • 像使用Behavior同樣使用Componenthtm

行爲的要素

  • $owner:指向行爲的依附對象對象

  • events():行爲全部要響應的事件

  • attach():將行爲與Component綁定起來

  • detach():你懂的

行爲的依附對象

在行爲的方法中, $this 引用的是行爲自己, 試圖經過 $this 來訪問行爲所依附的Component是行不通的。 正確的方法是經過yii\base\Behavior::$owner來訪問Component。

行爲所要響應的事件

重載yii\base\Behavior::events()方法,表示這個行爲將對類何種事件進行何種反饋。

namespace app\Components;

use yii\db\ActiveRecord;
use yii\base\Behavior;

class MyBehavior extends Behavior{
    // 重載,使得事件觸發時,調用行爲中的一些方法
    public function events(){
        // 在AR的這個事件觸發時,調用成員函數beforeValidate
        return [ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate']
    }
    
    // 行爲的成員函數
    public function beforeValidate($events){...}
}

行爲的綁定和解除

綁定和解除,均須要行爲和Component雙方共同參與才行。

實際操做,Behavior分別使用attach()detach()來實現便可。

定義一個行爲

定義一個行爲,便是準備好注入到現有類的屬性和方法。即要寫一個Behavior的子類

namespace app\Components;
use yii\base\Behavior;

class MyBehavior extends Behavior{
    public $prop1;
    
    private $_prop2;
    private $_prop3;
    private $_prop4;
    
    public function getProp2(){
        return $this->prop2;
    }
    
    public function setProp3($value){
        $this->_prop3 = $value;
    }
    
    public function foo(){}
    
    protected function bar(){}
}

該子類繼承了Behavior,同時間接的繼承了Object。當該類與Component綁定後,Component也就擁有了相對應public的屬性和方法,而private和protected的屬性和方法並不能獲得。

行爲的綁定

有兩種方法能夠將一個Behavior綁定到一個yii\base\Component上。

靜態方法:在代碼沒有跑起來以前

靜態綁定,只須要重載yii\base\Component::bahaviors()就能夠了。該方法描述類所具備的行爲。描述方法:

  • 配置來描述

  • Behavior類名

  • Behavior類的配置數組

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(),
            // 匿名行爲 + 給出配置數組
            [
                'class' => MyBehavior::className(),
                'prop1' => 'v1',
                'prop3' => 'v3',
            ],
            // 帶名稱的行爲 + 配置數組
            'myBehavior4' => [
                'class' => MyBehavior::className(),
                'prop1' => 'v1',
                'prop3' => 'v3'
            ]
        ];
    }
}

另外經過配置文件:

[
    'as myBehavior2' => MyBehavior::className(),
]

動態方法綁定行爲

須要調用yii\base\Component::attachBehaviors()

$Component->attachBehaviors([
    'myBehavior1' => new MyBehavior,
]);
$behavior = $Component->getBehavior('myBehavior1');

綁定的內部原理

  • yii\base\Component::behaviors()

  • yii\base\Component::ensureBehaviors()

  • yii\base\Component::attachBehaviorInternal()

  • yii\base\Behavior::attach()

關於綁定,作個小結:

  • 綁定的動做從Component發起;

  • 靜態綁定經過重載 yii\base\Componet::behaviors() 實現;

  • 動態綁定經過調用 yii\base\Component::attachBehaviors() 實現;

  • 行爲還能夠經過爲 Component 配置 as 配置項進行綁定;

  • 行爲有匿名行爲和命名行爲之分,區別在於綁定時是否給出命名。 命名行爲能夠經過其命名進行標識,從而有針對性地進行解除等操做;

  • 綁定過程當中,後綁定的行爲會取代已經綁定的同名行爲;

  • 綁定的意義有兩點,一是爲行爲設置 $owner 。二是將行爲中擬響應的事件的handler綁定到類中去。

行爲響應的事件實例

綁定和解除的過程,實際上就是將行爲中的事件handler綁定到類中去。行爲用的最多的,也是對於Component各類事件的響應。

行爲的屬性和方法注入原理

__get(), __set(), __call()

行爲與繼承和特性(Traits)的區別

相比較於使用繼承的方式來擴充類功能,使用行爲的方式,一是沒必要對現有類進行修改,二是PHP不支持多繼承,可是Yii能夠綁定多個行爲,從而達到相似多繼承的效果。

傾向於使用行爲的狀況:

  • 行爲從本質上講,也是PHP的類,所以一個行爲能夠繼承自另外一個行爲,從而實現代碼的複用。而特性只是PHP的一種語法,效果上相似於把特性的代碼導入到了類中從而實現代碼的注入,特性是不支持繼承的。

  • 行爲能夠動態地綁定、解除,而沒必要要對類進行修改。可是特性必須在類在使用 use 語句,要解除特性時,則要刪除這個語句。換句話說,須要對類進行修改。

  • 行爲還以在在配置階段進行綁定,特性就不行了。

  • 行爲能夠用於對事件進行反饋,而特性不行。

  • 當出現命名衝突時,行爲會自行排除衝突,自動使用先綁定的行爲。而特性在發生衝突時,須要人爲干預,修改發生衝突的變量名、屬性名、方法名。

傾向於使用特性的狀況:

  • 特性比行爲在效率上要高一點,由於行爲實際上是類的實例,須要時間和空間進行分配。

  • 特性是PHP的語法,所以,IDE的支持要好一些。目前尚未IDE能支持行爲。

參考

  1. http://www.digpage.com/behavior.html

相關文章
相關標籤/搜索