Go Back   SitePoint Forums > Forum Index > Program Your Site > PHP > PHP Application Design
Newsletter FAQ Members List Calendar Mark Forums Read

New to SitePoint Forums? Register here for free!

SitePoint Sponsor
 
Reply
 
Thread Tools Display Modes
Old Jan 17, 2006, 01:58   #1
river_jetties
SitePoint Enthusiast
 
Join Date: Mar 2005
Posts: 64
example of a callback in PHP

I was talking with a friend who does most of his coding in C today and he was mentioning callback functions and said how handy they were. Most definitions on the web seemed pretty vague and most of the code was C code involving pointers. I kinda understood it but was wondering if someone could provide a good example of a callback in PHP.


thanks
river_jetties is offline   Reply With Quote
Old Jan 17, 2006, 02:21   #2
AutisticCuckoo
SitePoint Author
silver trophybronze trophy
 
Join Date: Nov 2004
Location: Åsnorrbodarna
Posts: 12,249
The xml_set_element_handler function, among others, uses callbacks.

Callbacks work like this:
PHP Code:

// This function uses a callback function.

function doIt($callback)
{
    
$data = acquireData();
    
$callback($data);
}


// This is a sample callback function for doIt().
function myCallback($data)
{
    echo
'Data is: ', $data, "\n";
}


// Call doIt() and pass our sample callback function's name.
doIt('myCallback');
__________________
Tommy Olsson The Autistic Cuckoo
Ultimate CSS Reference: book & site
Be like the 22nd elephant with heated value in space – bark!
(gotta end this ding dong for the freesias)
AutisticCuckoo is offline   Reply With Quote
Old Jan 17, 2006, 03:38   #3
michel
SitePoint Zealot
 
Join Date: Jul 2004
Location: The Netherlands
Posts: 170
http://www.php.net/callback
michel is offline   Reply With Quote
Old Jan 17, 2006, 04:33   #4
stereofrog
SitePoint Wizard
 
stereofrog's Avatar
 
Join Date: Apr 2004
Location: germany
Posts: 4,324
callbacks are indeed very handy in procedural programming, but in OOP every call is resolved via pointer and callbacks as such do not make much sense.

PHP Code:

// procedural version

do_something_if_greater($a, $b, $compareFunc) {
   if(
$compareFunc($a, $b) > 0) ...
}

// oo version
do_something_if_greater($a, $b) {
   if(
$a->compare($b) > 0) ...
}
stereofrog is offline   Reply With Quote
Old Jan 17, 2006, 05:13   #5
timvw
SitePoint Addict
 
timvw's Avatar
 
Join Date: Jan 2005
Location: Belgium
Posts: 355
Imho, in the OO version this would couple the compare function to $a..

Here is my version

PHP Code:

if ($comparator->compare($a, $b) > 0) { } 

timvw is offline   Reply With Quote
Old Jan 17, 2006, 07:25   #6
Dr Livingston
Non-Member
 
Join Date: Jan 2003
Posts: 5,788
Callbacks are much like Delegates, more than anything else, from an object oriented context. Do a search for the Delegate on Google if you want to learn more
Dr Livingston is offline   Reply With Quote
Old Jan 17, 2006, 11:09   #7
Findus
SitePoint Addict
 
Join Date: Jun 2004
Location: Norway - Oslo
Posts: 203
A good example of callback is preg_replace_callback , atleast one i've used for complex regexes for text parsing more than once.
Findus is offline   Reply With Quote
Old Jan 17, 2006, 14:42   #8
lastcraft
SitePoint Victim
 
lastcraft's Avatar
 
Join Date: Apr 2003
Location: London
Posts: 2,385
Hi.

The PHP array_map() function is a good one...
PHP Code:

$lower = array('marcus', 'lastcraft');

$capitalised = array_map('uc_first', $lower);
print_r($capitalised);
Basically you blast the whole array with a function in one go, avoiding looping through it. A much more declarative style.

