A commonly used technique when programming is to use callbacks. A callback is a piece of code that is passed to a function that is later executed by that function. In this article I will show you how to define and call callbacks in PHP, as well as a practical example of how callbacks can be useful.
To demonstrate how callbacks can be beneficial, assume we have a PHP script that when called will download an RSS feed from 10 different web sites. If it takes 2 seconds to download from each site, it will take 20 seconds to download from all sites.
The code to achieve this may look something like the following code. I've added a called to sleep() to demonstrate how slow this script may be.
Listing 1 listing-1.php
// this function simulates downloading data from a site function downloadFromSites($sites) { $ret = array(); foreach ($sites as $site) { sleep(2); // write the data for this site to return array $ret[] = array( 'site' => $site, 'data' => 'downloaded data here' ); } return $ret; } // imaginary list of sites to download from $sites = array( 'http://www.example1.com', 'http://www.example2.com' // more sites... ); // start downloading and store the return data $data = downloadFromSites($sites); // output the return data foreach ($data as $row) { echo sprintf("Finished downloading from %s\n", $row['site']); }
If you run this code, you'll notice that it hangs until all sites have been completed. You then have access to all of the return data at once. The following figure shows the workflow of this script.
A far better solution would be to return newly-downloaded data as it becomes available. Of course, you could hard-code the required functionality in the
downloadFromSites()
function, but doing so makes it extremely difficult to reuse the code and use the returned data in a different way should the need arise.
The following diagram shows the workflow of the previous function modified to use callbacks. In this case, the callback function is called every time a single site has completed downloading.
PHP makes using callbacks extremely simple using the helper functions is_callable() and call_user_func(). There are three types of callbacks that can be used:
- A standard PHP function. This is passed as a string such as
'callbackName'
- A static method of a class. This is passed as an array with the the class name as the first element and the method name as the second argument. For example,
array('MyClass', 'MyCallback')
- A method of an object. This is also passed as an array, but instead of the class name being the first element, the object is. For example,
array($obj, 'MyCallback')
The following listing demonstrates how to define each of the types of callbacks and then how to reference each of them.
Listing 2 listing-2.php
// Callback type 1: normal function function myCallback() { } $callback = 'myCallback'; // Callback type 2: static method class MyClass { public static function MyCallback() { } } $callback = array('MyClass', 'MyCallback'); // Callback type 3: object method class AnotherClass { public function MyCallback() { } } $obj = new AnotherClass(); $callback = array($obj, 'MyCallback');
Note: The static method callback is defined as
static
.
To test if a callback can be used you can use the is_callable() function. This function accepts the callback as its only argument and returns true if it's a valid callback. You can then call the callback usingcall_user_func(). The callback is passed as the first argument, and if your callback accepts any arguments you can include them as the second and subsequent arguments.
The following code demonstrates how this would be typically used. This code ensures a callback can be called, then calls it with a single argument. This script outputs
Hello
.
Listing 3 listing-3.php
function myCallback($arg1) { echo $arg1; } $callback = 'myCallback'; if (is_callable($callback)) { call_user_func($callback, 'Hello'); }
Note: In this particular example you could simply call
$callback('Hello')
. The problem with this is that it will only work for normal functions - you can't use a class method as a callback using this technique.
Returning to our previous example, we can now alter the
downloadFromSites()
function so it accepts a callback as the second argument. Now in addition to returning all of the data once all sites have been processed, the callback will be called after each site is processed.
The callback will accept the site name as its first argument and the downloaded data as the second argument.
Listing 4 listing-4.php
// this function simulates downloading data from a site function downloadFromSites($sites, $callback = null) { $ret = array(); foreach ($sites as $site) { sleep(2); $data = 'downloaded data here'; // check if the callback is valid if (is_callable($callback)) { // callback is valid - call it with given arguments call_user_func($callback, $site, $data); } // write the data for this site to return array $ret[] = array( 'site' => $site, 'data' => $data ); } return $ret; } // define a fictional class used for the callback class MyClass { // this is the callback method public static function downloadComplete($site, $data) { echo sprintf("Finished downloading from %s\n", $site); } } // imaginary list of sites to download from $sites = array( 'http://www.example1.com', 'http://www.example2.com' // more sites... ); // start downloading and store the return data downloadFromSites($sites, array('MyClass', 'downloadComplete')); // we don't need to loop over the return data // now since the callback handles that instead
When you run this code, you will see that the "finished downloading" message is displayed as each site is completed rather than all at the end.