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