Event php
Yii中的Event由兩部分組成,分別是$events和EventHandler,其中$event表明事件發生時的具體數據,而EventHandler表明事件發生時的具體的處理函數。web
所以,當錯誤發生時,直接調用onError函數就能夠,這時,以前被註冊過的onError這個事件對應的錯誤處理函數都會被執行。具體的ErrorHandler發生過程能夠經過raiseEvent()的源代碼來了解。數組
在Yii中,Event一般是在CComponent的子類中擴展出來的,通常以on開頭,如:app
public function onError($event)yii
{函數
$this->raiseEvent('onError', $event);this
}spa
public function raiseEvent($name,$event) { $name=strtolower($name); if(isset($this->_e[$name])) { foreach($this->_e[$name] as $handler) { if(is_string($handler)) call_user_func($handler,$event); else if(is_callable($handler,true)) { if(is_array($handler)) { // an array: 0 - object, 1 - method name list($object,$method)=$handler; if(is_string($object)) // static method call call_user_func($handler,$event); else if(method_exists($object,$method)) $object->$method($event); else throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".', array('{class}'=>get_class($this), '{event}'=>$name, '{handler}'=>$handler[1]))); } else // PHP 5.3: anonymous function call_user_func($handler,$event); } else throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".', array('{class}'=>get_class($this), '{event}'=>$name, '{handler}'=>gettype($handler)))); // stop further handling if param.handled is set true if(($event instanceof CEvent) && $event->handled) return; } } else if(YII_DEBUG && !$this->hasEvent($name)) throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.', array('{class}'=>get_class($this), '{event}'=>$name))); }
此函數的主要內容就是在CComponent的變量$_e尋找與事件名稱(如本例中的'onError')對應的EventHandler(函數),而後調用該EventHandler,同時將事件發生時的數據做爲參數傳遞給該函數,完成事件的處理。code
那麼既然有EventHandler的調用,那麼確定就會有Event和EventHandler的註冊了,不然就沒有EventHandler可調用。component
說到註冊,天然會想到CComponent類中的__set()函數了,來看一下__set()函數的源碼:
public function __set($name,$value) { $setter='set'.$name; if(method_exists($this,$setter)) return $this->$setter($value); else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name)) { // duplicating getEventHandlers() here for performance $name=strtolower($name); if(!isset($this->_e[$name])) $this->_e[$name]=new CList; return $this->_e[$name]->add($value); } else if(is_array($this->_m)) { foreach($this->_m as $object) { if($object->getEnabled() && (property_exists($object,$name) || $object->canSetProperty($name))) return $object->$name=$value; } } if(method_exists($this,'get'.$name)) throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.', array('{class}'=>get_class($this), '{property}'=>$name))); else throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.', array('{class}'=>get_class($this), '{property}'=>$name))); }
關於Event事件的註冊是從第6行開始,從代碼中能夠看出Event名稱必須以'on'開頭,如onError,onClick等。對應的$value是一個List,便可以有多個EventHandler。
具體的註冊代碼,Yii註釋中也已經給出:
$component->onClick=$callback; // or $component->onClick->add($callback);
總結一下,Event的原理就是首先要定義一個事件處理函數,其次將其註冊到該類的對應的事件中去,最後在發生事件時調用該事件的EventHandler。實例以下,當改變類中的變量name時,打印字符串:
class example extends CComponent { private $name; public function getName() { return isset($name) ? $name : ''; } public function setName($newName) { $this->name = $newName; $this->raiseEvent('onChange', new CEvent()); } } //在某一controller中實現: $exa = new example(); $exa->onChange = array($this, "showChange"); $exa->setName("zhangsan"); public function showChange() { echo "changed"; }
Behavior
Behavior是一種跟EventHandler功能比較相似的另外一種表現形式,有點相似於多重繼承,經過類綁定的方法,component將一個或者多個CBehavior類的成員方法和變量綁定到本身身上。
經過調用CComponet中的attachBehavior方法實現關聯,咱們深刻起源碼看看究竟作了什麼?
public function attachBehavior($name,$behavior) //$name:行爲名稱 $behavior:行爲對象 { if(!($behavior instanceof IBehavior)) $behavior=Yii::createComponent($behavior); $behavior->setEnabled(true); $behavior->attach($this); return $this->_m[$name]=$behavior; }
程序很短,就是判斷傳入的behaviour是否是IBehavior的實例,根據狀況作一下處理,反正最後是經過調用IBehavior的attach方法去掛到當前的CComponent方法。
好了,關鍵的地方來了。注意看第七行,沒有錯,起把傳入的behavior傳入了 $this->_m[$name]變量,這個也就是爲何能夠多綁定的緣由,實現了收集的機制。轉入IBehavior看attach方法定義
public function attach($owner) { $this->_owner=$owner; foreach($this->events() as $event=>$handler) $owner->attachEventHandler($event,array($this,$handler)); }
分析這段代碼,首先能夠看出對於每個Behavior,都只能屬於一個Component.
那麼Event和Behavior又怎麼會有關係了?看到 $owner->attachEventHandler($event,array($this,$handler));這句了嗎?$owner就是咱們綁定behavior的component,用該方法的名稱就能夠很容易的發現這裏實現的Event Handler的綁定功能。
這裏的$this->events()是什麼?
public function events() { return array(); }
怎麼忽然和上面匹配不了了?返回了一個空的array()?千萬別搞錯,這個函數是一個virtual的函數是須要重寫的。你其實這裏就能夠理解爲events()方法返回的是一個含有事件名稱和事件處理方法的關聯數組。
若是還不能理解,咱們看下CModelBehavior的這段的具體實現,他但是繼承自CBehavior的。
public function events() { return array( 'onAfterConstruct'=>'afterConstruct', 'onBeforeValidate'=>'beforeValidate', 'onAfterValidate'=>'afterValidate', ); }
看到了嗎?定義了三個事件,以及相應的事件處理函數,只是這些事件處理函數,在你擴展的時候,須要重寫。
總結一下,behavior其實與event很相似,只不過behavior是將事件分類整理到一塊兒,歸到一個類中,而後一塊兒註冊給component,所以behavior能夠看作是一個事件與事件處理函數的集合。須要注意的是,當一個behavior被attach到一個component時,該behavior的全部函數都能被component調用。具體事例以下:
首先重寫Behavior類:
class exampleBehavior extends CBehavior { //重寫關聯數組 public function events() { return array( 'onBegin' => 'begin'; 'onEnd' => 'end'; 'onClick' => 'click'; ); } //事件處理函數 public function begin() { } public function end() { } public function click() { } } //在某一個controller中實現: $exBehavior = new exampleBehavior(); $component->attachBehavior('example', 'application.behavior.exampleBehavior'); $component->begin();