初學YII框架的心得(二)

Yii是基於組件(component-based)的web框架,CComponent類是所有組件的基類。

  CComponent類爲子類提供了基於屬性(property)、事件(event)、行爲(behavior)編程接口。

  組件的屬性(property)

  Ccomponent類並沒有提供屬性的變量存儲,需要由子類來提供兩個方法來實現。子類的getPropertyName()方法提供$component->PropertyName的取值操作數據,子類的setPropertyName($val)方法提供$component->PropertyName賦值操作。

  $width=$component->textWidth;     // 獲取 textWidth 屬性

  實現方式爲調用子類提供的方法 $width=$component->getTextWidth()

  $component->textWidth=$width;     // 設置 textWidth 屬性

  實現方式爲調用子類提供的方法 $component->setTextWidth($width)

public function getTextWidth()
{
    return $this->_textWidth;
}
 
public function setTextWidth($value)
{
    $this->_textWidth=$value;
}

組件的屬性值是大小寫不敏感的(類的成員時大小寫敏感的)

  組件的事件(event)

  組件事件是一種特殊的屬性,它可以將事件處理句柄(可以是函數名、類方法或對象方法)註冊(綁定)到一個事件名上,句柄在事件被喚起的時候被自動調用。

  組件事件存放在CComponent 的$_e[]數組裏,數組的鍵值爲事件的名字,鍵值的數值爲一個Clist對象,Clist是Yii提供的一個隊列容器,Clist的方法add()添加事件的回調handle。

//添加一個全局函數到事件處理
$component-> onBeginRequest=”logRequest”;
//添加一個類靜態方法到事件處理
$component-> onBeginRequest=array(“CLog”,” logRequest”);
//添加一個對象方法到事件處理
$component-> onBeginRequest=array($mylog,” logRequest”);

喚起事件:

  $component ->raiseEvent('onBeginRequest ', $event);

  會自動調用:

  logRequest($event), Clog:: logRequest($event)和$mylog.logRequest($event)

  事件句柄必須按照如下來定義 :

function methodName($event)
{
    ......
}

$event 參數是 CEvent 或其子類的實例,它至少包含了"是誰掛起了這個事件"的信息。

  事件的名字以”on”開頭,在__get()和__set()裏可以通過這個來區別屬性和事件。

  組件行爲(behavior)

  組件的行爲是一種不通過繼承而擴展組件功能的方法(參見設計模式裏的策略模式)。

  行爲類必須實現 IBehavior 接口,大多數行爲可以從 CBehavior 基類擴展而來。

  IBehavior接口提供了4個方法。

  attach($component)將自身關聯到組件,detach($component) 解除$component關聯,getEnabled()和setEnabled()設置行爲對象的有效性。

  行爲對象存放在組件的$_m[]數組裏,數組鍵值爲行爲名字符串,數組值爲行爲類對象。

  組件通過attachBehavior ($name,$behavior)來擴展一個行爲:

  $component-> attachBehavior (‘render’,$htmlRender)

  爲$component添加了一個名字爲render的行爲,$htmlRender 需是一個實現 IBehavior 接口的對象,或是一個數組:

array( 'class'=>'path.to.BehaviorClass',
   'property1'=>'value1',
   'property2'=>'value2',
   * )

會根據數組的class來創建行爲對象並設置屬性值。

  $htmlRender被存儲到$_m[‘render’]中。

  外部調用一個組件未定義的方法時,魔術方法__call() 會遍歷所有行爲對象,如果找到同名方法就調用之。

  例如 $htmlRender 有個方法 renderFromFile(),則可以直接當做組件的方法來訪問:

  $component-> renderFromFile ()

  CComponent源碼分析

//所有部件的基類
class CComponent
{
private $_e;
private $_m;
 
//獲取部件屬性、事件和行爲的magic method
public function __get($name)
{
   $getter='get'.$name;
   //是否存在屬性的get方法
   if(method_exists($this,$getter))
    return $this->$getter();
   //以on開頭,獲取事件處理句柄
   else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
   {
    // 事件名小寫
    $name=strtolower($name);
    // 如果_e[$name] 不存在,返回一個空的CList事件句柄隊列對象
    if(!isset($this->_e[$name]))
     $this->_e[$name]=new CList;
    // 返回_e[$name]裏存放的句柄隊列對象
    return $this->_e[$name];
   }
   // _m[$name] 裏存放着行爲對象則返回
   else if(isset($this->_m[$name]))
    return $this->_m[$name];
   else
    throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',
     array('{class}'=>get_class($this), '{property}'=>$name)));
}
 
