How do I implement a callback in PHP?
Asked Answered
E

9

192

How are callbacks written in PHP?

Eachelle answered 8/9, 2008 at 0:53 Comment(1)
Gonna link another question to this one, because I was trying to call a closure.Discreet
A
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.
Aversion answered 8/9, 2008 at 20:29 Comment(5)
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.Carmine
Changed accepted answer. Agreed with comments, this is a great answer.Eachelle
It would be helpful to readers coming from Python programming to point out in the answer that 'someGlobalFunction' indeed is a defined function.Capsulize
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.Dispossess
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.Aversion
I
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.

Intersex answered 26/3, 2010 at 14:5 Comment(1)
This needs to be the top answer.Suspensory
E
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

Eachelle answered 8/9, 2008 at 0:54 Comment(6)
Oh my god. Is that the standard? That's terrible!Pygmy
There are a few other ways to do it as shown above. I really thought the same.Eachelle
Wow, this adds some new twists to conventional programming.Carioca
@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.Reader
@Reader 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.Pygmy
@Reader 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!Ill
G
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.

Glossographer answered 8/9, 2008 at 2:20 Comment(2)
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().Magus
Would you mind updating the answer with PHP 7.4 version (arrow functions) and adding a warning about deprecated create_function()?Cesaro
G
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

Graveyard answered 8/9, 2008 at 12:33 Comment(0)
B
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
    }
}
Beaming answered 8/9, 2008 at 19:51 Comment(3)
What if callback is not a function, but an array holding object and method?Jeaninejeanlouis
or rather is_callable( $callback )Towill
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 generalBeaming
D
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.

Dorr answered 13/8, 2009 at 14:43 Comment(4)
Doesn't the Dispatcher class require an attribute for $this->callback = $callback to work?Carioca
@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. ThanksDorr
I didn't know this was possible. Thanks for the answer :) .Carioca
Woudn't it be safer to declare the do_callback function before creating the Dispatcher object and calling the async method?Maund
B
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"
Bak answered 25/7, 2012 at 20:3 Comment(1)
Warning create_function() has been DEPRECATED as of PHP 7.2.0. Relying on this function is highly discouraged.Cesaro
T
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.

Towline answered 8/9, 2008 at 12:26 Comment(2)
And, of course, it's runtime string eval, so it doesn't get checked for valid syntax or anything else at compile time.Fluorometer
This answer has been outdated for the past 2 years. create_function() is now deprecated and should not be used.Cesaro

© 2022 - 2024 — McMap. All rights reserved.