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