/**
* PHP magic method
* 設置組件的屬性和事件
*/
public function __set($name,$value)
{
   $setter='set'.$name;
   //是否存在屬性的set方法
   if(method_exists($this,$setter))
    $this->$setter($value);
   //name以on開頭,這是事件處理句柄
   else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
   {
    // 事件名小寫
    $name=strtolower($name);
    // _e[$name] 不存在則創建一個CList對象
    if(!isset($this->_e[$name]))
     $this->_e[$name]=new CList;
    // 添加事件處理句柄
    $this->_e[$name]->add($value);
   }
   // 屬性沒有set方法,只有get方法,爲只讀屬性,拋出異常
   else 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)));
}
 
/**
* PHP magic method
* 爲isset()函數提供是否存在屬性和事件處理句柄的判斷
*/
public function __isset($name)
{
   $getter='get'.$name;
   if(method_exists($this,$getter))
    return $this->$getter()!==null;
   else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
   {
    $name=strtolower($name);
    return isset($this->_e[$name]) && $this->_e[$name]->getCount();
   }
   else
    return false;
}
 
/**
* PHP magic method
* 設置屬性值爲空或刪除事件名字對應的處理句柄
*/
public function __unset($name)
{
   $setter='set'.$name;
   if(method_exists($this,$setter))
    $this->$setter(null);
   else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
    unset($this->_e[strtolower($name)]);
   else 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)));
}
 
 
 
/**
* PHP magic method
* CComponent未定義的類方法,尋找行爲類裏的同名方法,實現行爲方法的調用
*/
public function __call($name,$parameters)
{
   // 行爲類存放的$_m數組不空
   if($this->_m!==null)
   {
    // 循環取出$_m數組裏存放的行爲類
    foreach($this->_m as $object)
    {
     // 行爲類對象有效,並且方法存在,調用之
     if($object->enabled && method_exists($object,$name))
      return call_user_func_array(array($object,$name),$parameters);
    }
   }
   throw new CException(Yii::t('yii','{class} does not have a method named "{name}".',
    array('{class}'=>get_class($this), '{name}'=>$name)));
}
 
/**
* 根據行爲名返回行爲類對象
*/
public function asa($behavior)
{
   return isset($this->_m[$behavior]) ? $this->_m[$behavior] : null;
}
 
/**
* Attaches a list of behaviors to the component.
* Each behavior is indexed by its name and should be an instance of
* {@link IBehavior}, a string specifying the behavior class, or an
* array of the following structure:
* <pre>
* array(
*     'class'=>'path.to.BehaviorClass',
*     'property1'=>'value1',
*     'property2'=>'value2',
* )
* </pre>
* @param array list of behaviors to be attached to the component
* @since 1.0.2
*/
public function attachBehaviors($behaviors)
{
   // $behaviors爲數組 $name=>$behavior
   foreach($behaviors as $name=>$behavior)
    $this->attachBehavior($name,$behavior);
}
 
 
/**
* 添加一個行爲到組件
*/
public function attachBehavior($name,$behavior)
{
   /* $behavior不是IBehavior接口的實例,則爲
   * array(
   *     'class'=>'path.to.BehaviorClass',
   *     'property1'=>'value1',
   *     'property2'=>'value2',
   * )
   * 傳遞給Yii::createComponent創建行爲了並初始化對象屬性
   */
   if(!($behavior instanceof IBehavior))
    $behavior=Yii::createComponent($behavior);
   $behavior->setEnabled(true);
   $behavior->attach($this);
   return $this->_m[$name]=$behavior;
}
 
/**
* Raises an event.
* This method represents the happening of an event. It invokes
* all attached handlers for the event.
* @param string the event name
* @param CEvent the event parameter
* @throws CException if the event is undefined or an event handler is invalid.
*/
public function raiseEvent($name,$event)
{
   $name=strtolower($name);
   // _e[$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))
     {
      // an array: 0 - object, 1 - method name
      list($object,$method)=$handler;
      if(is_string($object)) // 靜態類方法
       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
      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))));
    // $event 的handled 設置爲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)));
}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章