Yii PHP 框架分析(二)

Yii PHP 框架分析(二)
作者:wdy

http://hi.baidu.com/delphiss/blog/item/54597af595085ad3f3d38552.html

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

* 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)));

}

}

 

你可能感兴趣的:(PHP)