192

How are callbacks written in PHP?

Nick Stinemates
  • 41,511
  • 21
  • 59
  • 60
  • 1
    Gonna link another [question](https://stackoverflow.com/questions/4535330/calling-closure-assigned-to-object-property-directly) to this one, because I was trying to call a closure. – akinuri Aug 28 '18 at 07:37

9 Answers9

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.
Steve Clay
  • 8,671
  • 2
  • 42
  • 48
  • 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. – icco Aug 30 '09 at 20:44
  • 4
    Changed accepted answer. Agreed with comments, this is a great answer. – Nick Stinemates Oct 05 '09 at 23:33
  • It would be helpful to readers coming from Python programming to point out in the answer that `'someGlobalFunction'` indeed is a defined function. – TMOTTM Apr 02 '13 at 13:45
  • 1
    As of PHP 5.3, there are closures, see [Bart van Heukelom's answer](http://stackoverflow.com/a/2523807/1504300). It's much simpler and "standard" than all this legacy mess. – reallynice Apr 24 '15 at 15:53
  • 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. – Steve Clay Apr 24 '15 at 19:53
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.

Bart van Heukelom
  • 43,244
  • 59
  • 186
  • 301
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

Nick Stinemates
  • 41,511
  • 21
  • 59
  • 60
  • 23
    Oh my god. Is that the standard? That's terrible! – Nick Retallack Sep 08 '08 at 02:49
  • There are a few other ways to do it as shown above. I really thought the same. – Nick Stinemates Sep 08 '08 at 04:39
  • Wow, this adds some new twists to conventional programming. – James P. Sep 13 '11 at 01:00
  • 3
    @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 Dec 15 '11 at 14:16
  • 4
    @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. – Nick Retallack Dec 15 '11 at 19:00
  • 1
    @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! – botenvouwer Aug 17 '13 at 12:25
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.

yukondude
  • 24,013
  • 13
  • 49
  • 58
  • 6
    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(). – fuxia Mar 26 '10 at 16:04
  • Would you mind updating the answer with PHP 7.4 version (arrow functions) and adding a warning about deprecated `create_function()`? – Dharman Mar 01 '20 at 14:09
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

pilif
  • 12,548
  • 5
  • 34
  • 31
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
    }
}
SeanDowney
  • 17,368
  • 20
  • 81
  • 90
  • What if callback is not a function, but an array holding object and method? – d-_-b Apr 29 '10 at 11:59
  • 4
    or rather `is_callable( $callback )` – Roko C. Buljan Mar 30 '17 at 17:32
  • 1
    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 – SeanDowney Apr 03 '17 at 18:45
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.

goliatone
  • 2,183
  • 16
  • 29
  • 1
    Doesn't the Dispatcher class require an attribute for $this->callback = $callback to work? – James P. Sep 13 '11 at 01:02
  • @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 – goliatone Sep 13 '11 at 09:28
  • I didn't know this was possible. Thanks for the answer :) . – James P. Sep 13 '11 at 10:22
  • Woudn't it be safer to declare the do_callback function before creating the Dispatcher object and calling the async method? – ejectamenta Jan 08 '16 at 19:21
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"
Kendall Hopkins
  • 43,213
  • 17
  • 66
  • 89
  • 1
    **Warning** `create_function()` has been DEPRECATED as of PHP 7.2.0. Relying on this function is highly discouraged. – Dharman Mar 01 '20 at 14:12
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.

Pat
  • 36,282
  • 18
  • 72
  • 87
  • 1
    And, of course, it's runtime string eval, so it doesn't get checked for valid syntax or anything else at compile time. – hobbs Aug 13 '09 at 14:54
  • This answer has been outdated for the past 2 years. `create_function()` is now deprecated and should not be used. – Dharman Mar 01 '20 at 14:11