如何在PHP中实现回调?


Answers:


173

该手册可互换地使用术语“回调”和“可调用”,但是,“回调”传统上是指充当函数指针的字符串或数组值,引用函数或类方法以供将来调用。自PHP 4以来,已经允许使用函数式编程的某些元素。

$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_funccall_user_func_array支持以上所有内容。

参见:http : //php.net/manual/en/language.types.callable.php

注释/注意事项:

  1. 如果函数/类已命名空间,则字符串必须包含标准名称。例如['Vendor\Package\Foo', 'method']
  2. call_user_func不支持通过引用传递非对象,所以您可以使用call_user_func_array,或者在以后的PHP版本,保存回调到var和使用直接的语法:$cb();
  3. 具有__invoke()方法(包括匿名函数)的对象属于“可调用”类别,可以按相同方式使用,但是我个人不将其与旧有“回调”术语相关联。
  4. 旧版将create_function()创建全局函数并返回其名称。它是包装器,eval()应改用匿名函数。

确实,使用该功能是执行此功能的正确方法。正如接受的答案所建议的那样,在使用变量然后调用它的同时,它很丑陋,并且无法很好地扩展代码。
icco

4
更改了接受的答案。同意评论,这是一个很好的答案。
Nick Stinemates,2009年

对于来自Python编程的读者来说,指出答案'someGlobalFunction'确实是已定义的函数将很有帮助。
TMOTTM 2013年

1
从PHP 5.3开始,有闭包,请参见Bart van Heukelom的答案。它比所有这些遗留问题更简单和“标准”。
realnice 2015年

是的,我在答案中提到了匿名函数,但是OP要求“回调”(在2008年),而这些旧式的回调在很多PHP代码库中仍然非常有用。
史蒂夫·克莱

74

使用PHP 5.3,您现在可以执行以下操作:

function doIt($callback) { $callback(); }

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

最后,这是一个不错的方法。对PHP的重要补充,因为回调很棒。


1
这应该是最佳答案。
GROVER。

30

回调的实现是这样完成的

// 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');

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


21
哦,我的上帝。这是标准吗?这太可怕了!
Nick Retallack

如上所示,还有其他几种方法。我真的也这么认为
Nick Stinemates,

3
@Nick Retallack,我看不出有什么太可怕了。对于我所知道的语言,例如JavaScript和C#,它们都可以按这种模式构造回调函数。来自JavaScirpt和C#,我真的不习惯call_user_func()。这让我觉得自己必须适应PHP,而不是相反。
安东尼

4
@Antony我反对这样的事实,即字符串是该语言中的函数指针。我三年前发布了该评论,所以到目前为止我已经很习惯了,但是我认为PHP是我所知道的唯一的语言(除了shell脚本之外)。
Nick Retallack

1
@Antony我更喜欢使用与javascript中相同的语法。所以我什至不理解人们为什么要使用call_user_func()他们的语法,使他们能够动态调用函数并进行回调。我同意你的看法!
botenvouwer

9

我最近发现的一个妙招是使用PHP create_function()创建一次性使用的匿名/ lambda函数。它是PHP函数等是有用的array_map()preg_replace_callback()usort()用于加工定制是使用回调。它看起来很像是eval()在幕后进行的,但是它仍然是使用PHP的一种很好的功能风格方式。


6
不幸的是,垃圾回收器在这种构造中不能很好地发挥作用,从而可能导致内存泄漏。如果您出于性能考虑,请避免使用create_function()。
fuxia

1
哎哟。感谢您的单挑。
育空地

您介意使用PHP 7.4版本(箭头功能)更新答案并添加有关已弃用的警告create_function()吗?
Dharman


7

您将要验证您的通话是否有效。例如,对于特定功能,您将要检查该功能是否存在:

function doIt($callback) {
    if(function_exists($callback)) {
        $callback();
    } else {
        // some error handling
    }
}

如果回调不是函数而是包含对象和方法的数组怎么办?
d -_- b 2010年

3
或者更确切地说is_callable( $callback )
ROKO C. Buljan

1
两者都是很好的建议-有必要检查您自己的回调实现方式。我希望警告不要调用一些不存在的东西,从而导致致命事故。我做了回答更普遍
SeanDowney

5

create_function在课堂上对我不起作用。我不得不用call_user_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!" );
        }
    }

}

然后,使用:

<?php 
include_once('Dispatcher.php');
$d = new Dispatcher( 'do_callback' );
$d->asynchronous_method();

function do_callback( $data ){
   print 'Data is: ' .  $data .  "\n";
}
?>

[编辑]添加了缺少的括号。另外,添加了回调声明,我更喜欢这样。


1
Dispatcher类是否不需要$ this-> callback = $ callback的属性才能工作?
James P.

@james poulson:PHP是一种动态语言,因此可以正常工作。但是我很懒。我通常会声明属性,使每个人的生活更加轻松。您的问题使我再次查看该代码,发现语法错误。谢谢
goliatone 2011年

我不知道这是可能的。感谢你的回答 :) 。
James P.

在创建Dispatcher对象并调用async方法之前声明do_callback函数不是更安全吗?
–jectamenta

3

每次create_function()在php中使用时,我都会畏缩。

参数是一个用逗号分隔的字符串,整个函数体都在一个字符串中...哎呀...我认为即使尝试,它们也无法使其更难看。

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


1
而且,当然,它是运行时字符串eval,因此在编译时不会检查其有效语法或其他任何内容。
hobbs

这个答案在过去两年中已经过时了。create_function()现在已弃用,不应使用。
Dharman

2

对于那些不关心破坏与PHP兼容性的人< 5.4,我建议使用类型提示进行更简洁的实现。

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"

create_function()从PHP 7.2.0开始,警告已被弃用。强烈建议不要使用此功能。
达曼
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.