如何在PHP中实现回调?

如何在PHP中实现回调?,php,Php,回调是如何用PHP编写的?回调的实现是这样完成的 // This function uses a callback function. function doIt($callback) { $data = "this is my data"; $callback($data); } // This is a sample callback function for doIt(). function myCallback($data) { print '

回调是如何用PHP编写的?

回调的实现是这样完成的

// This function uses a callback function. 
function doIt($callback) 
{ 
    $data = "this is my data";
    $callback($data); 
} 


// This is a sample callback function for doIt(). 
function myCallback($data) 
{ 
    print 'Data is: ' .  $data .  "\n"; 
} 


// Call doIt() and pass our sample callback function's name. 
doIt('myCallback');

显示:数据是:这是我的数据

我最近发现的一个妙招是使用PHP创建一个匿名/lambda函数供一次性使用。对于使用回调进行自定义处理的PHP函数,如
array\u map()
preg\u replace\u callback()
,或
usort()
,它非常有用。它看起来很像是在封面下做了一个
eval()
,但它仍然是使用PHP的一种很好的函数式方式。

每次在PHP中使用
create\u function()
时,我都会畏缩

参数是一个分开的字符串,整个函数体在一个字符串中。。。啊。。。我认为即使他们尝试了,也不可能让事情变得更丑陋


不幸的是,当创建命名函数时,它是唯一的选择,不值得这么麻烦。

那么。。。有了5.3,一切都会更好,因为有了5.3,我们将得到闭包和匿名函数


您需要验证您的呼叫是否有效。例如,对于特定函数,您需要检查该函数是否存在:

function doIt($callback) {
    if(function_exists($callback)) {
        $callback();
    } else {
        // some error handling
    }
}
本手册交替使用“回调”和“可调用”这两个术语,然而,“回调”传统上指的是一个字符串或数组值,其作用类似于,引用函数或类方法以供将来调用。自PHP4以来,这就允许了一些函数式编程元素。口味是:

$cb1 = 'someGlobalFunction';
$cb2 = ['ClassName', 'someStaticMethod'];
$cb3 = [$object, 'somePublicMethod'];

// this syntax is callable since PHP 5.2.3 but a string containing it
// cannot be called directly
$cb2 = 'ClassName::someStaticMethod';
$cb2(); // fatal error

// legacy syntax for PHP 4
$cb3 = array(&$object, 'somePublicMethod');
通常,这是使用可调用值的安全方法:

if (is_callable($cb2)) {
    // Autoloading will be invoked to load the class "ClassName" if it's not
    // yet defined, and PHP will check that the class has a method
    // "someStaticMethod". Note that is_callable() will NOT verify that the
    // method can safely be executed in static context.

    $returnValue = call_user_func($cb2, $arg1, $arg2);
}
现代PHP版本允许以
$cb()
的形式直接调用上述前三种格式
call_user_func
call_user_func_数组
支持上述所有功能

见:

