文章来源:
http://www.cnitblog.com/CoffeeCat/archive/2009/04/21/56541.html
function
fnCallBack(
$news
)
{
//
将$news的信息记录到日志中
writeLog(
$news
->
getTitle()
.
'
has been added successfully!
'
);
}

$newsEventManager
->
addEventListener(
'
add
'
,
fnCallBack );
其中,fnCallBack函数是回调函数,addEventListener表示监听newsEventManager的add事件。当一篇news被add以后,系统就会调用fnCallBack函数,从而完成writeLog的动作。
但是,PHP中的函数传递方法和Javascript有很大的不同。在Javascript中,函数也是对象,它可以很方便的当作参数传递,但是PHP不行。
$newsEventManager
->
addEventListener(
'
add
'
,
fnCallBack );
上面这行代码中的fnCallBack,看上去好像是那个函数的句柄,但实质上它是一个字符串,并不是我们所要的函数。
为了实现我们的事件模型,有必要研究一下PHP的回调函数的实现方法。
全局函数的回调
这里的全局函数的意思,是直接使用function定义的函数,它不包含在任何对象或类之中。请看下面的例子
示例代码
function
fnCallBack(
$msg1
,
$msg2
)
{
echo
'
msg1:
'
.
$msg1
;
echo
"
<br />\n
"
;
echo
'
msg2:
'
.
$msg2
;
}
$fnName
=
"
fnCallBack
"
;
$params
=
array
(
'
hello
'
,
'
world
'
);
call_user_func_array
(
$fnName
,
$params
);
代码说明:
这里使用了PHP内置的函数call_user_func_array来进行调用。call_user_func_array有两个参数,第1个参数是一个字符串,表示要调用的函数名,第2个参数是一个数组,表示参数列表,按照顺序依次会传递给要调用的函数。
效果如下:
类的静态方法的回调
如果我们要回调的方法,是一个类的静态方法,那怎么办呢?我们依然可以利用PHP内置的call_user_func_array方法来进行调用,请看示例:
示例代码:
class
MyClass
{
public
static
function
fnCallBack(
$msg1
,
$msg2
)
{
echo
'
msg1:
'
.
$msg1
;
echo
"
<br />\n
"
;
echo
'
msg2:
'
.
$msg2
;
}
}

$className
=
'
MyClass
'
;
$fnName
=
"
fnCallBack
"
;
$params
=
array
(
'
hello
'
,
'
world
'
);
call_user_func_array
(
array
(
$className
,
$fnName
)
,
$params
);
代码说明:
这段代码和第1种方法的代码很相似,我们将类名(MyClass)也作为call_user_func_array的第1个参数传递进去,就可以实现类的静态方法的回调了。注意,这时call_user_func_array的第1个参数是一个数组了,数组的第1个元素是类名,第二个元素是要调用的函数名
运行结果:
(其实和第1种方法的结果是一样的 ^_^ )
继续研究
如果我用这种方法调用一个类的非静态方法(也就是把static去掉),会出现什么结果呢?请看下面代码
class
MyClass
{
public
function
fnCallBack(
$msg1
,
$msg2
)
{
echo
'
msg1:
'
.
$msg1
;
echo
"
<br />\n
"
;
echo
'
msg2:
'
.
$msg2
;
}
}

$className
=
'
MyClass
'
;
$fnName
=
"
fnCallBack
"
;
$params
=
array
(
'
hello
'
,
'
world
'
);
call_user_func_array
(
array
(
$className
,
$fnName
)
,
$params
);
运行结果
和前面的结果还是一样的。。。
现在我为这个类添加一点属性,并在方法中引用
class
MyClass
{
private
$name
=
'
abc
'
;
public
function
fnCallBack(
$msg1
,
$msg2
)
{
echo
'
object name:
'
.
$this
->
name;
echo
"
<br />\n
"
;
echo
'
msg1:
'
.
$msg1
;
echo
"
<br />\n
"
;
echo
'
msg2:
'
.
$msg2
;
}
}

$className
=
'
MyClass
'
;
$fnName
=
"
fnCallBack
"
;
$params
=
array
(
'
hello
'
,
'
world
'
);
call_user_func_array
(
array
(
$className
,
$fnName
)
,
$params
);
运行结果
出现解析错误,提示$this没有在对象环境下出现,说明这个方法不能用类来调用,而是要用对象来调用。那我们就修改一下代码,创建一个对象:
class
MyClass
{
public
function
fnCallBack(
$msg1
,
$msg2
)
{
echo
'
msg1:
'
.
$msg1
;
echo
"
<br />\n
"
;
echo
'
msg2:
'
.
$msg2
;
}
}

