Implementing advanced add_* function wrappers

add_action() and add_filter() are major functions. However in some scenarios add one more function and hook it somewhere approach gets bulky and inconvenient.

I had determined for myself several use cases that can streamline code with wrappers on top of add_* functions. Things that are better handled with single-liner than extra function each and every time.

  1. Add arbitrary filter return. There is already __return_* functions but they are very limited by definition. Why not just pass what you want returned in filter. Saves from myriad function return_stuff(){return 'stuff';}
  2. Replace X with Y in filter. Saves from myriad function replace_stuff(){return str_replace();}
  3. Add action with arbitrary arguments. Actions fire with arguments, passed in hook. But sometimes you don’t care about that and just want ot run your function with your own arguments at specific hook. Saves from myriad function echo_stuff(){echo 'stuff'}; and more.

So…

  • Are there any other use cases you want and/or use in practice?
  • How would you implement such wrappers? There are a lot of possible approaches (closures, global variables to keep additional arguments, passing objects in callback, etc).

PS I have couple different implementations for (1) and (3) already and (as suggested) will post bits of my code some time later so I don’t spoil the fun. 🙂

Example

Current way:

add_filter('wp_feed_cache_transient_lifetime', 'change_transient_lifetime', 10);

function change_transient_lifetime($lifetime) {

    return 3600;
}

Wrapper:

add_filter_return('wp_feed_cache_transient_lifetime', 10, 3600);

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

For simple cases like quick one-liner returns one should remember that it’s possible to hook an anonymous function directly in the add filter call, e.g:

add_filter('some_filter_hook', function($v){return str_replace('..', '.', $v);});

add_action('some_action_hook', function(){echo '....';});

Method 2

You might want to look into magic class methods and overloading in PHP which will have the function name called passed as a parameter. Then you can make decisions based on the function name like return__some_string I pin-up some exemplary and untested code:

class magiCall {
    public static function __callStatic ( string $name , array $arguments ) {
        // your code here, $name is the function name called.
    }
}

add_filter('hookname', 'magiCall::return__the_string');

I think that example pretty much shows the idea.

This one is PHP 5.3.x you can make this PHP 5.2 compatible by using the __call magic method and if you have a static public function to return the callback, then you might be able to write it comparable simplified. See the link to the php manual above.

Method 3

I had released my code as Advanced Hooks API library.

Some examples:

add_action_with_arguments('test', 'printf', 10, 'boo%s', '<br />');
do_action('test');
// boo

add_filter_return('test2',10,'boo');
echo apply_filters('test2','not boo') . '<br />';
// boo

add_filter_append('test3',10,' and boo');
echo apply_filters('test3','boo') . '<br />';
// boo and boo

add_filter_prepend('test4',10,'boo and ');
echo apply_filters('test4','boo') . '<br />';
// boo and boo

add_filter_replace('test5','boo','you thought...');
echo apply_filters('test5','boo');
// you thought...

Method 4

For simple return values you don’t need a lot of extra functions. There is the handy current_filter(). You can use that inside of your own functions.

Example

<?php # -*- coding: utf-8 -*-
/*
Plugin Name: Filter System From Mail
Description: Sets the WP from mail address to the first admin’s mail and the from name to blog name.
Version:     1.1
Author:      Thomas Scholz
Author URI:  http://toscho.de
License:     GPL
*/

if ( ! function_exists( 'filter_system_from_mail' ) )
{
    /**
     * First admin's e-mail address or blog name depending on current filter.
     *
     * @return string
     */
    function filter_system_from_mail()
    {
        return get_option( 'wp_mail_from' == current_filter()
            ? 'admin_email' : 'blogname' );
    }

    add_filter( 'wp_mail_from',      'filter_system_from_mail' );
    add_filter( 'wp_mail_from_name', 'filter_system_from_mail' );
}

Or … did I misread your question?

Method 5

Ad anonymous or lambda functions) In every case I would avoid these, as you won’t know the name or get a pretty cryptic output if you inspect a hook later. Debugging would be a pain.

Ad your A) The approach looks nice and offers a pretty simple API (+1).

Ad @hakre A) I use pretty much the same function[1] in my frameworks core – extended by “set_” & an exception – to set and get variables on the fly. This way I only have to extend this class with a child class that looks like a “variable only”-interface (those which shall be available). It’s a nice and organized approach that only keeps stuff in the files that serves a single task. With something like this you can maybe avoid writing specific functions for each and every taks like “_replace”, etc.
[1] Code – I run this all the time so you can call it “tested – and works”

/**
 * Magic getter/setter method
 * Guesses for a class variable & calls/fills it or throws an exception.
 * Note: Already defined methods override this method.
 * 
 * Original @author Miles Keaton <<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="335e5a5f5640585652475c5d73545e525a5f1d505c5e">[email protected]</a>> 
 * on {@link http://www.php.net/manual/de/language.oop5.overloading.php#48440}
 * The function was extended to also allow 'set' tasks/calls and throws an exception.
 * 
 * @param (string) $name | Name of the property
 * @param unknown_type $args | arguments the function can take
 */
function __call( $name, $args )
{
    $_get = false;

    // See if we're calling a getter method & try to guess the variable requested
    if( substr( $val, 0, 4 ) == 'get_' )
    {
        $_get = true;
        $varname = substr( $val, 4 );
    }
    elseif( substr( $val, 0, 3 ) == 'get' )
    {
        $_get = true;
        $varname = substr( $val, 3 );
    }

    // See if we're calling a setter method & try to guess the variable requested
    if( substr( $val, 0, 4 ) == 'set_' )
    {
        $varname = substr( $val, 4 );
    }
    elseif( substr( $val, 0, 3 ) == 'set' )
    {
        $varname = substr( $val, 3 );
    }

    if ( ! isset( $varname ) )
        return new Exception( sprintf( __( "The method %1$s doesn't exist" ), "<em>{$val}</em>" ) );

    // Now see if that variable exists:
    foreach( $this as $class_var => $class_var_value )
    {
        if ( strtolower( $class_var ) == strtolower( $varname ) )
        {
            // GET
            if ( $_get )
            {
                return $this->{$class_var};
            }
            // SET
            else 
            {
                return $this->{$class_var} = $x[0];
            }
        }
    }

    return false;
}


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x