You can write your own call back functions, as the function name is usually passed as a string...
PHP Code:

function process_text($callback, $text) {

    
$replaced = array();
    foreach (
split($text) as $word) {
        
$replaced[] = $callback($word);
    }
    return
implode(' ', $replaced);
}
The OO equvalent is either observer, listener or visitor. The listener...
PHP Code:

class XmlListener {

    function
openTag($tag, $attributes) { ... }
    function
characterData($text) { ... }
    function
closeTag($tag) { ... }
}

class
XmlParser {
    function
parse($xml, $listener) { ... }
}
As the parser reads the text, it sends the results as calls to the listener.

The Observer pattern has a single notify() method rather than a full interface. This is equivalent of the delegate in C# I believe (someone please correct me).

The Visitor pattern involves passing the caller itself in as the listener. Say we are using the parser from an RSS reader...
PHP Code:

class RssReader {

    function
read($url) {
        
$parser = new XmlParser();
        
$parser->parse(file($url), $this);
    }

    function
openTag(...) { ... }
    function
characterData(...) { ... }
    function
closeTag(...) { ... }
}
Here it's especially effective. All the RSS code is in one place, and you don't need any clumsy iterators to move over the data structure (here, text tokens). The Visitor/Listener is often a very nice replacement for the Iterator pattern. It means the mechanics of iteration can stay in the data structure being traversed.

Other languages have different tricks for this. Ruby/Smalltalk/Perl can pass an anonymous block of code (PHP lambda functions are rubbish by comparison). Java can pass an anonymous inner class to avoid polluting the namespace.

For functional languages, callbacks are the foundation. It's how you get the code to the data. You will see things like "list comprehensions" (basically generators and transformers) used in the same context. Python has some support for this, but I'm not a Python developer, so hopefully someone else will elaborate.

yours, Marcus
__________________
Marcus Baker
Testing: SimpleTest, Cgreen, Fakemail
Other: Phemto dependency injector
Books: PHP in Action, 97 things

Last edited by lastcraft; Jan 18, 2006 at 09:16.. Reason: Fixed code glitch
lastcraft is offline   Reply With Quote
Old Jan 17, 2006, 15:33   #9
kyberfabrikken
Community Advisor
silver trophy
 
kyberfabrikken's Avatar
 
Join Date: Jun 2004
Location: Copenhagen, Denmark
Posts: 6,048
And, may I add, javascript excells in this. Since functions are variables, they can be passed around. Gives you plenty of ammunition to shoot yourself, or tools to write some encredibly beautiful code, depending on your skills.
kyberfabrikken is offline   Reply With Quote
Old Jan 17, 2006, 16:02   #10
Ren
SitePoint Wizard
 
Ren's Avatar
 
Join Date: Aug 2003
Location: UK
Posts: 1,020
Yeah, can do nice things with javascript. Even create anonymous objects, with methods

Code:
	var a = [3, 4, 5];
	run = function(a, o) { for(i in a) { o.visit(a[i]); } };
	var o = { total: 0, visit: function(v) { this.total += v; } };
	run(a, o);	
	alert(o.total);
Ren is offline   Reply With Quote
Old Jan 17, 2006, 23:39   #11
dagfinn
SitePoint Guru
 
dagfinn's Avatar
 
Join Date: Jan 2004
Location: Oslo, Norway
Posts: 894
Quote:
Originally Posted by lastcraft
The Visitor pattern involves passing the caller itself in as the listener. Say we are using the parser from an RSS reader...
PHP Code:

class RssReader {

    function
read($url) {
        
$parser = new XmlParser();
        
$parser->parse(file($url), $this);
    }

    function
openTag(...) { ... }
    function
characterData(...) { ... }
    function
closeTag(...) { ... }
}
Doesn't look like a Visitor to me. It may be a Builder, though.

Let me try a canonical form for the client code:
PHP Code:

