WordPress.org

Make WordPress Core

Ticket #14671: 14671.4.diff

File 14671.4.diff, 7.5 KB (added by westonruter, 9 years ago)

Patch which allows omission 4th argument by using PHP Reflection to look up the number of accepted parameters from the supplied function

  • wp-includes/plugin.php

     
    5959 * @param string $tag The name of the filter to hook the $function_to_add to.
    6060 * @param callback $function_to_add The name of the function to be called when the filter is applied.
    6161 * @param int $priority optional. Used to specify the order in which the functions associated with a particular action are executed (default: 10). Lower numbers correspond with earlier execution, and functions with the same priority are executed in the order in which they were added to the action.
    62  * @param int $accepted_args optional. The number of arguments the function accept (default 1).
     62 * @param int $accepted_args optional. The number of arguments the function accept (default: number of required arguments introspected from $function_to_add via the PHP Reflection API).
    6363 * @return boolean true
    6464 */
    65 function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
     65function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = null) {
    6666        global $wp_filter, $merged_filters;
    6767
    6868        $idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
     
    167167                $args = func_get_args();
    168168
    169169        do {
    170                 foreach( (array) current($wp_filter[$tag]) as $the_ )
     170                foreach( (array) current($wp_filter[$tag]) as $the_ ) {
    171171                        if ( !is_null($the_['function']) ){
     172                                $accepted_args = _wp_get_hook_handler_accepted_arg_count($the_['function'], $the_['accepted_args']);
    172173                                $args[1] = $value;
    173                                 $value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $the_['accepted_args']));
     174                                $value = call_user_func_array($the_['function'], array_slice($args, 1, $accepted_args));
    174175                        }
     176                }
    175177
    176178        } while ( next($wp_filter[$tag]) !== false );
    177179
     
    226228
    227229        do {
    228230                foreach( (array) current($wp_filter[$tag]) as $the_ )
    229                         if ( !is_null($the_['function']) )
    230                                 $args[0] = call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
     231                        if ( !is_null($the_['function']) ) {
     232                                $accepted_args = _wp_get_hook_handler_accepted_arg_count($the_['function'], $the_['accepted_args']);
     233                                $args[0] = call_user_func_array($the_['function'], array_slice($args, 0, $accepted_args));
     234                        }
    231235
    232236        } while ( next($wp_filter[$tag]) !== false );
    233237
     
    254258 * @param string $tag The filter hook to which the function to be removed is hooked.
    255259 * @param callback $function_to_remove The name of the function which should be removed.
    256260 * @param int $priority optional. The priority of the function (default: 10).
    257  * @param int $accepted_args optional. The number of arguments the function accepts (default: 1).
    258261 * @return boolean Whether the function existed before it was removed.
    259262 */
    260263function remove_filter( $tag, $function_to_remove, $priority = 10 ) {
     
    328331 * @param string $tag The name of the action to which the $function_to_add is hooked.
    329332 * @param callback $function_to_add The name of the function you wish to be called.
    330333 * @param int $priority optional. Used to specify the order in which the functions associated with a particular action are executed (default: 10). Lower numbers correspond with earlier execution, and functions with the same priority are executed in the order in which they were added to the action.
    331  * @param int $accepted_args optional. The number of arguments the function accept (default 1).
     334 * @param int $accepted_args Optional. The number of arguments the function accept (default: number of required arguments introspected from $function_to_add via the PHP Reflection API).
    332335 */
    333 function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
     336function add_action($tag, $function_to_add, $priority = 10, $accepted_args = null) {
    334337        return add_filter($tag, $function_to_add, $priority, $accepted_args);
    335338}
    336339
     
    402405
    403406        do {
    404407                foreach ( (array) current($wp_filter[$tag]) as $the_ )
    405                         if ( !is_null($the_['function']) )
    406                                 call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
     408                        if ( !is_null($the_['function']) ){
     409                                $accepted_args = _wp_get_hook_handler_accepted_arg_count($the_['function'], $the_['accepted_args']);
     410                                call_user_func_array($the_['function'], array_slice($args, 0, $accepted_args));
     411                        }
    407412
    408413        } while ( next($wp_filter[$tag]) !== false );
    409414
     
    483488
    484489        do {
    485490                foreach( (array) current($wp_filter[$tag]) as $the_ )
    486                         if ( !is_null($the_['function']) )
    487                                 call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
     491                        if ( !is_null($the_['function']) ) {
     492                                $accepted_args = _wp_get_hook_handler_accepted_arg_count($the_['function'], $the_['accepted_args']);
     493                                call_user_func_array($the_['function'], array_slice($args, 0, $accepted_args));
     494                        }
    488495
    489496        } while ( next($wp_filter[$tag]) !== false );
    490497
     
    787794                return $function[0].$function[1];
    788795        }
    789796}
     797
     798/**
     799 * Obtain the argument count for a hook callback (function/method/closure) via Reflection
     800 *
     801 * It used to be that if hook (filter/action) is called with more than one
     802 * argument, you would have to supply a 4th argument to
     803 * add_action()/add_filter() in order for those extra arguments to be passed
     804 * into the function. This is painful because it usually violates DRY
     805 * principles since the number of arguments that a function takes is already
     806 * defined when the function was defined and so it should already be known. It
     807 * is also painful to have to pass in the number of arguments because
     808 * add_action() and add_filter() take positional arguments and so you always
     809 * have to define the 3rd argument ($priority) in order to be able to specify
     810 * the 4th ($accepted_args). PHP now has an always-included Reflection
     811 * extension that allows it to introspect to programmatically determine the
     812 * number of arguments that a function or method takes. In older versions of
     813 * WordPress when support for PHP 4 was required, WordPress was not able to
     814 * take advantage of Reflection because it was first introduced in PHP 5. But
     815 * now that PHP 5.2 is required, we can take advantage of Reflection and
     816 * simplify the calls to add_action() and add_filter().
     817 *
     818 * This function is a helper that is re-used in do_action(), apply_filters(),
     819 * and do_action_ref_array(). If the accepted_args has been supplied when
     820 * add_action()/add_filter() was called (and it is not the default of null)
     821 * then it will return that instead of trying to introspect for the number of
     822 * args.
     823 *
     824 * @package WordPress
     825 * @subpackage Plugin
     826 * @access private
     827 * @since 3.6
     828 * @link http://core.trac.wordpress.org/ticket/14671
     829 * @link http://php.net/manual/en/book.reflection.php
     830 *
     831 * @param callable $function The filter/action handler supplied when calling add_action()/add_filter()
     832 * @param int $accepted_args The 4th argument passed into add_action()/add_filter()
     833 * @return int The number of arguments to pass into hook handler
     834 */
     835function _wp_get_hook_handler_accepted_arg_count($function, $accepted_args) {
     836        // Note is_callable() is better for methods than method_exists() because it will work with __call magic methods
     837        if ( is_null($accepted_args) && is_callable($function) ) {
     838                if ( is_array($function) ) {
     839                        list( $object, $method ) = $function;
     840                        $reflection = new ReflectionMethod( $object, $method );
     841                } else {
     842                        $reflection = new ReflectionFunction( $function );
     843                }
     844                $accepted_args = $reflection->getNumberOfParameters();
     845        }
     846        return (int) $accepted_args;
     847}