SitePoint Sponsor

User Tag List

Page 1 of 2 12 LastLast
Results 1 to 25 of 33

Hybrid View

  1. #1
    SitePoint Enthusiast
    Join Date
    Mar 2005
    Posts
    64
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    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

  2. #2
    SitePoint Author silver trophybronze trophy

    Join Date
    Nov 2004
    Location
    Ankh-Morpork
    Posts
    12,158
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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'); 
    Birnam wood is come to Dunsinane

  3. #3
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    The Netherlands
    Posts
    170
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

  4. #4
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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) ...


  5. #5
    SitePoint Addict timvw's Avatar
    Join Date
    Jan 2005
    Location
    Belgium
    Posts
    354
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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) { } 

  6. #6
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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

  7. #7
    SitePoint Zealot
    Join Date
    Jun 2004
    Location
    Norway - Oslo
    Posts
    198
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    A good example of callback is preg_replace_callback , atleast one i've used for complex regexes for text parsing more than once.

  8. #8
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    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
    Last edited by lastcraft; Jan 18, 2006 at 11:16. Reason: Fixed code glitch
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  9. #9
    SitePoint Guru dagfinn's Avatar
    Join Date
    Jan 2004
    Location
    Oslo, Norway
    Posts
    894
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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

  10. #10
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    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

  11. #11
    SitePoint Guru dagfinn's Avatar
    Join Date
    Jan 2004
    Location
    Oslo, Norway
    Posts
    894
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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

  12. #12
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    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

  13. #13
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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.

  14. #14
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    The Netherlands
    Posts
    170
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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

  15. #15
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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);

  16. #16
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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 

  17. #17
    SitePoint Addict
    Join Date
    Aug 2003
    Location
    Toronto
    Posts
    300
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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!

  18. #18
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    The Netherlands
    Posts
    170
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by stereofrog
    Currying in php
    Came across this today. Sometimes I wish you could do more at runtime. Perhaps a function data type in PHP 6?
    Last edited by michel; Jan 22, 2006 at 14:09.

  19. #19
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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?

  20. #20
    Web developer Carl's Avatar
    Join Date
    Sep 2003
    Location
    sweden
    Posts
    320
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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.

  21. #21
    Web developer Carl's Avatar
    Join Date
    Sep 2003
    Location
    sweden
    Posts
    320
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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);


  22. #22
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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.

  23. #23
    Web developer Carl's Avatar
    Join Date
    Sep 2003
    Location
    sweden
    Posts
    320
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    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.
    Well, uh, yeah....so what's your point?

    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.

  24. #24
    SitePoint Enthusiast mrsmiley's Avatar
    Join Date
    Jul 2004
    Location
    Melbourne
    Posts
    96
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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.

  25. #25
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •