192

How are callbacks written in PHP?

1
  • 1
    Gonna link another question to this one, because I was trying to call a closure. Commented Aug 28, 2018 at 7:37

9 Answers 9

177

The manual uses the terms "callback" and "callable" interchangeably, however, "callback" traditionally refers to a string or array value that acts like a function pointer, referencing a function or class method for future invocation. This has allowed some elements of functional programming since PHP 4. The flavors are:

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

This is a safe way to use callable values in general:

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

Modern PHP versions allow the first three formats above to be invoked directly as $cb(). call_user_func and call_user_func_array support all the above.

See: http://php.net/manual/en/language.types.callable.php

Notes/Caveats:

  1. If the function/class is namespaced, the string must contain the fully-qualified name. E.g. ['Vendor\Package\Foo', 'method']
  2. call_user_func does not support passing non-objects by reference, so you can either use call_user_func_array or, in later PHP versions, save the callback to a var and use the direct syntax: $cb();
  3. Objects with an __invoke() method (including anonymous functions) fall under the category "callable" and can be used the same way, but I personally don't associate these with the legacy "callback" term.
  4. The legacy create_function() creates a global function and returns its name. It's a wrapper for eval() and anonymous functions should be used instead.
Sign up to request clarification or add additional context in comments.

5 Comments

Indeed, using the function is the proper way to do it. While using a variable and then just calling it, as suggested in the accepted answer is cool, it's ugly and won't scale well with code.
Changed accepted answer. Agreed with comments, this is a great answer.
It would be helpful to readers coming from Python programming to point out in the answer that 'someGlobalFunction' indeed is a defined function.
As of PHP 5.3, there are closures, see Bart van Heukelom's answer. It's much simpler and "standard" than all this legacy mess.
Yes, I mention anonymous functions in my answer, but the OP asked for "callback" (in 2008) and these older-style callbacks are still very much in use in tons of PHP codebases.
86

With PHP 5.3, you can now do this:

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

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

Finally a nice way to do it. A great addition to PHP, because callbacks are awesome.

1 Comment

This needs to be the top answer.
31

Implementation of a callback is done like so

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

Displays: Data is: this is my data

6 Comments

Oh my god. Is that the standard? That's terrible!
There are a few other ways to do it as shown above. I really thought the same.
@Nick Retallack, I don't see what is so horrible about it. For the languages I know of, such as JavaScript and C#, they all can structure their callback function in such pattern. Coming from JavaScirpt and C#, I am really not used to call_user_func(). It makes me feel like I have to adapt myself to PHP, instead of the other way around.
@Antony I was objecting to the fact that strings are function pointers in this language. I posted that comment three years ago, so I'm pretty used to it by now, but I think PHP is the only language I know of (other than shell scripting) where this is the case.
@Antony I prefer to use the same syntax I use in javascript. So I don't even understand why people want to use call_user_func() When they have a syntax which enables them to dynamically call functions and make callbacks. I agree with you!
|
10

One nifty trick that I've recently found is to use PHP's create_function() to create an anonymous/lambda function for one-shot use. It's useful for PHP functions like array_map(), preg_replace_callback(), or usort() that use callbacks for custom processing. It looks pretty much like it does an eval() under the covers, but it's still a nice functional-style way to use PHP.

2 Comments

Unfortunately, the garbage collector doesn’t play very well with this construct producing potential memory leaks. If you’re out for performance, avoid create_function().
Would you mind updating the answer with PHP 7.4 version (arrow functions) and adding a warning about deprecated create_function()?
8

well... with 5.3 on the horizon, all will be better, because with 5.3, we'll get closures and with them anonymous functions

http://wiki.php.net/rfc/closures

Comments

8

You will want to verify whatever your calling is valid. For example, in the case of a specific function, you will want to check and see if the function exists:

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

3 Comments

What if callback is not a function, but an array holding object and method?
or rather is_callable( $callback )
Both are good suggestions - It will be necessary to check your own particular implementation of how you do a callback. I was hoping to warn against calling something that didn't exist causing a fatal. I made the answer more general
5

create_function did not work for me inside a class. I had to use 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!" );
        }
    }

}

Then, to use:

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

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

[Edit] Added a missing parenthesis. Also, added the callback declaration, I prefer it that way.

4 Comments

Doesn't the Dispatcher class require an attribute for $this->callback = $callback to work?
@james poulson: PHP is a dynamic language, so it works. But I was being lazy. I usually do declare properties, makes everyones life easier. Your question made me look at that code again and spot a syntax error, thou. Thanks
I didn't know this was possible. Thanks for the answer :) .
Woudn't it be safer to declare the do_callback function before creating the Dispatcher object and calling the async method?
4

For those who don't care about breaking compatibility with PHP < 5.4, I'd suggest using type hinting to make a cleaner implementation.

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"

1 Comment

Warning create_function() has been DEPRECATED as of PHP 7.2.0. Relying on this function is highly discouraged.
3

I cringe every time I use create_function() in php.

Parameters are a coma separated string, the whole function body in a string... Argh... I think they could not have made it uglier even if they tried.

Unfortunately, it is the only choice when creating a named function is not worth the trouble.

2 Comments

And, of course, it's runtime string eval, so it doesn't get checked for valid syntax or anything else at compile time.
This answer has been outdated for the past 2 years. create_function() is now deprecated and should not be used.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.