$myobj
=
new
MyClass();
$className
=
'
myobj
'
;
$fnName
=
"
fnCallBack
"
;
$params
=
array
(
'
hello
'
,
'
world
'
);
call_user_func_array( array( $className , $fnName ) , $params );
运行结果:
提示call_user_func_array的第1个参数非法,也就是说,调用失败。看来我们不能用call_user_func_array方法来回调一个对象的方法了,那么如何实现对象方法的回调的?
对象的方法的回调
我先用最原始的字符串形式的调用方法尝试了一下,如下所示:
class
MyClass
{
private
$name
=
'
abc
'
;
public
function
fnCallBack(
$msg1
=
'
default msg1
'
,
$msg2
=
'
default msg2
'
)
{
echo
'
object name:
'
.
$this
->
name;
echo
"
<br />\n
"
;
echo
'
msg1:
'
.
$msg1
;
echo
"
<br />\n
"
;
echo
'
msg2:
'
.
$msg2
;
}
}

$myobj
=
new
MyClass();
$fnName
=
"
fnCallBack
"
;
$params
=
array
(
'
hello
'
,
'
world
'
);
$myobj
->
$fnName
();
成功了,输出结果
调用是成功了,不过如何把参数params传给这个方法呢,如果把params直接传进去,那么它会作为1个参数,怎么把params拆开来传进去呢?
查了下PHP手册,找到了create_function函数,这个方法可以用字符串来创建一个匿名函数,好,有思路了,可以创建一个匿名的函数,在这个匿名函数中,调用我们的回调函数,并把参数传进去。
我先手动创建一个匿名函数anonymous,在这个函数中,用前面试出来的方法调用回调函数,如下所示:
class
MyClass
{
private
$name
=
'
abc
'
;
public
function
fnCallBack(
$msg1
=
'
default msg1
'
,
$msg2
=
'
default msg2
'
)
{
echo
'
object name:
'
.
$this
->
name;
echo
"
<br />\n
"
;
echo
'
msg1:
'
.
$msg1
;
echo
"
<br />\n
"
;
echo
'
msg2:
'
.
$msg2
;
}
}

$myobj
=
new
MyClass();
$fnName
=
"
fnCallBack
"
;
$params
=
array
(
'
hello
'
,
'
world
'
);

function
anonymous()
{
global
$myobj
;
global
$fnName
;
global
$params
;
$myobj
->
$fnName
(
$params
[
0
]
,
$params
[
1
] );
}
anonymous();
成功了,可以看到,对象的属性name也输出来了
然后,我用create_function来创建这个匿名函数,同时,代码中的params[0],params[1]应该是动态生成的,代码如下:
$strParams
=
''
;
$strCode
=
'
global $myobj;global $fnName;global $params;$myobj->$fnName(
'
;
for
(
$i
=
0
;
$i
<
count
(
$params
) ;
$i
++
)
{
$strParams
.=
(
'
$params[
'
.
$i
.
'
]
'
);
if
(
$i
!=
count
(
$params
)
-
1
)
{
$strParams
.=
'
,
'
;
}
}
$strCode
=
$strCode
.
$strParams
.
"
);
"
;
$anonymous
=
create_function
(
''
,
$strCode
);
$anonymous
();
这段代码可以定义一个匿名函数,并保存在$anonymous变量中,最后调用这个$anonymous,实现了方法的回调,如图
PHP事件模型(观察者模式)的实现思路
至此,PHP中的3种常见的函数类型(全局函数,类静态函数,对象的方法)都可以回调了,可以实现文章一开始说的事件模型了 :)
事件模型模仿Firefox的Javascript实现,有3个方法,分别是
addEventListener:注册一个事件上的响应回调函数
removeEventListener:删除一个事件上的响应回调函数
fire:触发一个事件,也就是循环调用所有响应这个事件的回调函数
不过,由于第2、第3种方法需要传递上下文(也就是类名和对象名),所以addEventListener和removeEventListener应该有3个参数,我是这样设计的:
function
addEventListener(
$evtName
,
$handler
,
$scope
=
null
)
第1个参数表示事件名,字符串类型
第2个参数表示回调函数名,字符串类型
第3个参数$scope是上下文环境,一共有3种类型,null表示传入的handler函数是一个全局函数,字符串类型表示传入的handler函数是scope类的静态函数,对象类型表示传入的scope是一个对象,handler函数是对象的一个方法。
function
fire(
$evtName
,
$params
=
null
)
这个方法内,会读取出所有响应evtName的handler,然后判断它对应的scope,如果是null,则用本文第1种方法回调,如果是字符串,则用本文第2种方法回调,如果是对象,则用本文第3种方法回调。这样,一个PHP的事件模型就可以实现了,而且可以将回调函数放在某个对象中。
前言
最近在开发一个PHP系统,为了提高系统的扩展性,我想在系统中加入类似Javascript的事件处理机制,例如:我想在一篇新闻被添加以后,我想记录一下日志,用类似Javascript的代码,应该是这样写的:
最近在开发一个PHP系统,为了提高系统的扩展性,我想在系统中加入类似Javascript的事件处理机制,例如:我想在一篇新闻被添加以后,我想记录一下日志,用类似Javascript的代码,应该是这样写的:








其中,fnCallBack函数是回调函数,addEventListener表示监听newsEventManager的add事件。当一篇news被add以后,系统就会调用fnCallBack函数,从而完成writeLog的动作。
但是,PHP中的函数传递方法和Javascript有很大的不同。在Javascript中,函数也是对象,它可以很方便的当作参数传递,但是PHP不行。

上面这行代码中的fnCallBack,看上去好像是那个函数的句柄,但实质上它是一个字符串,并不是我们所要的函数。
为了实现我们的事件模型,有必要研究一下PHP的回调函数的实现方法。
全局函数的回调
这里的全局函数的意思,是直接使用function定义的函数,它不包含在任何对象或类之中。请看下面的例子
示例代码









代码说明:
这里使用了PHP内置的函数call_user_func_array来进行调用。call_user_func_array有两个参数,第1个参数是一个字符串,表示要调用的函数名,第2个参数是一个数组,表示参数列表,按照顺序依次会传递给要调用的函数。
效果如下:

类的静态方法的回调
如果我们要回调的方法,是一个类的静态方法,那怎么办呢?我们依然可以利用PHP内置的call_user_func_array方法来进行调用,请看示例:
示例代码:














代码说明:
这段代码和第1种方法的代码很相似,我们将类名(MyClass)也作为call_user_func_array的第1个参数传递进去,就可以实现类的静态方法的回调了。注意,这时call_user_func_array的第1个参数是一个数组了,数组的第1个元素是类名,第二个元素是要调用的函数名
运行结果:

(其实和第1种方法的结果是一样的 ^_^ )
继续研究
如果我用这种方法调用一个类的非静态方法(也就是把static去掉),会出现什么结果呢?请看下面代码














运行结果

和前面的结果还是一样的。。。
现在我为这个类添加一点属性,并在方法中引用

















运行结果

出现解析错误,提示$this没有在对象环境下出现,说明这个方法不能用类来调用,而是要用对象来调用。那我们就修改一下代码,创建一个对象:















运行结果:

提示call_user_func_array的第1个参数非法,也就是说,调用失败。看来我们不能用call_user_func_array方法来回调一个对象的方法了,那么如何实现对象方法的回调的?
对象的方法的回调
我先用最原始的字符串形式的调用方法尝试了一下,如下所示:

















成功了,输出结果

调用是成功了,不过如何把参数params传给这个方法呢,如果把params直接传进去,那么它会作为1个参数,怎么把params拆开来传进去呢?
查了下PHP手册,找到了create_function函数,这个方法可以用字符串来创建一个匿名函数,好,有思路了,可以创建一个匿名的函数,在这个匿名函数中,调用我们的回调函数,并把参数传进去。
我先手动创建一个匿名函数anonymous,在这个函数中,用前面试出来的方法调用回调函数,如下所示:

























成功了,可以看到,对象的属性name也输出来了

然后,我用create_function来创建这个匿名函数,同时,代码中的params[0],params[1]应该是动态生成的,代码如下:













这段代码可以定义一个匿名函数,并保存在$anonymous变量中,最后调用这个$anonymous,实现了方法的回调,如图

PHP事件模型(观察者模式)的实现思路
至此,PHP中的3种常见的函数类型(全局函数,类静态函数,对象的方法)都可以回调了,可以实现文章一开始说的事件模型了 :)
事件模型模仿Firefox的Javascript实现,有3个方法,分别是



不过,由于第2、第3种方法需要传递上下文(也就是类名和对象名),所以addEventListener和removeEventListener应该有3个参数,我是这样设计的:

第1个参数表示事件名,字符串类型
第2个参数表示回调函数名,字符串类型
第3个参数$scope是上下文环境,一共有3种类型,null表示传入的handler函数是一个全局函数,字符串类型表示传入的handler函数是scope类的静态函数,对象类型表示传入的scope是一个对象,handler函数是对象的一个方法。

这个方法内,会读取出所有响应evtName的handler,然后判断它对应的scope,如果是null,则用本文第1种方法回调,如果是字符串,则用本文第2种方法回调,如果是对象,则用本文第3种方法回调。这样,一个PHP的事件模型就可以实现了,而且可以将回调函数放在某个对象中。