$rssreader = new RssReader;

$parser = new XmlParser($rssreader);
$parser->parse(file($url));
Now the parser is the Director and the RssReader the Builder. It's surprisingly similar to the example on page 97 of GoF, except that the RTFReader in that example is Director, not Builder.
__________________
Dagfinn Reiersøl
PHP in Action / Blog / Twitter
"Making the impossible possible, the possible easy,
and the easy elegant"
-- Moshe Feldenkrais
dagfinn is offline   Reply With Quote
Old Jan 18, 2006, 00:33   #12
michel
SitePoint Zealot
 
Join Date: Jul 2004
Location: The Netherlands
Posts: 170
Quote:
Originally Posted by kyberfabrikken
And, may I add, javascript excells in this. Since functions are variables, they can be passed around. Gives you plenty of ammunition to shoot yourself, or tools to write some encredibly beautiful code, depending on your skills.
Off Topic:

Regarding functional programming, currying et al, there are some nice examples here as well. JavaScript can be a lot of fun
michel is offline   Reply With Quote
Old Jan 18, 2006, 02:10   #13
stereofrog
SitePoint Wizard
 
stereofrog's Avatar
 
Join Date: Apr 2004
Location: germany
Posts: 4,324
Currying in php

PHP Code:

function curry($func, $arity) {

    return
create_function('', "
        \$args = func_get_args();
        if(count(\$args) >= $arity)
            return call_user_func_array('$func', \$args);
        \$args = var_export(\$args, 1);
        return create_function('','
            \$a = func_get_args();
            \$z = ' . \$args . ';
            \$a = array_merge(\$z,\$a);
            return call_user_func_array(\'$func\', \$a);
        ');
    "
);
}

function
sum($a, $b) {
    return
$a + $b;
}

$sum = curry('sum', 2);
$plus5 = $sum(5);
echo
$plus5(10); // 15

$map = curry('array_map', 2);
$toupper = $map('strtoupper');
$ary = array('haskell', 'curry',);
print_r($toupper($ary)); // HASKELL CURRY
stereofrog is offline   Reply With Quote
Old Jan 18, 2006, 04:59   #14
lastcraft
SitePoint Victim
 
lastcraft's Avatar
 
Join Date: Apr 2003
Location: London
Posts: 2,385
Hi...

Quote:
Originally Posted by dagfinn
Doesn't look like a Visitor to me.
I may have played fast and loose with the term "visitor", but I think it's just about OK. I am using it in this sense...

http://www.rubycentral.com/book/lib_patterns.html

OK, that doesn't explain much, because it just points you at "each". They had a better article somewhere, but I couldn't find it.

I am on a bit of "replace iterator with visitor" mission at the moment at work, so this is on my brain right now.

Quote:
Originally Posted by dagfinn
It may be a Builder, though.
Except that I wasn't even thinking of building anything. I was thinking that the RSS reader would output, say HTML, as the events came in. I didn't even think of building up the document internally. Goes to show how even the simplest snippet of code can be interpreted completely differently without sufficient context. I should have filled out the other methods.

But then, patterns are fuzzy things best deployed subconsciously I think.

yours, Marcus
__________________
Marcus Baker
Testing: SimpleTest, Cgreen, Fakemail
Other: Phemto dependency injector
Books: PHP in Action, 97 things
lastcraft is offline   Reply With Quote
Old Jan 18, 2006, 06:13   #15
dagfinn
SitePoint Guru
 
dagfinn's Avatar
 
Join Date: Jan 2004
Location: Oslo, Norway
Posts: 894
Quote:
Originally Posted by lastcraft
I've never written a line of Ruby code, but I would venture to say that mixins are the Ruby equivalent of Visitor.

Visitor allows you to add new operations to a class without putting them in the class itself.
Quote:
Originally Posted by lastcraft
Except that I wasn't even thinking of building anything. I was thinking that the RSS reader would output, say HTML, as the events came in. I didn't even think of building up the document internally. Goes to show how even the simplest snippet of code can be interpreted completely differently without sufficient context. I should have filled out the other methods.
It's "building" on the output. I admit that's far-fetched, but it may be less far-fetched than calling it Visitor.
Quote:
Originally Posted by lastcraft
But then, patterns are fuzzy things best deployed subconsciously I think.
Yes and no. I sometimes need to think consciously about them to learn. I keep re-reading GoF and learning something new every time.
__________________
Dagfinn Reiersøl
PHP in Action / Blog / Twitter
"Making the impossible possible, the possible easy,
and the easy elegant"
-- Moshe Feldenkrais
dagfinn is offline   Reply With Quote
Old Jan 18, 2006, 07:20   #16
Dr Livingston
Non-Member
 
Join Date: Jan 2003
Posts: 5,788
Quote:
Doesn't look like a Visitor to me.
Looks like a Visitor to me though; It's the RssReader it's self that's acting as the Visitor in this case no?
Dr Livingston is offline   Reply With Quote
Old Jan 18, 2006, 08:59   #17
lastcraft
SitePoint Victim
 
lastcraft's Avatar
 
Join Date: Apr 2003
Location: London
Posts: 2,385
Hi...

Quote:
Originally Posted by dagfinn
Yes and no. I sometimes need to think consciously about them to learn. I keep re-reading GoF and learning something new every time.
I agree for learning, do it consciously. Once absorbed, I don't think about them too much at the text editor.

Ruby Mixins are the metaclass stuff. It's actually the ruby block that's replacing the visitor...
Code:
class IterableFromWithin
    def each()
        names.sort.each { |name|
            yield(name)
        }
    end

    def names
        return ['Marcus', 'Dagfinn']
    end
end

# Pass callback as a block.
IterableFromWithin.new.each { |item| print 'Hello ' + item }
yours, Marcus
__________________
Marcus Baker
Testing: SimpleTest, Cgreen, Fakemail
Other: Phemto dependency injector
Books: PHP in Action, 97 things
lastcraft is offline   Reply With Quote
Old Jan 18, 2006, 09:33   #18
dagfinn
SitePoint Guru
 
dagfinn's Avatar
 
Join Date: Jan 2004
Location: Oslo, Norway
Posts: 894
IMHO it's not a Visitor unless it
  • behaves differently depending on what kind of object it visits, and
  • the visited object has an accept() or similarly generic method to allow the Visitor to replace any method that might have been defined in the visited class.
I don't see that in any of the examples here.

The Visitor pattern is apparently not necessary in Ruby, so I wouldn't expect Ruby experts to explain it well.
__________________
Dagfinn Reiersøl
PHP in Action / Blog / Twitter
"Making the impossible possible, the possible easy,
and the easy elegant"
-- Moshe Feldenkrais
dagfinn is offline   Reply With Quote
Old Jan 18, 2006, 10:45   #19
lastcraft
SitePoint Victim
 
lastcraft's Avatar
 
Join Date: Apr 2003
Location: London
Posts: 2,385
Hi...

Quote:
Originally Posted by dagfinn
behaves differently depending on what kind of object it visits,
It could, but my example was lousy. How about painting a list...
PHP Code:

class ListPainter {

    function
paint($list) {
        
$list->write($this);
    }

    function
setBorder($size) { ... }
    function
paintTitle($title) { ... }
    function
start() { ... }
    function
paintItem($item) { ... }
    function
finish() { ... }
}
Some examples...
PHP Code:

$painter = new PdfListPainter();

$painter->paint($simple_list_with_no_title);
$painter->paint($fancy_nested_list);
Quote:
Originally Posted by dagfinn
the visited object has an accept() or similarly generic method to allow the Visitor to replace any method that might have been defined in the visited class.
The single accept() method is C++ specific, and there the function overloading is used to dispatch. The above uses a second callback instead. I would say that as long as there is a negotiation between the two tightly coupled objects to decide the actual code block to run, then it's pretty visitorish. Otherwise the Visitor pattern isn't really a pattern, it's more of an implementation.

I do agree that I have stretched it as much as the pragmatic programmers have stretched it. I kind of hope the "Prags" win, though. I find the GOF explanation is truly horrible.

yours, Marcus
__________________
Marcus Baker
Testing: SimpleTest, Cgreen, Fakemail
Other: Phemto dependency injector
Books: PHP in Action, 97 things
lastcraft is offline   Reply With Quote
Old Jan 18, 2006, 11:02   #20
jayboots
SitePoint Addict
 
Join Date: Aug 2003
Location: Toronto
Posts: 303
Quote:
Originally Posted by stereofrog
Currying in php

PHP Code:

 function curry($func, $arity) {
    return
create_function('', "
        \$args = func_get_args();
        if(count(\$args) >= $arity)
            return call_user_func_array('$func', \$args);
        \$args = var_export(\$args, 1);
        return create_function('','
            \$a = func_get_args();
            \$z = ' . \$args . ';
            \$a = array_merge(\$z,\$a);
            return call_user_func_array(\'$func\', \$a);
        ');
    "
);
}

function
sum($a, $b) {
    return
$a + $b;
}

$sum = curry('sum', 2);
$plus5 = $sum(5);
echo
$plus5(10); // 15

$map = curry('array_map', 2);
$toupper = $map('strtoupper');
$ary = array('haskell', 'curry',);
print_r($toupper($ary)); // HASKELL CURRY
Very nifty!
jayboots is offline   Reply With Quote
Old Jan 18, 2006, 12:39   #21
Carl
Web developer
 
Carl's Avatar
 
Join Date: Sep 2003
Location: sweden
Posts: 321
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_reas...ork#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.
__________________
FHQK ed UP - It's a really FHQK ed UP world we live in!
Hiveminds Magazine
Search Engine for Drupal
Search Engine for Students
Carl is offline   Reply With Quote
Old Jan 18, 2006, 12:46   #22
Carl
Web developer
 
Carl's Avatar
 
Join Date: Sep 2003
Location: sweden
Posts: 321
About 80% of Drupal is built around this code

PHP 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);
}
__________________
FHQK ed UP - It's a really FHQK ed UP world we live in!
Hiveminds Magazine
Search Engine for Drupal
Search Engine for Students
Carl is offline   Reply With Quote
Old Jan 18, 2006, 13:54   #23
mrsmiley
SitePoint Zealot
 
mrsmiley's Avatar
 
Join Date: Jul 2004
Location: Melbourne
Posts: 173
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.
__________________
http://www.realityedge.com.au
mrsmiley is offline   Reply With Quote
Old Jan 18, 2006, 17:09   #24
Selkirk
SitePoint Guru
 
Join Date: Nov 2002
Posts: 846
Wact does something like this:

PHP Code:

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.

PHP Code:

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

This was done because the syntax seemed clearer. This:
PHP Code:

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

instead of
PHP Code:

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

It would be nice to see the callback in PHP go from a pseudo type to a real class.
__________________
Professional PHP Blog - twitter - about MVC
Selkirk is offline   Reply With Quote
Old Jan 18, 2006, 17:49   #25
kyberfabrikken
Community Advisor
silver trophy
 
kyberfabrikken's Avatar
 
Join Date: Jun 2004
Location: Copenhagen, Denmark
Posts: 6,048
Quote:
Originally Posted by Carl
About 80% of Drupal is built around this code
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.
kyberfabrikken is offline   Reply With Quote
Reply

Bookmarks

« Previous Thread | Next Thread »

Thread Tools
Display Modes

 
Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

 
Forum Jump


All times are GMT -7. The time now is 06:02.


Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.
Copyright 1998-2009, SitePoint Pty Ltd. All Rights Reserved