注意事项:

  • 如果函数/类是命名空间的,则字符串必须包含完全限定名。例如,
    ['Vendor\Package\Foo','method']
  • call\u user\u func
    不支持通过引用传递非对象,因此您可以使用
    call\u user\u func\u数组
    ,或者在以后的PHP版本中,将回调保存到var并使用直接语法:
    $cb()
  • 带有方法的对象(包括匿名函数)属于“可调用”类别,可以以相同的方式使用,但我个人不将它们与遗留的“回调”术语相关联
  • 传统的
    create_函数()
    创建一个全局函数并返回其名称。它是
    eval()
    的包装器,应该改用匿名函数

  • create_函数
    在类中对我不起作用。我不得不使用
    call\u user\u func

    <?php
    
    class Dispatcher {
        //Added explicit callback declaration.
        var $callback;
    
        public function Dispatcher( $callback ){
             $this->callback = $callback;
        }
    
        public function asynchronous_method(){
           //do asynch stuff, like fwrite...then, fire callback.
           if ( isset( $this->callback ) ) {
                if (function_exists( $this->callback )) call_user_func( $this->callback, "File done!" );
            }
        }
    
    }
    
    
    
    [编辑] 添加了缺少的括号。
    另外,添加了回调声明,我更喜欢这种方式。

    使用PHP5.3,您现在可以执行以下操作:

    function doIt($callback) { $callback(); }
    
    doIt(function() {
        // this will be done
    });
    

    终于找到了一个很好的方法。这是对PHP的一个很好的补充,因为回调非常棒。

    对于那些不关心破坏与PHP的兼容性的人,我建议使用类型暗示来实现更干净的实现

    function call_with_hello_and_append_world( callable $callback )
    {
         // No need to check $closure because of the type hint
         return $callback( "hello" )."world";
    }
    
    function append_space( $string )
    {
         return $string." ";
    }
    
    $output1 = call_with_hello_and_append_world( function( $string ) { return $string." "; } );
    var_dump( $output1 ); // string(11) "hello world"
    
    $output2 = call_with_hello_and_append_world( "append_space" );
    var_dump( $output2 ); // string(11) "hello world"
    
    $old_lambda = create_function( '$string', 'return $string." ";' );
    $output3 = call_with_hello_and_append_world( $old_lambda );
    var_dump( $output3 ); // string(11) "hello world"
    


    哦,我的上帝。这是标准吗?太可怕了!如上图所示,还有其他几种方法。“我真的也这么想。”尼克·雷特拉克,我不明白这有什么可怕的。对于我所知道的语言,比如JavaScript和C#,它们都可以用这种模式构造回调函数。来自JavaScirpt和C#,我真的不习惯调用_user_func()。这让我觉得我必须适应PHP,而不是相反。@Antony我反对字符串是这种语言中的函数指针这一事实。我三年前发表了这篇评论,所以我现在已经很习惯了,但我认为PHP是我所知道的唯一一种语言(除了shell脚本),在这种情况下。@Antony我更喜欢使用与javascript相同的语法。所以我甚至不明白为什么人们想要使用
    call\u user\u func()
    ,因为他们有一种语法,可以动态调用函数并进行回调。我同意你的看法!当然,它是运行时字符串eval,因此在编译时不会检查它的有效语法或其他任何内容
    create_function()
    现在已被弃用,不应使用。事实上,使用该函数是正确的方法。当使用一个变量,然后仅仅调用它时,正如公认答案中所建议的那样,这很酷,它很难看,并且不能很好地与代码配合。更改了公认答案。同意评论,这是一个很好的答案。来自Python编程的读者在答案中指出
    'someGlobalFunction'
    确实是一个已定义的函数,这将对他们有所帮助。从PHP 5.3开始,有闭包,请参阅。是的,我在回答中提到了匿名函数,但OP要求“回调”(2008年),而这些老式回调仍然在大量PHP代码库中使用。不幸的是,垃圾收集器不能很好地使用这种结构,从而产生潜在的内存泄漏。如果出于性能考虑,请避免使用create_function()。是否介意使用PHP7.4版本(箭头函数)更新答案,并添加一条关于已弃用的
    create_function()
    ?如果回调不是函数,而是包含对象和方法的数组,或者
    是可调用的($callback),该怎么办
    这两个都是很好的建议-有必要检查您自己如何执行回调的具体实现。我希望提醒大家不要打电话给不存在的东西,以免造成致命的后果。我让答案更一般了Dispatcher类不需要$this->callback=$callback的属性才能工作吗?@james poulson:PHP是一种动态语言,所以它可以工作。但是我太懒了。我通常会声明属性,这样每个人的生活都会更轻松。你的问题让我又看了一遍代码,发现了一个错误
    function call_with_hello_and_append_world( callable $callback )
    {
         // No need to check $closure because of the type hint
         return $callback( "hello" )."world";
    }
    
    function append_space( $string )
    {
         return $string." ";
    }
    
    $output1 = call_with_hello_and_append_world( function( $string ) { return $string." "; } );
    var_dump( $output1 ); // string(11) "hello world"
    
    $output2 = call_with_hello_and_append_world( "append_space" );
    var_dump( $output2 ); // string(11) "hello world"
    
    $old_lambda = create_function( '$string', 'return $string." ";' );
    $output3 = call_with_hello_and_append_world( $old_lambda );
    var_dump( $output3 ); // string(11) "hello world"