前言

最近在开发一个PHP系统,为了提高系统的扩展性,我想在系统中加入类似Javascript的事件处理机制,例如:我想在一篇新闻被添加以后,我想记录一下日志,用类似Javascript的代码,应该是这样写的:

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的事件模型就可以实现了,而且可以将回调函数放在某个对象中。

转载于:https://blog.51cto.com/11104408/1768745

PHP回调函数的几种用法相关推荐

  1. Python扩展库numpy中where()函数的三种用法

    第一种用法:只给where()函数传递一个数组作为参数,返回其中非0元素的下标. 第二种用法:给where()函数传递一个包含True/False值的数组,返回该数组中True值的下标,结合numpy ...

  2. JS中回调函数的三种写法!

    //第一种---------------------------------------- function parent(str,callback) {console.log(str);callba ...

  3. decode函数的几种用法详解

    decode的几种用法 1:使用decode判断字符串是否一样 DECODE(value,if1,then1,if2,then2,if3,then3,...,else) 含义为 IF 条件=值1 TH ...

  4. python range 步长为负数_Python入门第7课,循环结构学习,for语句range函数的3种用法...

    上课前,大陈带领学生们一起回顾和梳理前面学过的知识. 体验课,Python与人工智能初体验. 第1课,输出语句及赋值语句. 第2课,输入语句学习. 第3课.第4课,学习条件控制if语句. 第5课.第6 ...

  5. birt脚本for循环语句_Python入门第7课,循环结构学习,for语句range函数的3种用法

    上课前,大陈带领学生们一起回顾和梳理前面学过的知识. 体验课,Python与人工智能初体验. 第1课,输出语句及赋值语句. 第2课,输入语句学习. 第3课.第4课,学习条件控制if语句. 第5课.第6 ...

  6. python中for in range循环的用法_Python入门第7课,循环结构学习,for语句range函数的3种用法...

    上课前,大陈带领学生们一起回顾和梳理前面学过的知识. 体验课,Python与人工智能初体验. 第1课,输出语句及赋值语句. 第2课,输入语句学习. 第3课.第4课,学习条件控制if语句. 第5课.第6 ...

  7. QT中connect函数的几种用法详解总结

    前言 信号与槽机制是QT非常核心的东西,通过信号与槽我们可以将不同的部分有机的结合起来,使得各个组件之间的交互简单高效,信号与曹槽像是设计模式中的观察者模式(我自己觉得是这样),只关心信号何时发来,以 ...

  8. SUMPRODUCT函数的四种用法

    SUMPRODUCT函数 在给定的几组数组中,将数组间对应的元素相乘,并返回乘积之和. 语法 SUMPRODUCT(array1,array2,array3, ...) Array1,array2,a ...

  9. Mysql中decode函数的几种用法

    1.使用decode判断字符串是否一样 decode(value,if1,then1,if2,then2,if3,then3,...,else) 含义为 IF 条件=值1 THEN RETURN(va ...

最新文章

  1. 中文语境下的手机号识别
  2. bat no moudle named xxx
  3. 除了写代码,还能如何晋升为高级工程师?
  4. .netcore 判断是否 ajax 请求_第3部分-异步使用2-2:Ajax 之 状态码
  5. mongodb配置文件启动linux,Linux运维知识之Mongodb启动方法:设定参数启动;从设置文件启动...
  6. JBoss - 调整JVM内存 -Xms512m -Xmx1024m
  7. windows xp下Apache2.2.11整合Tomcat6.0.20
  8. 耳机插在电脑上没声音linux,解决linux上耳机没有声音
  9. excel自动调整行高和设置默认行高
  10. 箱形图适用于哪种数据_python数据分布型图表箱形图系列带误差线箱形图
  11. 活非光合细胞中ROS的主要从哪里细胞来源?
  12. t分布, 卡方x分布,F分布
  13. 被AI潮抛弃的企业?对话微软CTO韦青,如何应对“变革”焦虑
  14. 荣耀V30系列正式发布,它有满足你对5G手机的期待吗?
  15. 联想小娜怎么开启_联想小娜怎么唤醒
  16. 云计算赛项--2020年赛题基础部分[任务3]
  17. a50交割日时间表2021(a50三大主要功能)
  18. w ndows远程自动退出登陆,远程桌面连接Wndows 常见故障解决方法.doc
  19. 国外调查问卷项目赚美金
  20. 怎么说离职原因新的公司比较能接受?

热门文章

  1. 深度解析使用CSS单位px、em、rem、vh、vw、vmin、vmax实现页面布局
  2. 生成器generator
  3. MATALO OPENCART 自适应主题模板 ABC-0679-02
  4. 58. Length of Last Word
  5. 如何让EasyUI弹出层跳出框架
  6. Linux下使用Apache实现域名转发(Tomcat/JBOSS)
  7. UIBezierPath的使用(持续更新)
  8. uboot移植参考资料
  9. C++/C的文件结构
  10. python变量如何声明_如何确定变量是否在Python中声明?