Yii PHP 框架分析(二)

 

  1. Yii PHP 框架分析(二) 
  2. 作者:wdy 
  3.  
  4. http://hi.baidu.com/delphiss/blog/item/54597af595085ad3f3d38552.html 
  5.  
  6. Yii是基於組件(component-based)的 web框架,CComponent類是所有組件的基類。 
  7.  
  8. CComponent類爲子類提供了基於屬性 (property)、事件(event)、行爲(behavior)編程接口。 
  9.  
  10. 組件的屬性(property) 
  11.  
  12. Ccomponent類並沒有提供屬性的變量存儲,需要由子類來提供兩個方法來實現。子類的getPropertyName()方法提供$component->PropertyName的取值操作數據,子類的setPropertyName($val)方法提供$component->PropertyName賦值操作。 
  13.  
  14. $width=$component->textWidth;     // 獲取 textWidth 屬性 
  15.  
  16. 實現方式爲調用子類提供的方法 $width=$component->getTextWidth() 
  17.  
  18. $component->textWidth=$width;     // 設置 textWidth 屬性 
  19.  
  20. 實現方式爲調用子類提供的方法 $component->setTextWidth($width
  21.  
  22. public function getTextWidth() 
  23.     return $this->_textWidth; 
  24.  
  25. public function setTextWidth($value
  26.     $this->_textWidth=$value
  27.  
  28. 組件的屬性值是大小寫不敏感的(類的成員時大小寫敏感的) 
  29.  
  30. 組件的事件(event) 
  31.  
  32. 組件事件是一種特殊的屬性,它可以將事件處理句柄(可以是函數名、類方法或對象方法)註冊(綁定)到一個事件名上,句柄在事件被喚起的時候被自動調用。 
  33. 組件事件存放在CComponent 的$_e[]數組裏,數組的鍵值爲事件的名字,鍵值的數值爲一個Clist對象,Clist是Yii提供的一個隊列容器,Clist的方法add()添加事件的回調handle。 
  34.  
  35. //添加一個全局函數到事件處理 
  36. $component-> onBeginRequest=”logRequest”; 
  37. //添加一個類靜態方法到事件處理 
  38. $component-> onBeginRequest=array(“CLog”,” logRequest”); 
  39. //添加一個對象方法到事件處理 
  40. $component-> onBeginRequest=array($mylog,” logRequest”); 
  41.  
  42. 喚起事件: 
  43. $component ->raiseEvent('onBeginRequest '$event); 
  44. 會自動調用: 
  45. logRequest($event), Clog:: logRequest($event)和$mylog.logRequest($event
  46.  
  47. 事件句柄必須按照如下來定義 : 
  48. function methodName($event
  49.     ...... 
  50. $event 參數是 CEvent 或其子類的實例,它至少包含了"是誰掛起了這個事件"的信息。 
  51.  
  52. 事件的名字以”on”開頭,在__get()和 __set()裏可以通過這個來區別屬性和事件。 
  53.  
  54. 組件行爲(behavior) 
  55.  
  56. 組件的行爲是一種不通過繼承而擴展組件功能的方法(參見設計模式裏的策略模式)。 
  57.  
  58. 行爲類必須實現 IBehavior 接口,大多數行爲可以從 CBehavior 基類擴展而來。 
  59.  
  60. IBehavior接口提供了4個方法。 
  61. attach($component)將自身關聯到組件,detach($component) 解除$component關聯,getEnabled()和setEnabled()設置行爲對象的有效性。 
  62.  
  63. 行爲對象存放在組件的$_m[]數組裏,數組鍵值爲行爲名字符串,數組值爲行爲類對象。 
  64.  
  65. 組件通過attachBehavior ($name,$behavior)來擴展一個行爲: 
  66. $component-> attachBehavior (‘render’,$htmlRender
  67. $component添加了一個名字爲render的行爲,$htmlRender 需是一個實現 IBehavior 接口的對象,或是一個數組: 
  68. array'class'=>'path.to.BehaviorClass'
  69.    'property1'=>'value1'
  70.    'property2'=>'value2'
  71.    * ) 
  72. 會根據數組的class來創建行爲對象並設置屬性值。 
  73.  
  74. $htmlRender被存儲到$_m[‘render’]中。 
  75.  
  76. 外部調用一個組件未定義的方法時,魔術方法__call() 會遍歷所有行爲對象,如果找到同名方法就調用之。 
  77.  
  78.   
  79.  
  80. 例如 $htmlRender 有個方法 renderFromFile(),則可以直接當做組件的方法來訪問: 
  81.  
  82. $component-> renderFromFile () 
  83.  
  84.   
  85.  
  86. CComponent源碼分析 
  87.  
  88. //所有部件的基類 
  89. class CComponent 
  90. private $_e
  91. private $_m
  92.  
  93. //獲取部件屬性、事件和行爲的magic method 
  94. public function __get($name
  95.    $getter='get'.$name
  96.    //是否存在屬性的get方法 
  97.    if(method_exists($this,$getter)) 
  98.     return $this->$getter(); 
  99.    //以on開頭,獲取事件處理句柄 
  100.    else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name)) 
  101.    { 
  102.     // 事件名小寫 
  103.     $name=strtolower($name); 
  104.     // 如果_e[$name] 不存在,返回一個空的CList事件句柄隊列對象 
  105.     if(!isset($this->_e[$name])) 
  106.      $this->_e[$name]=new CList; 
  107.     // 返回_e[$name]裏存放的句柄隊列對象 
  108.     return $this->_e[$name]; 
  109.    } 
  110.    // _m[$name] 裏存放着行爲對象則返回 
  111.    else if(isset($this->_m[$name])) 
  112.     return $this->_m[$name]; 
  113.    else 
  114.     throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.'
  115.      array('{class}'=>get_class($this), '{property}'=>$name))); 
  116.  
  117. /** 
  118. * PHP magic method 
  119. * 設置組件的屬性和事件 
  120. */ 
  121. public function __set($name,$value
  122.    $setter='set'.$name
  123.    //是否存在屬性的set方法 
  124.    if(method_exists($this,$setter)) 
  125.     $this->$setter($value); 
  126.    //name以on開頭,這是事件處理句柄 
  127.    else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name)) 
  128.    { 
  129.     // 事件名小寫 
  130.     $name=strtolower($name); 
  131.     // _e[$name] 不存在則創建一個CList對象 
  132.     if(!isset($this->_e[$name])) 
  133.      $this->_e[$name]=new CList; 
  134.     // 添加事件處理句柄 
  135.     $this->_e[$name]->add($value); 
  136.    } 
  137.    // 屬性沒有set方法,只有get方法,爲只讀屬性,拋出異常 
  138.    else if(method_exists($this,'get'.$name)) 
  139.     throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.'
  140.      array('{class}'=>get_class($this), '{property}'=>$name))); 
  141.    else 
  142.     throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.'
  143.      array('{class}'=>get_class($this), '{property}'=>$name))); 
  144.  
  145. /** 
  146. * PHP magic method 
  147. * 爲isset()函數提供是否存在屬性和事件處理句柄的判斷 
  148. */ 
  149. public function __isset($name
  150.    $getter='get'.$name
  151.    if(method_exists($this,$getter)) 
  152.     return $this->$getter()!==null; 
  153.    else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name)) 
  154.    { 
  155.     $name=strtolower($name); 
  156.     return isset($this->_e[$name]) && $this->_e[$name]->getCount(); 
  157.    } 
  158.    else 
  159.     return false; 
  160.  
  161. /** 
  162. * PHP magic method 
  163. * 設置屬性值爲空或刪除事件名字對應的處理句柄 
  164. */ 
  165. public function __unset($name
  166.    $setter='set'.$name
  167.    if(method_exists($this,$setter)) 
  168.     $this->$setter(null); 
  169.    else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name)) 
  170.     unset($this->_e[strtolower($name)]); 
  171.    else if(method_exists($this,'get'.$name)) 
  172.     throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.'
  173.      array('{class}'=>get_class($this), '{property}'=>$name))); 
  174.  
  175.   
  176.  
  177. /** 
  178. * PHP magic method 
  179. * CComponent未定義的類方法,尋找行爲類裏的同名方法,實現行爲方法的調用 
  180. */ 
  181. public function __call($name,$parameters
  182.    // 行爲類存放的$_m數組不空 
  183.    if($this->_m!==null) 
  184.    { 
  185.     // 循環取出$_m數組裏存放的行爲類 
  186.     foreach($this->_m as $object
  187.     { 
  188.      // 行爲類對象有效,並且方法存在,調用之 
  189.      if($object->enabled && method_exists($object,$name)) 
  190.       return call_user_func_array(array($object,$name),$parameters); 
  191.     } 
  192.    } 
  193.    throw new CException(Yii::t('yii','{class} does not have a method named "{name}".'
  194.     array('{class}'=>get_class($this), '{name}'=>$name))); 
  195.  
  196. /** 
  197. * 根據行爲名返回行爲類對象 
  198. */ 
  199. public function asa($behavior
  200.    return isset($this->_m[$behavior]) ? $this->_m[$behavior] : null; 
  201.  
  202. /** 
  203. * Attaches a list of behaviors to the component. 
  204. * Each behavior is indexed by its name and should be an instance of 
  205. * {@link IBehavior}, a string specifying the behavior class, or an 
  206. * array of the following structure: 
  207. * <pre> 
  208. * array( 
  209. *     'class'=>'path.to.BehaviorClass', 
  210. *     'property1'=>'value1', 
  211. *     'property2'=>'value2', 
  212. * ) 
  213. * </pre> 
  214. * @param array list of behaviors to be attached to the component 
  215. * @since 1.0.2 
  216. */ 
  217. public function attachBehaviors($behaviors
  218.    // $behaviors爲數組 $name=>$behavior 
  219.    foreach($behaviors as $name=>$behavior
  220.     $this->attachBehavior($name,$behavior); 
  221.  
  222.  
  223. /** 
  224. * 添加一個行爲到組件 
  225. */ 
  226. public function attachBehavior($name,$behavior
  227.    /* $behavior不是IBehavior接口的實例,則爲 
  228.    * array( 
  229.    *     'class'=>'path.to.BehaviorClass', 
  230.    *     'property1'=>'value1', 
  231.    *     'property2'=>'value2', 
  232.    * ) 
  233.    * 傳遞給Yii::createComponent創建行爲了並初始化對象屬性 
  234.    */ 
  235.    if(!($behavior instanceof IBehavior)) 
  236.     $behavior=Yii::createComponent($behavior); 
  237.    $behavior->setEnabled(true); 
  238.    $behavior->attach($this); 
  239.    return $this->_m[$name]=$behavior
  240.  
  241. /** 
  242. * Raises an event. 
  243. * This method represents the happening of an event. It invokes 
  244. * all attached handlers for the event. 
  245. * @param string the event name 
  246. * @param CEvent the event parameter 
  247. * @throws CException if the event is undefined or an event handler is invalid. 
  248. */ 
  249. public function raiseEvent($name,$event
  250.    $name=strtolower($name); 
  251.    // _e[$name] 事件處理句柄隊列存在 
  252.    if(isset($this->_e[$name])) 
  253.    { 
  254.      // 循環取出事件處理句柄 
  255.     foreach($this->_e[$nameas $handler
  256.     { 
  257.      // 事件處理句柄爲全局函數 
  258.      if(is_string($handler)) 
  259.       call_user_func($handler,$event); 
  260.      else if(is_callable($handler,true)) 
  261.      { 
  262.       // an array: 0 - object, 1 - method name 
  263.       list($object,$method)=$handler
  264.       if(is_string($object)) // 靜態類方法 
  265.        call_user_func($handler,$event); 
  266.       else if(method_exists($object,$method)) 
  267.        $object->$method($event); 
  268.       else 
  269.        throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".'
  270.         array('{class}'=>get_class($this), '{event}'=>$name'{handler}'=>$handler[1]))); 
  271.      } 
  272.      else 
  273.       throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".'
  274.        array('{class}'=>get_class($this), '{event}'=>$name'{handler}'=>gettype($handler)))); 
  275.     // $event 的handled 設置爲true後停止隊列裏剩餘句柄的調用 
  276.     if(($event instanceof CEvent) && $event->handled) 
  277.       return
  278.     } 
  279.    } 
  280.    else if(YII_DEBUG && !$this->hasEvent($name)) 
  281.     throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.'
  282.      array('{class}'=>get_class($this), '{event}'=>$name))); 

 

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