o.php

Callbacks

Callbacks in PHP

Callbacks have a wide variety of uses. In PHP functions like call_user_func() or usort() accept user-defined callback functions as a parameter. Callback functions can not only be simple functions, but also object methods, including static class methods. However they generally require the use of special functions employing convoluted signatures or are simply a string name.

oCallback

In order to follow the examples in this section you will need to require() the Object.php class and the Callback.php class. These files are include as part of the oLite.php script.

<?php
require('path/to/Object.php');
require(
'path/to/Callback.php');
?>

The static method Object::method() lets you pass in the string name of a function and returns a Callback object for that function. The Callback object can then be triggered by calling the method call(). The string can be any PHP or user-defined function within the running programs scope.

<?php
$time 
Object::method('time');
$time->call(); // (int)1265171032
?>

The call() method will take any arguments it is given and pass them on to the underlying function defined in the Callback object. This is useful as it lets you pass control to the code calling call().

<?php
$ucwords 
Object::method('ucwords');
$ucwords->call('arthur conan doyle'); // Arthur Conan Doyle
?>

So far we have just called functions, not very ground breaking, so what if we want to call methods on objects?

Methods on instantiated objects can be called by passing in a reference to the target object as the first argument of Object::method(), followed by the method name you wish to call. The method name, like when we called a function, must be a string.

<?php
// Create an object with the property 'first_name'
$obj Object::create(array('first_name'=>'Arthur'));

// Create a callback to the 'getFirst_name' method
$name Object::method($obj'getFirst_name');
$name->call(); // Arthur
?>

Finally you can call Static methods by providing the string name of the target class and the string name of the Static method you wish to trigger on that class.

<?php
$method 
Object::method('Object''method');
$name   $method->call('ucfirst');
$name->call('tolkien'); // Tolkien
?>

Note: you cannot name your Static class the same as an existing function.

The last example was a bit convoluted but details the different ways in which the Callback object can be used.

PHP 5.3.x

As of PHP 5.3 there is no need to explicitly call the call() method. You can simply treat the variable as a function.

<?php
$method 
Object::method('Object''method');
$name   $method('ucfirst');
$name('tolkien'); // Tolkien
?>

This makes using Callbacks in PHP a clean and enjoyable experience.

Methods

The Object class provides the ability to attach Callback objects as properties or methods. Depending on how you attach the Callback to the Object will dictate its behavior when eventually called.

Attaching a Callback as a method on an Object is done with a simple assignment statement. Each time the method is called, the Callback object in turn calls its underlying function or method. As you can see in the example below the Callback method looks and behaves the same as a declared method.

<?php
$obj 
Object::create();
$obj->upperCaseWords Object::method('ucwords');
$obj->upperCaseWords('j r r tolkien'); // J R R Tolkien
?>

The subtle difference to keep in mind is that methods are added by assignment, not as an argument to a setter, which we will see next.

Properties

In the following example the function time() is attached to the Object as if it was a property. We know this because we explicitly call a setter method and pass the Callback object as the only argument.

The first call to the getter method getCurrentTime() will return the result of the time() function. This result then becomes the properties value. Any subsequent calls to the getCurrentTime() method, or via accessing the property directly, will return the same value integer, not a new current time.

<?php
$obj 
Object::create();
$obj->setCurrentTime(Object::method('time'));
$obj->getCurrentTime(); // (int)1265171032
?>

When using Callbacks as properties they must be provided as an argument to a setter method. The Callback is only called once, at which point the result of the Callback becomes the properties new value.

Arguments

So far we have been using the Callback object by providing arguments at the time of calling. Here we will explore providing them at creation time as well as at calling time. By mixing these two methods we can create useful and powerful Callback objects.

In this first example we implement the Factory Pattern when creating a Callback object which constructs Author objects. By passing in an array() of properties when we call the Callback method we create a pre-populated Author object.

<?php
// First we create our factory
$factory Object::create();
$factory->build Object::method('Object''create''Author');

// Then we get the factory to build an 'Author'
$author $factory->build(array('first_name'=>'John'));
$author->getFirst_name(); // Johnprint_r($author);
$author->toString();      // Author
?>

In the following example we create a method on an object that when called returns the current date in the format 'Day, Date Month Year'. However the format of the date may change through the course of the programs execution so we have to make it dynamic.

You will note that the arguments themselves are Callback objects. This allows those arguments to recalculate their values each time the Callback is called because the argument Callback objects are also called.

<?php
// Create our source object
$src Object::create(array('format'=>'D, d M Y H:i:s'));

// Our new object inherits from $src
$obj Object::create($src);

// Now we set our Callbacks
$obj->getCurrentDate Object::method('date',
    
Object::method($obj'getFormat'),
    
Object::method('time')
);

$obj->getCurrentDate();      // Tue, 02 Feb 2010 23:26:10
$src->setFormat('D, d M Y'); // Change the date format in the $src
$obj->getCurrentDate();      // Tue, 02 Feb 2010
?>

When the date format is changed in the source object it is immediately reflected in the getCurrentDate() methods output.

You can touch it, smell it, taste it so sweet - Mike Patton