2

I was updating the codex page example for action hooks, while playing around to get some reuseable functions done (originally for some Q over here @WA). But then i ran into a problem i wasn't aware of before: After hooking into a function to modify the output of a variable, i can't anymore decide if i want to echo the output or just return it.

The Problem: I can modify variables that i pass to a do_action hook with a callback function. Everything i modify/add with the variable is only available inside the callback function, but not after the do_action call inside the original function.

FOR YOUR PLEASURE: I reworked it to an working example, so you can just copy/paste it in some functions.php file and see/test the problem without any efforts.

Example:

Handle data function

This is the handle_data_fn. A little over documented as it should serve as a guide on how to parse and merge default with other arguments.
You can see the problem at the end of the function right after the do_action() call.

/**
 * The "global" data handle function
 * 
 * This function can serve a lot of different purposes.
 * Incl. merging db values from an options entry with input arguments.
 * 
 * Throws a fully translateable Error if no database option name was specified.
 * Tells you from which file the Error was triggered and in which line you should search it.
 * Also tells you the "hook_data_handle_$args['UID']" name of the action hook where the Error occured. 
 * 
 * Uses of external function calls in order of their appearance inside the function:
 * @uses:   isset()         - @link: http://php.net/manual/en/function.isset.php
 * @uses:   wp_die()        - @link: http://codex.wordpress.org/Function_Reference/wp_die
 * @uses:   printf()        - @link: http://php.net/manual/en/function.printf.php
 * @uses:   _e()            - @link: http://codex.wordpress.org/Function_Reference/_e (i18n function)
 * @uses:   apply_filters() - @link: http://codex.wordpress.org/Function_Reference/apply_filters
 * @uses:   wp_parse_args() - @link: http://codex.wordpress.org/Function_Reference/wp_parse_args
 * @uses:   extract()       - @link: http://php.net/manual/en/function.extract.php
 * @uses:   get_option()    - @link: http://codex.wordpress.org/Function_Reference/get_option
 * @uses:   do_action()     - @link: http://codex.wordpress.org/Function_Reference/do_action
 * @uses:   return          - @link: http://php.net/manual/en/function.return.php
 * 
 * @param:  (array) mixed $args | array of arguments - `$args['UID']` is always a must have
 * @param:  $database           | true if you want to get and modify some db-option - `$args['name']` then is a must have
 * @param:  $output             | result from the function - @internal: should not get set
 */
    function handle_data_fn( $args = array() )
    {
        // abort if we ain't got some unique identifier as argument
        if ( !isset( $args['UID']) )
            return;

        // Trigger Error if an option should get retrieved from the database, 
        // but no option name was specified
        if ( !isset( $args['name'] ) )
            wp_die( 
                printf(
                    _e(
                        'You have to specify the "name" of a db-entry as argument inside a handle_data_fn at for the action hook: %1$s.'."\n".
                        'Error triggered inside: file name %2$s (line number %3$s)'
                     )
                    ,'some_textdomain'
                 )
                ,'hook_data_handle_'.$args['UID'].'`'
                ,__FILE__
                ,__LINE__
            );


        // setup default arguments
        $defaults = ( 
            array(
                 'UID'      => null     // DB/css: #id | used to identify the data inside your database - $name[$ID] - can be used as css #id too
                ,'name'     => null     // name of DB field, should be a constant when fn get's triggered - just here for completeness, not needed
                ,'args'     => array(   // $arguments the function can handle - put default arguments in here as array data
                    // 'classes'    => null     // css: .class - example
                 )
                ,'output'   => ''
                ,'echo'     => false    // if you want to echo the output or just save it in a var for later modifying
            ) 
        );
        // filter defaults
        $defaults = apply_filters( 'filter_defaults_'.$args['UID'], $defaults ); 

        // merge defaults with input arguments
        $args = wp_parse_args( $args, $defaults );
        extract( $args, EXTR_SKIP );

        // in case you want to call the global function again, 
        // but for some reason need to modify the merged result of defaults & arguments
        $args = apply_filters( 'filter_args_'.$args['UID'], $args );

        // retrieve the database option
        if ( isset( $args['name'] ) )
            $options = get_option( $args['name'] );


        # >>> start building $output

            // if true, echo the $output
            if ( isset( $args['echo'] ) )
            {
                if ( $args['echo'] === true )
                {
                    // do stuff here - your argument is the initial array
                    do_action( 'hook_data_handle_'.$args['UID'], $args, $options );

                    // Test output inside handle_fn: 
                    echo '<pre>From inside handle_fn: ';
                        print_r($args);
                    echo '</pre>';

                    return;
                }

            }

            // else just return the $output
            // HOW CAN I NOT ECHO THE DATA HERE. 
            // STORING THE do_action return VALUE DOESN'T WORK.
            // NEITHER DOES JUST returnING IT INSIDE THE CALLBACK FN 'modify_args_fn' BELOW
            do_action( 'hook_data_handle_'.$args['UID'], $args, $options );
            return;

        # <<< end building $output
    }

Callback functions

Those are used to a) build the initial array and b) modify the output.

/**
 * EXAMPLE for how to add initial data to the handle_data_fn function.
 * 
 * @param (array) mixed $args
 */
    function build_args_fn ( $args ) {
        // build initial array
        $args = array(
             'UID'      => 'whatever_UID'
            ,'name'     => 'some_options_name'
            ,'args'     => array(
                'class' => 'example-wrap'
             )
            ,'echo'     => true
        );

        handle_data_fn( $args );
    }
    // 'some_hook' is some hook in a template file where the output should get echoed.
    add_action( 'some_hook', 'build_args_fn', 0, 1 );

/**
 * EXAMPLE for how to add content and modify the options from the DB inside a handle_data_fn function.
 * 
 * @param (array) mixed $args
 * @param (array) mixed $options    | db-options retrieved inside the oxo_parse function
 */
    function modify_args_fn ( $args, $options )
    {
        $args['output'] .= '<div class="container">';
        $args['output'] .= '<div class="'.$args['args']['class'].'">';
        $args['output'] .= $options;
        $args['output'] .= '</div>';
        $args['output'] .= '</div>';

        // Test output inside callback fn
        echo '<pre>From inside callback: ';
            print_r($args);
        echo '</pre>';

        // HERE'S THE PROBLEM. I CAN'T SIMPLE return IT, BECAUSE IT WOULDN'T BE AVAILABLE INSIDE MY 'data_handle_fn'.
        # return $args['output'];
        // I HAVE TO PRINT IT TO GET SOME OUTPUT. THE $args['output'] IS NOT AVAILABLE INSIDE THE 'handle_data_fn' AFTER THE do_action CALL.
        return print $args['output'];
    }
    // Name of the hook comes from 'UID' argument in 'build_args_fn' combined with 'hook_data_handle_'
    add_action ( 'hook_data_handle_whatever_UID', 'modify_args_fn', 10, 2 );

Thanks for any info & help about this issue.

5
  • 1
    Actions aren't for modifying data, they're points in time for producing additional output, filters are for modifying data before a specific thing happens to them. If the hook wants to allow data to be changed, it's typically a filter, if the hooks wants to allow extra output to occur in a specific place, it's an action.... generally
    – t31os
    Commented Mar 20, 2011 at 11:01
  • 1
    @t31os - I frequently use actions to modify data in global vars because there is often no other way to accomplish a specific client requirement. JMTCW. Commented Mar 20, 2011 at 21:33
  • 1
    @Mike - Sure you can use it for changing globals, i was just saying that generally actions aren't for changing data, though in some cases they may be.
    – t31os
    Commented Mar 21, 2011 at 9:20
  • 1
    @t31os - Agreed. Only reason I mentioned is some people take things they read from people like you they respect as dogma. Whereas you know when to violate the guideline, many will adhere to it is as a rule. Commented Mar 22, 2011 at 23:49
  • @MikeSchinkel @t31os I upvoted both your comments. Good that this its mentioned here (that they generally aren't for changing data) below this Q. The Q/function itself is abstract enough to cause some confusion. (From a look at the core function code itself there's no real difference between do_action and apply_filters.)
    – kaiser
    Commented Mar 23, 2011 at 1:46

1 Answer 1

3

If you want to return the data, the best solution is to use apply_filters() and instruct the callback to always return:

$output = apply_filters( 'hook_data_handle_'.$args['UID'], $args, $options );

if ( $args['echo'] )
  echo $output;
else
  return $output;

A less elegant solution would be to use output buffering.

5
  • So you'd suggest to replace the second do_action with apply_filters? This would force me to drop the echo argument and decide if i want to use the filter or the action call, which i woul call "less elegant" too :). Further more i wouldn't know how to pass the $args and the $options into one filter and work with it. Could you update your answer & provide some (short) example? Thanks.
    – kaiser
    Commented Mar 19, 2011 at 17:45
  • @Kaiser - Take a look at wordpress.stackexchange.com/questions/12470/… it has a simple example of working with apply_filters.
    – Bainternet
    Commented Mar 19, 2011 at 18:11
  • @Bainternet: Sry, but that's no answer to the Q. That's some explanation of how hooks & filters work. I already know how the concept works. - If you want to tell me about some alternating concept: feel free to teach me.
    – kaiser
    Commented Mar 20, 2011 at 2:56
  • Updated answer.
    – scribu
    Commented Mar 20, 2011 at 10:54
  • This was how i first had it, before trying to use a hook and it wasn't working. I wanted to tell you this, but i just re-tried it and it worked. I guess i had misspelled something or similar stupid stuff. So, thanks for your time and answer. +1
    – kaiser
    Commented Mar 20, 2011 at 13:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.