Example of a callback in PHP

If you want to look at PHP callbacks in the wild the you should take a look at the Drupal CMS code. It is built entirely on the callback functionallity for everything from access control, templating and the use of hooks and overriding functions. A few months ago I wrote a couple of shorties about the why’s and do nots of using PHP callbacks.

http://www.hivemindz.com/drupal_reasons_why_it_does_not_make_a_good_development_framework#comment-82
http://www.hivemindz.com/aggregation_vs_callback

I was waiting for more info and examples to come to the PHP5 manual on aggregate functions before writing more but they are still not there.

About 80% of Drupal is built around this code


function theme() {
  global $theme;
  global $theme_engine;

  $args = func_get_args();
  $function = array_shift($args);

  if (($theme != '') && function_exists($theme .'_'. $function)) {
    // call theme function
    return call_user_func_array($theme .'_'. $function, $args);
  }
  elseif (($theme != '') && isset($theme_engine) && function_exists($theme_engine .'_'. $function)) {
    // call engine function
    return call_user_func_array($theme_engine .'_'. $function, $args);
  }
  elseif (function_exists('theme_'. $function)){
    // call Drupal function
    return call_user_func_array('theme_'. $function, $args);
  }
}

/**
 * Return the path to the currently selected theme.
 */
function path_to_theme() {
  global $theme;

  $themes = list_themes();

  return dirname($themes[$theme]->filename);
}


I’m suprised it took a mention of Drupal to bring up the call_user_func set of functions. I use callbacks in my template class to format column output for tabular data. An example would be something like date formatting. ddmmyy vs mmddyy vs … you get the point. If I have a callback function created to handle the date formatting, I can tell my code to that the callback should be run without running it until its needed if that makes sense. It essentially means one function call in my code (make the code cleaner in this instance), but allows me to make smarter decisions at runtime based on user preferences without cluttering up my code. I know which function I want to run, but I dont necessarily know what parameters I want to pass through to it.

All said and done, its just another method of accomplishing the same thing. There are only a few problems I think that can only be solved with callbacks as opposed to just standard procedural/OO code. I think php its main strength is giving coders the ability to couple a php function with a c function in a php extension. As mentioned above with the xml functions, you can feed it the name of the php function you want to run every time the appropriate point/event in the extension code is triggered. This isn’t possible (at least that I’m aware of) without callbacks.

Marcus, your guess of C# delegates being like callbacks is close. Delegates are closer to function pointers. You have to give them an address for the function you want to call as opposed to calling them by name. For the sake of arguement though, in php, the difference is probably very minimal. In fact in the extension to php callback scenario I described, it more or less is behaving like a delegate, just without the php land code having to pass through the memory address. Zend Engine probably does that internally though, so I would guess it gets converted to a function pointer/delegate on throughput.

Wact does something like this:


class MySubject {
    var $onExecuteListeners;

    function registerOnExecuteListener(&$listener) {
        if (!isset($this->onExecuteListeners)) {
            $this->onExecuteListeners =& new MulticastNotifier();
        }
        $this->onExecuteListeners->addListener($listener);
    }

    function onExecute(&$request, &$responseModel) {
        if (isset($this->onExecuteListeners)) {
            return $this->onExecuteListeners->invokeChainArray(
                array(&$this, &$request, &$responseModel));
        }
    }
}

(This example features lazy instantiation of the Notifier.)

The Notifier objects (UnicastNotifier and MulticastNotifier) support three styles:

  1. invokeAll - All callbacks are invoked. return values are ignored.
  2. invokeChain - Each callback is invoked. When the first non-NULL return value is encountered, invocation of the chain ceases and that value is returned.
  3. invokeFilter - All callbacks are invoked. The return value of each invocation becomes the parameter for the next. The final value is returned.

The Callback object is mostly a wrapper for call_user_func_array with an invoke method.


$callback->invoke('param1', 'param2', ...);

This was done because the syntax seemed clearer. This:


$subject->registerOnExecuteListener(new Callback($obj, 'method'));

instead of


$subject->registerOnExecuteListener(array($obj, 'method'));

It would be nice to see the callback in PHP go from a pseudo type to a real class.

Arg … Not to smite drupal or anything, but wouldn’t that become very unmanagable in no time ? You’ll have to refer to a manual to figure out exactly what a function like theme() does … no hints from the argumentlist, and rich use of globals.

thanks for the responses. i’ve used array_map before and that makes sense but I’ll have to sift through some of the other ones. things due; brain tired.

-j

Well, uh, yeah…so what’s your point? :wink:

No, but seriously you are correct. When you develop for Drupal it takes about a year to get the code and even then you have to spend a lot of time in the API docs or just grepping the source code inorder to develop new modules or modify stuff. But this is the “Drupal Way” as they so eloquently put it. The use of PHP calbacks is their mainstay and defense against using OOP.

Wordpress’s plugin system works in a similar fashion, albeit in a somewhat less OOP way. Each plugin in the plugin directory gets included, and each has code to register its callback functions into the notification system.

You know, whenever I start thinking about All The Stuff I Wish PHP Had, I end up transforming it into Ruby.

Yes, this is something I wish Drupal had rather than having every module load all of it functions everytime. Wordpress is nice like that.

I suffer from the exact same symptom. So what we’re going to need is mod_ruby on 90% of webhosts, and we can live in peace once again.

:slight_smile: Came across this today. Sometimes I wish you could do more at runtime. Perhaps a function data type in PHP 6? :wink:

I hope you don’t take it seriously. php is perhaps the least suitable language for functional programming.

I didn’t take it seriously at all, i just happened to find some more PHP experiments. Amusing, that’s all. I’m not sure if I was kidding about the function data type though. I don’t have a clue if it’s feasible for PHP to have this implemented. You would probably need namespaces then as well…