多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
Yii PHP 框架分析(二) 2009-10-03 00:12 <table style="TABLE-LAYOUT: fixed; WIDTH: 100%"><tbody><tr><td><div class="cnt" id="blog_text"> <p><strong>Yii PHP 框架分析(二)<br/> 作者:wdy</strong></p> <p><strong><a href="http://hi.baidu.com/delphiss/blog/item/54597af595085ad3f3d38552.html">http://hi.baidu.com/delphiss/blog/item/54597af595085ad3f3d38552.html</a></strong></p> <p>Yii是基于组件(component-based)的web框架,CComponent类是所有组件的基类。</p> <p>CComponent类为子类提供了基于属性(property)、事件(event)、行为(behavior)编程接口。</p> <p><strong>组件的属性(property)</strong></p> <p>Ccomponent类并没有提供属性的变量存储,需要由子类来提供两个方法来实现。子类的getPropertyName()方法提供$component-&gt;PropertyName的取值操作数据,子类的setPropertyName($val)方法提供$component-&gt;PropertyName赋值操作。</p> <p>$width=$component-&gt;textWidth;     // 获取 textWidth 属性</p> <p>实现方式为调用子类提供的方法 $width=$component-&gt;getTextWidth()</p> <p>$component-&gt;textWidth=$width;     // 设置 textWidth 属性</p> <p>实现方式为调用子类提供的方法 $component-&gt;setTextWidth($width)</p> <p>public function getTextWidth()<br/> {<br/>     return $this-&gt;_textWidth;<br/> }<br/><br/> public function setTextWidth($value)<br/> {<br/>     $this-&gt;_textWidth=$value;<br/> }</p> <p>组件的属性值是大小写不敏感的(类的成员时大小写敏感的)</p> <p><strong>组件的事件(event)</strong></p> <p>组件事件是一种特殊的属性,它可以将事件处理句柄(可以是函数名、类方法或对象方法)注册(绑定)到一个事件名上,句柄在事件被唤起的时候被自动调用。<br/> 组件事件存放在CComponent 的$_e[]数组里,数组的键值为事件的名字,键值的数值为一个Clist对象,Clist是Yii提供的一个队列容器,Clist的方法add()添加事件的回调handle。</p> <p>//添加一个全局函数到事件处理<br/> $component-&gt; onBeginRequest=”logRequest”;<br/> //添加一个类静态方法到事件处理<br/> $component-&gt; onBeginRequest=array(“CLog”,” logRequest”);<br/> //添加一个对象方法到事件处理<br/> $component-&gt; onBeginRequest=array($mylog,” logRequest”);</p> <p>唤起事件:<br/> $component -&gt;raiseEvent('onBeginRequest ', $event);<br/> 会自动调用:<br/> logRequest($event), Clog:: logRequest($event)和$mylog.logRequest($event)</p> <p>事件句柄必须按照如下来定义 :<br/> function methodName($event)<br/> {<br/>     ......<br/> }<br/> $event 参数是 CEvent 或其子类的实例,它至少包含了"是谁挂起了这个事件"的信息。</p> <p>事件的名字以”on”开头,在__get()和__set()里可以通过这个来区别属性和事件。</p> <p><strong>组件行为(behavior)</strong></p> <p>组件的行为是一种不通过继承而扩展组件功能的方法(参见设计模式里的策略模式)。</p> <p>行为类必须实现 IBehavior 接口,大多数行为可以从 CBehavior 基类扩展而来。</p> <p>IBehavior接口提供了4个方法。<br/> attach($component)将自身关联到组件,detach($component) 解除$component关联,getEnabled()和setEnabled()设置行为对象的有效性。</p> <p>行为对象存放在组件的$_m[]数组里,数组键值为行为名字符串,数组值为行为类对象。</p> <p>组件通过attachBehavior ($name,$behavior)来扩展一个行为:<br/> $component-&gt; attachBehavior (‘render’,$htmlRender)<br/> 为$component添加了一个名字为render的行为,$htmlRender 需是一个实现 IBehavior 接口的对象,或是一个数组:<br/> array( 'class'=&gt;'path.to.BehaviorClass',<br/>    'property1'=&gt;'value1',<br/>    'property2'=&gt;'value2',<br/>    * )<br/> 会根据数组的class来创建行为对象并设置属性值。</p> <p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align="left"><span style="FONT-SIZE: 10pt; mso-hansi-: 0pt">$htmlRender</span><span style="FONT-SIZE: 10pt; mso-hansi-: 0pt">被存储到</span><span style="FONT-SIZE: 10pt; mso-hansi-: 0pt">$_m[</span><span style="FONT-SIZE: 10pt; mso-hansi-: 0pt"><font face="Calibri">‘</font></span><span style="FONT-SIZE: 10pt; mso-hansi-: 0pt">render</span><span style="FONT-SIZE: 10pt; mso-hansi-: 0pt"><font face="Calibri">’</font></span><span style="FONT-SIZE: 10pt; mso-hansi-: 0pt">]</span><span style="FONT-SIZE: 10pt; mso-hansi-: 0pt">中</span><span style="FONT-SIZE: 10pt; mso-hansi-: 0pt">。<span/></span></p> <p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align="left"><span style="FONT-SIZE: 10pt; mso-hansi-: 0pt">外部调用一个组件未定义的方法时,魔术方法</span><span style="FONT-SIZE: 10pt; mso-hansi-: 0pt">__call() </span><span style="FONT-SIZE: 10pt; mso-hansi-: 0pt">会遍历所有行为对象,如果找到同名方法就调用之。<span/></span></p> <p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align="left"><span style="FONT-SIZE: 10pt; mso-font-kerning: 0pt"/></p> <p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align="left"><span style="FONT-SIZE: 10pt; mso-hansi-: 0pt">例如 </span><span style="FONT-SIZE: 10pt; mso-hansi-: 0pt">$htmlRender </span><span style="FONT-SIZE: 10pt; mso-hansi-: 0pt">有个方法<span> renderFromFile()</span>,</span><span style="FONT-SIZE: 10pt; mso-hansi-: 0pt">则可以直接当做组件的方法来访问:</span></p> <p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none" align="left"><span style="FONT-SIZE: 10pt; mso-hansi-: 0pt">$component-&gt; renderFromFile ()</span><span style="FONT-SIZE: 10pt; mso-font-kerning: 0pt"/></p> <p/> <p><strong>CComponent源码分析</strong></p> <p>//所有部件的基类<br/> class CComponent<br/> {<br/> private $_e;<br/> private $_m;</p> <p><font color="#38761d">//获取部件属性、事件和行为的magic method</font><br/> public function __get($name)<br/> {<br/>    $getter='get'.$name;<br/><font color="#38761d">   //是否存在属性的get方法</font><br/>    if(method_exists($this,$getter))<br/>     return $this-&gt;$getter();<br/><font color="#38761d">   //以on开头,获取事件处理句柄</font><br/>    else if(strncasecmp($name,'on',2)===0 &amp;&amp; method_exists($this,$name))<br/>    {<br/><font color="#38761d">    // 事件名小写</font><br/>     $name=strtolower($name);<br/><font color="#38761d">    // 如果_e[$name] 不存在,返回一个空的CList事件句柄队列对象</font><br/>     if(!isset($this-&gt;_e[$name]))<br/>      $this-&gt;_e[$name]=new CList;<br/><font color="#38761d">    // 返回_e[$name]里存放的句柄队列对象</font><br/>     return $this-&gt;_e[$name];<br/>    }<br/><font color="#38761d">   // _m[$name] 里存放着行为对象则返回</font><br/>    else if(isset($this-&gt;_m[$name]))<br/>     return $this-&gt;_m[$name];<br/>    else<br/>     throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',<br/>      array('{class}'=&gt;get_class($this), '{property}'=&gt;$name)));<br/> }</p> <p><font color="#38761d">/**<br/> * PHP magic method<br/> * 设置组件的属性和事件<br/> */</font><br/> public function __set($name,$value)<br/> {<br/>    $setter='set'.$name;<br/><font color="#38761d">   //是否存在属性的set方法</font><br/>    if(method_exists($this,$setter))<br/>     $this-&gt;$setter($value);<br/><font color="#38761d">   //name以on开头,这是事件处理句柄</font><br/>    else if(strncasecmp($name,'on',2)===0 &amp;&amp; method_exists($this,$name))<br/>    {<br/><font color="#38761d">    // 事件名小写</font><br/>     $name=strtolower($name);<br/><font color="#38761d">    // _e[$name] 不存在则创建一个CList对象</font><br/>     if(!isset($this-&gt;_e[$name]))<br/>      $this-&gt;_e[$name]=new CList;<br/><font color="#38761d">    // 添加事件处理句柄</font><br/>     $this-&gt;_e[$name]-&gt;add($value);<br/>    }<br/><font color="#38761d">   // 属性没有set方法,只有get方法,为只读属性,抛出异常</font><br/>    else if(method_exists($this,'get'.$name))<br/>     throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.',<br/>      array('{class}'=&gt;get_class($this), '{property}'=&gt;$name)));<br/>    else<br/>     throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',<br/>      array('{class}'=&gt;get_class($this), '{property}'=&gt;$name)));<br/> }</p> <p><font color="#38761d">/**<br/> * PHP magic method<br/> * 为isset()函数提供是否存在属性和事件处理句柄的判断<br/> */</font><br/> public function __isset($name)<br/> {<br/>    $getter='get'.$name;<br/>    if(method_exists($this,$getter))<br/>     return $this-&gt;$getter()!==null;<br/>    else if(strncasecmp($name,'on',2)===0 &amp;&amp; method_exists($this,$name))<br/>    {<br/>     $name=strtolower($name);<br/>     return isset($this-&gt;_e[$name]) &amp;&amp; $this-&gt;_e[$name]-&gt;getCount();<br/>    }<br/>    else<br/>     return false;<br/> }</p> <p><font color="#38761d">/**<br/> * PHP magic method<br/> * 设置属性值为空或删除事件名字对应的处理句柄<br/> */</font><br/> public function __unset($name)<br/> {<br/>    $setter='set'.$name;<br/>    if(method_exists($this,$setter))<br/>     $this-&gt;$setter(null);<br/>    else if(strncasecmp($name,'on',2)===0 &amp;&amp; method_exists($this,$name))<br/>     unset($this-&gt;_e[strtolower($name)]);<br/>    else if(method_exists($this,'get'.$name))<br/>     throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.',<br/>      array('{class}'=&gt;get_class($this), '{property}'=&gt;$name)));<br/> }</p> <p/> <p><font color="#38761d">/**<br/><font size="3">* PHP magic method<br/> * <span style="FONT-SIZE: 10pt; COLOR: green; mso-hansi-: 0pt"><a name="OLE_LINK4"/><a name="OLE_LINK3"><span style="FONT-SIZE: 10pt; COLOR: green; mso-hansi-: 0pt"><span style="mso-bookmark: OLE_LINK4">CComponent</span></span><span style="mso-bookmark: OLE_LINK4"><span style="FONT-SIZE: 10pt; COLOR: green; mso-hansi-: 0pt">未定义的类方法</span><span style="FONT-SIZE: 10pt; COLOR: green; mso-hansi-: 0pt">,</span><span style="FONT-SIZE: 10pt; COLOR: green; mso-hansi-: 0pt">寻找行为类里的同名方法</span></span></a><span style="FONT-SIZE: 10pt; COLOR: green; mso-hansi-: 0pt">,实现行为方法的调用</span></span></font><br/> */</font><br/> public function __call($name,$parameters)<br/> {<br/><font color="#38761d">   // 行为类存放的$_m数组不空</font><br/>    if($this-&gt;_m!==null)<br/>    {<br/><font color="#38761d">    // 循环取出$_m数组里存放的行为类</font><br/>     foreach($this-&gt;_m as $object)<br/>     {<br/><font color="#38761d">     // 行为类对象有效,并且方法存在,调用之</font><br/>      if($object-&gt;enabled &amp;&amp; method_exists($object,$name))<br/>       return call_user_func_array(array($object,$name),$parameters);<br/>     }<br/>    }<br/>    throw new CException(Yii::t('yii','{class} does not have a method named "{name}".',<br/>     array('{class}'=&gt;get_class($this), '{name}'=&gt;$name)));<br/> }</p> <p><font color="#38761d">/**<br/> * 根据行为名返回行为类对象<br/> */</font><br/> public function asa($behavior)<br/> {<br/>    return isset($this-&gt;_m[$behavior]) ? $this-&gt;_m[$behavior] : null;<br/> }</p> <p><font color="#38761d">/**<br/> * Attaches a list of behaviors to the component.<br/> * Each behavior is indexed by its name and should be an instance of<br/> * </font><a href="mailto:{@link"><font color="#38761d">{@link</font></a><font color="#38761d"> IBehavior}, a string specifying the behavior class, or an<br/> * array of the following structure:<br/> * &lt;pre&gt;<br/> * array(<br/> *     'class'=&gt;'path.to.BehaviorClass',<br/> *     'property1'=&gt;'value1',<br/> *     'property2'=&gt;'value2',<br/> * )<br/> * &lt;/pre&gt;<br/> * @param array list of behaviors to be attached to the component<br/> * @since 1.0.2<br/> */</font><br/> public function attachBehaviors($behaviors)<br/> {<br/><font color="#38761d">   // $behaviors为数组 $name=&gt;$behavior</font><br/>    foreach($behaviors as $name=&gt;$behavior)<br/>     $this-&gt;attachBehavior($name,$behavior);<br/> }</p> <p><br/><font color="#38761d">/**<br/> * 添加一个行为到组件<br/> */</font><br/> public function attachBehavior($name,$behavior)<br/> {<br/><font color="#38761d">   /* $behavior不是IBehavior接口的实例,则为<br/>    * array(<br/>    *     'class'=&gt;'path.to.BehaviorClass',<br/>    *     'property1'=&gt;'value1',<br/>    *     'property2'=&gt;'value2',<br/>    * )<br/>    * 传递给Yii::createComponent创建行为了并初始化对象属性<br/>    */</font><br/>    if(!($behavior instanceof IBehavior))<br/>     $behavior=Yii::createComponent($behavior);<br/>    $behavior-&gt;setEnabled(true);<br/>    $behavior-&gt;attach($this);<br/>    return $this-&gt;_m[$name]=$behavior;<br/> }</p> <p><font color="#38761d">/**<br/> * Raises an event.<br/> * This method represents the happening of an event. It invokes<br/> * all attached handlers for the event.<br/> * @param string the event name<br/> * @param CEvent the event parameter<br/> * @throws CException if the event is undefined or an event handler is invalid.<br/> */</font><br/> public function raiseEvent($name,$event)<br/> {<br/>    $name=strtolower($name);<br/><font color="#38761d">   // _e[$name] 事件处理句柄队列存在</font><br/>    if(isset($this-&gt;_e[$name]))<br/>    {<br/><font color="#38761d">     // 循环取出事件处理句柄</font><br/>     foreach($this-&gt;_e[$name] as $handler)<br/>     {<br/><font color="#38761d">     // 事件处理句柄为全局函数</font><br/>      if(is_string($handler))<br/>       call_user_func($handler,$event);<br/>      else if(is_callable($handler,true))<br/>      {<br/>       // an array: 0 - object, 1 - method name<br/>       list($object,$method)=$handler;<br/>       if(is_string($object)) <font color="#38761d">// 静态类方法</font><br/>        call_user_func($handler,$event);<br/>       else if(method_exists($object,$method))<br/>        $object-&gt;$method($event);<br/>       else<a href="Yii PHP 框架分析(三).htm">Yii PHP 框架分析(三)</a><br/>        throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".',<br/>         array('{class}'=&gt;get_class($this), '{event}'=&gt;$name, '{handler}'=&gt;$handler[1])));<br/>      }<br/>      else<br/>       throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".',<br/>        array('{class}'=&gt;get_class($this), '{event}'=&gt;$name, '{handler}'=&gt;gettype($handler))));<br/><font color="#38761d">    // $event 的handled 设置为true后停止队列里剩余句柄的调用</font><br/>     if(($event instanceof CEvent) &amp;&amp; $event-&gt;handled)<br/>       return;<br/>     }<br/>    }<br/>    else if(YII_DEBUG &amp;&amp; !$this-&gt;hasEvent($name))<br/>     throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.',<br/>      array('{class}'=&gt;get_class($this), '{event}'=&gt;$name)));<br/> }<br/> }</p> <p/> <p/> </div></td> </tr></tbody></table>