Ticket #17817: 17817.diff
File 17817.diff, 31.6 KB (added by , 10 years ago) |
---|
-
src/wp-includes/class-wp-hook.php
1 <?php 2 /** 3 * 4 * @package WordPress 5 * @subpackage Plugin 6 * @since 4.2.0 7 */ 8 class WP_Hook implements IteratorAggregate, ArrayAccess { 9 public $callbacks = array(); 10 11 /** 12 * The priority keys of actively running iterations of a hook 13 * 14 * @var array 15 */ 16 private $iterations = array(); 17 18 /** 19 * How recursively has this hook been called? 20 * 21 * @var int 22 */ 23 private $nesting_level = 0; 24 25 /** 26 * Hook a function or method to a specific filter action. 27 * 28 * @param callable $function_to_add The callback to be run when the filter is applied. 29 * @param int $priority Optional. The order in which the functions associated with a 30 * particular action are executed. Lower numbers correspond with 31 * earlier execution, and functions with the same priority are executed 32 * in the order in which they were added to the action. 33 * Default: 10. 34 * @param int $accepted_args Optional. The number of arguments the function accepts. Default: 1. 35 * @param string $tag The name of the filter to hook the $function_to_add callback to. 36 */ 37 public function add_filter( $function_to_add, $priority, $accepted_args, $tag ) { 38 $idx = _wp_filter_build_unique_id( $tag, $function_to_add, $priority ); 39 $priority_existed = isset( $this->callbacks[ $priority ] ); 40 41 $this->callbacks[ $priority ][ $idx ] = array( 42 'function' => $function_to_add, 43 'accepted_args' => $accepted_args 44 ); 45 46 if ( ! $priority_existed && count( $this->callbacks ) > 1 ) { 47 ksort( $this->callbacks, SORT_NUMERIC ); 48 } 49 50 if ( $this->nesting_level > 0 ) { 51 $this->resort_active_iterations(); 52 } 53 } 54 55 /** 56 * When a hook's callbacks are changed mid-iteration, the priority 57 * keys need to be reset, with the array pointer at the correct location 58 */ 59 private function resort_active_iterations() { 60 $new_priorities = array_keys( $this->callbacks ); 61 62 // if there are no remaining hooks, clear out all running iterations 63 if ( empty( $new_priorities ) ) { 64 foreach ( $this->iterations as $index => $iteration ) { 65 $this->iterations[ $index ] = $new_priorities; 66 } 67 return; 68 } 69 70 $min = min( $new_priorities ); 71 foreach ( $this->iterations as $index => $iteration ) { 72 $current = current( $iteration ); 73 $this->iterations[ $index ] = $new_priorities; 74 75 if ( $current < $min ) { 76 array_unshift( $this->iterations[ $index ], $current ); 77 continue; 78 } 79 while ( current( $this->iterations[ $index ] ) < $current ) { 80 if ( false === next( $this->iterations[ $index ] ) ) { 81 break; 82 } 83 } 84 } 85 } 86 87 /** 88 * @param string $function_key 89 * @param int $priority 90 * 91 * @return bool Whether the callback existed before it was removed 92 */ 93 public function remove_filter( $function_key, $priority ) { 94 $exists = isset( $this->callbacks[ $priority ][ $function_key ] ); 95 if ( $exists ) { 96 unset( $this->callbacks[ $priority ][ $function_key ] ); 97 if ( empty( $this->callbacks[ $priority ] ) ) { 98 unset( $this->callbacks[ $priority ] ); 99 if ( $this->nesting_level > 0 ) { 100 $this->resort_active_iterations(); 101 } 102 } 103 } 104 return $exists; 105 } 106 107 /** 108 * Check if an specific action has been registered for this hook. 109 * 110 * @param string $function_key The hashed index of the filter 111 * @return mixed The priority of that hook is returned, or false if the function is not attached. 112 */ 113 public function has_filter( $function_key ) { 114 foreach ( $this->callbacks as $priority => &$callbacks ) { 115 if ( isset( $callbacks[ $function_key ] ) ) { 116 return $priority; 117 } 118 } 119 return false; 120 } 121 122 /** 123 * Check if any callbacks have been registered for this hook 124 * 125 * @return bool 126 */ 127 public function has_filters() { 128 foreach ( $this->callbacks as &$callbacks ) { 129 if ( ! empty( $callbacks ) ) { 130 return true; 131 } 132 } 133 return false; 134 } 135 136 /** 137 * Remove all of the callbacks from the filter. 138 * 139 * @param int|bool $priority The priority number to remove. 140 */ 141 public function remove_all_filters( $priority = false ) { 142 if ( empty( $this->callbacks ) ) { 143 return; 144 } 145 if ( false !== $priority && isset( $this->callbacks[ $priority ] ) ) { 146 unset( $this->callbacks[ $priority ] ); 147 } else { 148 $this->callbacks = array(); 149 } 150 if ( $this->nesting_level > 0 ) { 151 $this->resort_active_iterations(); 152 } 153 } 154 155 /** 156 * Call the functions added to a filter hook. 157 * 158 * @param mixed $value The value to filter. 159 * @param array $args Arguments to pass to callbacks 160 * 161 * @return mixed The filtered value after all hooked functions are applied to it 162 */ 163 public function apply_filters( $value, &$args ) { 164 if ( empty( $this->callbacks ) ) { 165 return $value; 166 } 167 $nesting_level = $this->nesting_level++; 168 $this->iterations[ $nesting_level ] = array_keys( $this->callbacks ); 169 170 do { 171 $priority = current( $this->iterations[ $nesting_level ] ); 172 foreach ( $this->callbacks[ $priority ] as $the_ ) { 173 $args[0] = $value; 174 $value = call_user_func_array( $the_['function'], array_slice( $args, 0, (int) $the_['accepted_args'] ) ); 175 } 176 } while ( false !== next( $this->iterations[ $nesting_level ] ) ); 177 178 unset( $this->iterations[ $nesting_level ] ); 179 $this->nesting_level--; 180 return $value; 181 } 182 183 /** 184 * Execute functions hooked on a specific action hook. 185 * 186 * @param mixed $args Arguments to pass to callbacks 187 * @return void 188 */ 189 public function do_action( &$args ) { 190 if ( empty( $this->callbacks ) ) { 191 return; 192 } 193 $nesting_level = $this->nesting_level++; 194 $this->iterations[ $nesting_level ] = array_keys( $this->callbacks ); 195 $num_args = count( $args ); 196 197 do { 198 $priority = current( $this->iterations[ $nesting_level ] ); 199 foreach ( $this->callbacks[ $priority ] as $the_ ) { 200 $func =& $the_['function']; 201 // avoid the array_slice if possible 202 if ( $the_['accepted_args'] == 0 ) { 203 call_user_func_array( $func, array() ); 204 } elseif ( $the_['accepted_args'] >= $num_args ) { 205 call_user_func_array( $func, $args ); 206 } else { 207 call_user_func_array( $func, array_slice( $args, 0, (int) $the_['accepted_args'] ) ); 208 } 209 } 210 } while ( false !== next( $this->iterations[ $nesting_level ] ) ); 211 212 unset( $this->iterations[ $nesting_level ] ); 213 $this->nesting_level--; 214 } 215 216 /** 217 * Process the functions hooked into the 'all' hook. 218 * 219 * @param array $args Arguments to pass to callbacks 220 */ 221 public function do_all_hook( &$args ) { 222 $nesting_level = $this->nesting_level++; 223 $this->iterations[ $nesting_level ] = array_keys( $this->callbacks ); 224 225 do { 226 $priority = current( $this->iterations[ $nesting_level ] ); 227 foreach ( $this->callbacks[ $priority ] as $the_ ) { 228 call_user_func_array( $the_['function'], $args ); 229 } 230 } while ( false !== next( $this->iterations[ $nesting_level ] ) ); 231 232 unset( $this->iterations[ $nesting_level ] ); 233 $this->nesting_level--; 234 } 235 236 /** 237 * Retrieve an external iterator 238 * 239 * Provided for backwards compatibility with plugins that iterate over the 240 * $wp_filter global 241 * 242 * @link http://php.net/manual/en/iteratoraggregate.getiterator.php 243 * @return Traversable An instance of an object implementing Iterator or 244 * Traversable 245 */ 246 public function getIterator() { 247 return new ArrayIterator( $this->callbacks ); 248 } 249 250 /** 251 * Some plugins may set up filters before WordPress has initialized. 252 * Normalize them to WP_Hook objects. 253 * 254 * @param array $filters 255 * @return WP_Hook[] 256 */ 257 public static function build_preinitialized_hooks( $filters ) { 258 /** @var WP_Hook[] $normalized */ 259 $normalized = array(); 260 foreach ( $filters as $tag => $callback_groups ) { 261 if ( is_object( $callback_groups ) && $callback_groups instanceof WP_Hook ) { 262 $normalized[ $tag ] = $callback_groups; 263 continue; 264 } 265 $hook = new WP_Hook(); 266 foreach ( $callback_groups as $priority => $callbacks ) { 267 foreach ( $callbacks as $cb ) { 268 $hook->add_filter( $cb['function'], $priority, $cb['accepted_args'], $tag ); 269 } 270 } 271 $normalized[ $tag ] = $hook; 272 } 273 return $normalized; 274 } 275 276 /** 277 * Whether an offset exists 278 * 279 * @link http://php.net/manual/en/arrayaccess.offsetexists.php 280 * 281 * @param mixed $offset An offset to check for. 282 * 283 * @return bool 284 */ 285 public function offsetExists( $offset ) { 286 return isset( $this->callbacks[ $offset ] ); 287 } 288 289 /** 290 * Offset to retrieve 291 * 292 * @link http://php.net/manual/en/arrayaccess.offsetget.php 293 * 294 * @param mixed $offset The offset to retrieve. 295 * 296 * @return mixed 297 */ 298 public function offsetGet( $offset ) { 299 return isset( $this->callbacks[ $offset ] ) ? $this->callbacks[ $offset ] : null; 300 } 301 302 /** 303 * Offset to set 304 * 305 * @link http://php.net/manual/en/arrayaccess.offsetset.php 306 * 307 * @param mixed $offset The offset to assign the value to. 308 * @param mixed $value The value to set. 309 */ 310 public function offsetSet( $offset, $value ) { 311 if ( is_null( $offset ) ) { 312 $this->callbacks[] = $value; 313 } else { 314 $this->callbacks[ $offset ] = $value; 315 } 316 } 317 318 /** 319 * Offset to unset 320 * 321 * @link http://php.net/manual/en/arrayaccess.offsetunset.php 322 * 323 * @param mixed $offset The offset to unset. 324 */ 325 public function offsetUnset( $offset ) { 326 unset( $this->callbacks[ $offset ] ); 327 } 328 } 329 -
src/wp-includes/plugin.php
20 20 */ 21 21 22 22 // Initialize the filter globals. 23 global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter; 23 /** @var WP_Hook[] $wp_filter */ 24 global $wp_filter, $wp_actions, $wp_current_filter; 24 25 25 if ( ! isset( $wp_filter ) ) 26 if ( ! empty( $wp_filter ) ) { 27 $wp_filter = WP_Hook::build_preinitialized_hooks( $wp_filter ); 28 } else { 26 29 $wp_filter = array(); 30 } 27 31 28 32 if ( ! isset( $wp_actions ) ) 29 33 $wp_actions = array(); 30 34 31 if ( ! isset( $merged_filters ) )32 $merged_filters = array();33 34 35 if ( ! isset( $wp_current_filter ) ) 35 36 $wp_current_filter = array(); 36 37 … … 64 65 * 65 66 * @since 0.71 66 67 * 67 * @global array $wp_filter A multidimensional array of all hooks and the callbacks hooked to them. 68 * @global array $merged_filters Tracks the tags that need to be merged for later. If the hook is added, 69 * it doesn't need to run through that process. 68 * @global array $wp_filter A multidimensional array of all hooks and the callbacks hooked to them. 70 69 * 71 70 * @param string $tag The name of the filter to hook the $function_to_add callback to. 72 71 * @param callback $function_to_add The callback to be run when the filter is applied. … … 79 78 * @return boolean true 80 79 */ 81 80 function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) { 82 global $wp_filter , $merged_filters;81 global $wp_filter; 83 82 84 $idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority); 85 $wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args); 86 unset( $merged_filters[ $tag ] ); 83 if ( ! isset( $wp_filter[ $tag ] ) ) { 84 $wp_filter[ $tag ] = new WP_Hook(); 85 } 86 87 $wp_filter[ $tag ]->add_filter( $function_to_add, $priority, $accepted_args, $tag ); 88 87 89 return true; 88 90 } 89 91 … … 103 105 * that evaluates to false (e.g.) 0, so use the === operator for testing the 104 106 * return value. 105 107 */ 106 function has_filter($tag, $function_to_check = false) { 107 // Don't reset the internal array pointer 108 $wp_filter = $GLOBALS['wp_filter']; 108 function has_filter( $tag, $function_to_check = false ) { 109 global $wp_filter; 109 110 110 $has = ! empty( $wp_filter[ $tag ]);111 $has = isset( $wp_filter[ $tag ] ) && $wp_filter[ $tag ]->has_filters(); 111 112 112 // Make sure at least one priority has a filter callback 113 if ( $has ) { 114 $exists = false; 115 foreach ( $wp_filter[ $tag ] as $callbacks ) { 116 if ( ! empty( $callbacks ) ) { 117 $exists = true; 118 break; 119 } 120 } 121 122 if ( ! $exists ) { 123 $has = false; 124 } 113 if ( ! $function_to_check || ! $has ) { 114 return $has; 125 115 } 126 116 127 if ( false === $function_to_check || false == $has ) 128 return $has; 129 130 if ( !$idx = _wp_filter_build_unique_id($tag, $function_to_check, false) ) 117 if ( ! $idx = _wp_filter_build_unique_id( $tag, $function_to_check, false ) ) { 131 118 return false; 132 133 foreach ( (array) array_keys($wp_filter[$tag]) as $priority ) {134 if ( isset($wp_filter[$tag][$priority][$idx]) )135 return $priority;136 119 } 137 120 138 return false;121 return $wp_filter[ $tag ]->has_filter( $idx ); 139 122 } 140 123 141 124 /** … … 166 149 * @since 0.71 167 150 * 168 151 * @global array $wp_filter Stores all of the filters. 169 * @global array $merged_filters Merges the filter hooks using this function.170 152 * @global array $wp_current_filter Stores the list of current filters with the current one last. 171 153 * 172 154 * @param string $tag The name of the filter hook. … … 175 157 * @return mixed The filtered value after all hooked functions are applied to it. 176 158 */ 177 159 function apply_filters( $tag, $value ) { 178 global $wp_filter, $ merged_filters, $wp_current_filter;160 global $wp_filter, $wp_current_filter; 179 161 180 162 $args = array(); 181 163 182 164 // Do 'all' actions first. 183 if ( isset( $wp_filter['all']) ) {165 if ( isset( $wp_filter['all'] ) ) { 184 166 $wp_current_filter[] = $tag; 185 167 $args = func_get_args(); 186 _wp_call_all_hook($args); 168 _wp_call_all_hook( $args ); 169 array_shift( $args ); 187 170 } 188 171 189 if ( !isset($wp_filter[$tag]) ) { 190 if ( isset($wp_filter['all']) ) 191 array_pop($wp_current_filter); 172 if ( ! isset( $wp_filter[ $tag ] ) ) { 173 if ( isset( $wp_filter['all'] ) ) { 174 array_pop( $wp_current_filter ); 175 } 192 176 return $value; 193 177 } 194 178 195 if ( ! isset($wp_filter['all']) )179 if ( ! isset( $wp_filter['all'] ) ) { 196 180 $wp_current_filter[] = $tag; 197 198 // Sort.199 if ( !isset( $merged_filters[ $tag ] ) ) {200 ksort($wp_filter[$tag]);201 $merged_filters[ $tag ] = true;202 181 } 203 182 204 reset( $wp_filter[ $tag ] ); 205 206 if ( empty($args) ) 183 if ( empty( $args ) ) { 207 184 $args = func_get_args(); 185 array_shift( $args ); 186 } 208 187 209 do { 210 foreach( (array) current($wp_filter[$tag]) as $the_ ) 211 if ( !is_null($the_['function']) ){ 212 $args[1] = $value; 213 $value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $the_['accepted_args'])); 214 } 188 $filtered = $wp_filter[ $tag ]->apply_filters( $value, $args ); 215 189 216 } while ( next($wp_filter[$tag]) !== false );217 218 190 array_pop( $wp_current_filter ); 219 191 220 return $ value;192 return $filtered; 221 193 } 222 194 223 195 /** … … 229 201 * functions hooked to `$tag` are supplied using an array. 230 202 * 231 203 * @global array $wp_filter Stores all of the filters 232 * @global array $merged_filters Merges the filter hooks using this function.233 204 * @global array $wp_current_filter Stores the list of current filters with the current one last 234 205 * 235 206 * @param string $tag The name of the filter hook. … … 236 207 * @param array $args The arguments supplied to the functions hooked to $tag. 237 208 * @return mixed The filtered value after all hooked functions are applied to it. 238 209 */ 239 function apply_filters_ref_array( $tag, $args) {240 global $wp_filter, $ merged_filters, $wp_current_filter;210 function apply_filters_ref_array( $tag, $args ) { 211 global $wp_filter, $wp_current_filter; 241 212 242 213 // Do 'all' actions first 243 if ( isset( $wp_filter['all']) ) {214 if ( isset( $wp_filter['all'] ) ) { 244 215 $wp_current_filter[] = $tag; 245 216 $all_args = func_get_args(); 246 _wp_call_all_hook( $all_args);217 _wp_call_all_hook( $all_args ); 247 218 } 248 219 249 if ( !isset($wp_filter[$tag]) ) { 250 if ( isset($wp_filter['all']) ) 251 array_pop($wp_current_filter); 220 if ( ! isset( $wp_filter[$tag] ) ) { 221 if ( isset( $wp_filter['all'] ) ) { 222 array_pop( $wp_current_filter ); 223 } 252 224 return $args[0]; 253 225 } 254 226 255 if ( ! isset($wp_filter['all']) )227 if ( ! isset( $wp_filter['all'] ) ) { 256 228 $wp_current_filter[] = $tag; 257 258 // Sort259 if ( !isset( $merged_filters[ $tag ] ) ) {260 ksort($wp_filter[$tag]);261 $merged_filters[ $tag ] = true;262 229 } 263 230 264 reset( $wp_filter[ $tag ]);231 $filtered = $wp_filter[ $tag ]->apply_filters( $args[0], $args ); 265 232 266 do {267 foreach( (array) current($wp_filter[$tag]) as $the_ )268 if ( !is_null($the_['function']) )269 $args[0] = call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));270 271 } while ( next($wp_filter[$tag]) !== false );272 273 233 array_pop( $wp_current_filter ); 274 234 275 return $ args[0];235 return $filtered; 276 236 } 277 237 278 238 /** … … 294 254 * @return boolean Whether the function existed before it was removed. 295 255 */ 296 256 function remove_filter( $tag, $function_to_remove, $priority = 10 ) { 297 $function_to_remove = _wp_filter_build_unique_id( $tag, $function_to_remove, $priority );257 global $wp_filter; 298 258 299 $r = isset( $GLOBALS['wp_filter'][ $tag ][ $priority ][ $function_to_remove ] );300 301 if ( true === $r ) {302 unset( $GLOBALS['wp_filter'][ $tag ][ $priority ][ $function_to_remove ]);303 if ( empty( $ GLOBALS['wp_filter'][ $tag ][ $priority ]) ) {304 unset( $ GLOBALS['wp_filter'][ $tag ][ $priority] );259 $r = false; 260 if ( isset( $wp_filter[ $tag ] ) ) { 261 $function_to_remove = _wp_filter_build_unique_id( $tag, $function_to_remove, $priority ); 262 $r = $wp_filter[ $tag ]->remove_filter( $function_to_remove, $priority ); 263 if ( empty( $wp_filter[ $tag ]->callbacks ) ) { 264 unset( $wp_filter[ $tag ] ); 305 265 } 306 if ( empty( $GLOBALS['wp_filter'][ $tag ] ) ) {307 $GLOBALS['wp_filter'][ $tag ] = array();308 }309 unset( $GLOBALS['merged_filters'][ $tag ] );310 266 } 311 267 312 268 return $r; … … 322 278 * @return bool True when finished. 323 279 */ 324 280 function remove_all_filters( $tag, $priority = false ) { 325 global $wp_filter , $merged_filters;281 global $wp_filter; 326 282 327 if ( isset( $wp_filter[ $tag ]) ) { 328 if ( false === $priority ) { 329 $wp_filter[ $tag ] = array(); 330 } elseif ( isset( $wp_filter[ $tag ][ $priority ] ) ) { 331 $wp_filter[ $tag ][ $priority ] = array(); 332 } 283 if ( isset( $wp_filter[ $tag ] ) ) { 284 $wp_filter[ $tag ]->remove_all_filters( $priority ); 285 unset( $wp_filter[ $tag ] ); 333 286 } 334 287 335 if ( isset( $merged_filters[ $tag ] ) ) {336 unset( $merged_filters[ $tag ] );337 }338 339 288 return true; 340 289 } 341 290 … … 450 399 * functions hooked to the action. Default empty. 451 400 * @return null Will return null if $tag does not exist in $wp_filter array. 452 401 */ 453 function do_action( $tag, $arg = '') {454 global $wp_filter, $wp_actions, $ merged_filters, $wp_current_filter;402 function do_action( $tag, $arg = '' ) { 403 global $wp_filter, $wp_actions, $wp_current_filter; 455 404 456 if ( ! isset( $wp_actions[$tag]) )457 $wp_actions[ $tag] = 1;458 else459 ++$wp_actions[ $tag];460 405 if ( ! isset( $wp_actions[ $tag ] ) ) { 406 $wp_actions[ $tag ] = 1; 407 } else { 408 ++$wp_actions[ $tag ]; 409 } 461 410 // Do 'all' actions first 462 if ( isset( $wp_filter['all']) ) {411 if ( isset( $wp_filter['all'] ) ) { 463 412 $wp_current_filter[] = $tag; 464 413 $all_args = func_get_args(); 465 _wp_call_all_hook( $all_args);414 _wp_call_all_hook( $all_args ); 466 415 } 467 416 468 if ( !isset($wp_filter[$tag]) ) { 469 if ( isset($wp_filter['all']) ) 470 array_pop($wp_current_filter); 417 if ( ! isset( $wp_filter[ $tag ] ) ) { 418 if ( isset( $wp_filter['all'] ) ) { 419 array_pop( $wp_current_filter ); 420 } 471 421 return; 472 422 } 473 423 474 if ( ! isset($wp_filter['all']) )424 if ( ! isset( $wp_filter['all'] ) ) { 475 425 $wp_current_filter[] = $tag; 426 } 476 427 477 428 $args = array(); 478 if ( is_array($arg) && 1 == count($arg) && isset($arg[0]) && is_object($arg[0]) ) // array(&$this) 429 // array(&$this) 430 if ( is_array( $arg ) && 1 == count( $arg ) && isset( $arg[0] ) && is_object( $arg[0] ) ) { 479 431 $args[] =& $arg[0]; 480 else432 } else { 481 433 $args[] = $arg; 482 for ( $a = 2; $a < func_num_args(); $a++ ) 434 } 435 for ( $a = 2; $a < func_num_args(); $a++ ) { 483 436 $args[] = func_get_arg($a); 484 485 // Sort486 if ( !isset( $merged_filters[ $tag ] ) ) {487 ksort($wp_filter[$tag]);488 $merged_filters[ $tag ] = true;489 437 } 490 438 491 reset( $wp_filter[ $tag ]);439 $wp_filter[ $tag ]->do_action( $args ); 492 440 493 do { 494 foreach ( (array) current($wp_filter[$tag]) as $the_ ) 495 if ( !is_null($the_['function']) ) 496 call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args'])); 497 498 } while ( next($wp_filter[$tag]) !== false ); 499 500 array_pop($wp_current_filter); 441 array_pop( $wp_current_filter ); 501 442 } 502 443 503 444 /** … … 533 474 * @param array $args The arguments supplied to the functions hooked to `$tag`. 534 475 * @return null Will return null if `$tag` does not exist in `$wp_filter` array. 535 476 */ 536 function do_action_ref_array( $tag, $args) {537 global $wp_filter, $wp_actions, $ merged_filters, $wp_current_filter;477 function do_action_ref_array( $tag, $args ) { 478 global $wp_filter, $wp_actions, $wp_current_filter; 538 479 539 if ( ! isset( $wp_actions[$tag]) )540 $wp_actions[ $tag] = 1;541 else542 ++$wp_actions[ $tag];543 480 if ( ! isset( $wp_actions[ $tag ] ) ) { 481 $wp_actions[ $tag ] = 1; 482 } else { 483 ++$wp_actions[ $tag ]; 484 } 544 485 // Do 'all' actions first 545 if ( isset( $wp_filter['all']) ) {486 if ( isset( $wp_filter['all'] ) ) { 546 487 $wp_current_filter[] = $tag; 547 488 $all_args = func_get_args(); 548 489 _wp_call_all_hook($all_args); 549 490 } 550 491 551 if ( !isset($wp_filter[$tag]) ) { 552 if ( isset($wp_filter['all']) ) 553 array_pop($wp_current_filter); 492 if ( ! isset( $wp_filter[ $tag ] ) ) { 493 if ( isset( $wp_filter['all'] ) ) { 494 array_pop( $wp_current_filter ); 495 } 554 496 return; 555 497 } 556 498 557 if ( ! isset($wp_filter['all']) )499 if ( ! isset( $wp_filter['all'] ) ) { 558 500 $wp_current_filter[] = $tag; 559 560 // Sort561 if ( !isset( $merged_filters[ $tag ] ) ) {562 ksort($wp_filter[$tag]);563 $merged_filters[ $tag ] = true;564 501 } 565 502 566 reset( $wp_filter[ $tag ]);503 $wp_filter[ $tag ]->do_action( $args ); 567 504 568 do { 569 foreach( (array) current($wp_filter[$tag]) as $the_ ) 570 if ( !is_null($the_['function']) ) 571 call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args'])); 572 573 } while ( next($wp_filter[$tag]) !== false ); 574 575 array_pop($wp_current_filter); 505 array_pop( $wp_current_filter ); 576 506 } 577 507 578 508 /** … … 823 753 * 824 754 * @param array $args The collected parameters from the hook that was called. 825 755 */ 826 function _wp_call_all_hook( $args) {756 function _wp_call_all_hook( $args ) { 827 757 global $wp_filter; 828 829 reset( $wp_filter['all'] ); 830 do { 831 foreach( (array) current($wp_filter['all']) as $the_ ) 832 if ( !is_null($the_['function']) ) 833 call_user_func_array($the_['function'], $args); 834 835 } while ( next($wp_filter['all']) !== false ); 758 $wp_filter['all']->do_all_hook( $args ); 836 759 } 837 760 838 761 /** -
src/wp-settings.php
72 72 require( ABSPATH . WPINC . '/functions.php' ); 73 73 require( ABSPATH . WPINC . '/class-wp.php' ); 74 74 require( ABSPATH . WPINC . '/class-wp-error.php' ); 75 require( ABSPATH . WPINC . '/class-wp-hook.php' ); 75 76 require( ABSPATH . WPINC . '/plugin.php' ); 76 77 require( ABSPATH . WPINC . '/pomo/mo.php' ); 77 78 -
tests/phpunit/includes/functions.php
2 2 3 3 // For adding hooks before loading WP 4 4 function tests_add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) { 5 global $wp_filter , $merged_filters;5 global $wp_filter; 6 6 7 7 $idx = _test_filter_build_unique_id($tag, $function_to_add, $priority); 8 8 $wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args); 9 unset( $merged_filters[ $tag ] );10 9 return true; 11 10 } 12 11 13 12 function _test_filter_build_unique_id($tag, $function, $priority) { 14 global $wp_filter;15 static $filter_id_count = 0;16 17 13 if ( is_string($function) ) 18 14 return $function; 19 15 -
tests/phpunit/includes/testcase.php
130 130 * @return void 131 131 */ 132 132 protected function _backup_hooks() { 133 $globals = array( ' merged_filters', 'wp_actions', 'wp_current_filter', 'wp_filter' );133 $globals = array( 'wp_actions', 'wp_current_filter', 'wp_filter' ); 134 134 foreach ( $globals as $key ) { 135 135 self::$hooks_saved[ $key ] = $GLOBALS[ $key ]; 136 136 } … … 147 147 * @return void 148 148 */ 149 149 protected function _restore_hooks() { 150 $globals = array( ' merged_filters', 'wp_actions', 'wp_current_filter', 'wp_filter' );150 $globals = array( 'wp_actions', 'wp_current_filter', 'wp_filter' ); 151 151 foreach ( $globals as $key ) { 152 152 if ( isset( self::$hooks_saved[ $key ] ) ) { 153 153 $GLOBALS[ $key ] = self::$hooks_saved[ $key ]; -
tests/phpunit/tests/actions.php
114 114 $this->assertEquals( array( $val1 ), array_pop( $argsvar2 ) ); 115 115 } 116 116 117 /** 118 * Test that multiple callbacks receive the correct number of args even when the number 119 * is less than, or greater than previous hooks. 120 * 121 * @see https://core.trac.wordpress.org/ticket/17817#comment:72 122 */ 123 function test_action_args_3() { 124 $a1 = new MockAction(); 125 $a2 = new MockAction(); 126 $a3 = new MockAction(); 127 $tag = rand_str(); 128 $val1 = rand_str(); 129 $val2 = rand_str(); 130 131 // a1 accepts two arguments, a2 doesn't, a3 accepts two arguments 132 add_action( $tag, array( $a1, 'action' ), 10, 2 ); 133 add_action( $tag, array( $a2, 'action' ) ); 134 add_action( $tag, array( $a3, 'action' ), 10, 2 ); 135 // call the action with two arguments 136 do_action( $tag, $val1, $val2 ); 137 138 $call_count = $a1->get_call_count(); 139 // a1 should be called with both args 140 $this->assertEquals( 1, $call_count ); 141 $argsvar1 = $a1->get_args(); 142 $this->assertEquals( array( $val1, $val2 ), array_pop( $argsvar1 ) ); 143 144 // a2 should be called with one only 145 $this->assertEquals(1, $a2->get_call_count()); 146 $argsvar2 = $a2->get_args(); 147 $this->assertEquals( array( $val1 ), array_pop( $argsvar2 ) ); 148 149 // a3 should be called with both args 150 $this->assertEquals(1, $a3->get_call_count()); 151 $argsvar3 = $a3->get_args(); 152 $this->assertEquals( array( $val1, $val2 ), array_pop( $argsvar3 ) ); 153 } 154 117 155 function test_action_priority() { 118 156 $a = new MockAction(); 119 157 $tag = rand_str(); … … 258 296 } 259 297 260 298 /** 299 * @ticket 17817 300 */ 301 function test_action_recursion() { 302 $tag = rand_str(); 303 $a = new MockAction(); 304 $b = new MockAction(); 305 306 add_action( $tag, array( $a, 'action' ), 11, 1 ); 307 add_action( $tag, array( $b, 'action' ), 13, 1 ); 308 add_action( $tag, array( $this, 'action_that_causes_recursion' ), 12, 1 ); 309 do_action( $tag, $tag ); 310 311 $this->assertEquals( 2, $a->get_call_count(), 'recursive actions should call all callbacks with earlier priority' ); 312 $this->assertEquals( 2, $b->get_call_count(), 'recursive actions should call callbacks with later priority' ); 313 } 314 315 function action_that_causes_recursion( $tag ) { 316 static $recursing = false; 317 if ( !$recursing ) { 318 $recursing = true; 319 do_action( $tag, $tag ); 320 } 321 $recursing = false; 322 } 323 324 /** 325 * @ticket 9968 326 */ 327 function test_action_callback_manipulation_while_running() { 328 $tag = rand_str(); 329 $a = new MockAction(); 330 $b = new MockAction(); 331 $c = new MockAction(); 332 $d = new MockAction(); 333 $e = new MockAction(); 334 335 add_action( $tag, array( $a, 'action' ), 11, 2 ); 336 add_action( $tag, array( $this, 'action_that_manipulates_a_running_hook' ), 12, 2 ); 337 add_action( $tag, array( $b, 'action' ), 12, 2 ); 338 339 do_action( $tag, $tag, array( $a, $b, $c, $d, $e ) ); 340 do_action( $tag, $tag, array( $a, $b, $c, $d, $e ) ); 341 342 $this->assertEquals( 2, $a->get_call_count(), 'callbacks should run unless otherwise instructed' ); 343 $this->assertEquals( 1, $b->get_call_count(), 'callback removed by same priority callback should still get called' ); 344 $this->assertEquals( 1, $c->get_call_count(), 'callback added by same priority callback should not get called' ); 345 $this->assertEquals( 2, $d->get_call_count(), 'callback added by earlier priority callback should get called' ); 346 $this->assertEquals( 1, $e->get_call_count(), 'callback added by later priority callback should not get called' ); 347 } 348 349 function action_that_manipulates_a_running_hook( $tag, $mocks ) { 350 remove_action( $tag, array( $mocks[1], 'action' ), 12, 2 ); 351 add_action( $tag, array( $mocks[2], 'action' ), 12, 2 ); 352 add_action( $tag, array( $mocks[3], 'action' ), 13, 2 ); 353 add_action( $tag, array( $mocks[4], 'action' ), 10, 2 ); 354 } 355 356 /** 357 * @ticket 17817 358 * 359 * This specificaly addresses the concern raised at 360 * https://core.trac.wordpress.org/ticket/17817#comment:52 361 */ 362 function test_remove_anonymous_callback() { 363 $tag = rand_str(); 364 $a = new MockAction(); 365 add_action( $tag, array( $a, 'action' ), 12, 1 ); 366 $this->assertTrue( has_action( $tag ) ); 367 368 $hook = $GLOBALS['wp_filter'][ $tag ]; 369 370 // From http://wordpress.stackexchange.com/a/57088/6445 371 foreach ( $hook as $priority => $filter ) { 372 foreach ( $filter as $identifier => $function ) { 373 if ( is_array( $function) 374 && is_a( $function['function'][0], 'MockAction' ) 375 && 'action' === $function['function'][1] 376 ) { 377 remove_filter( 378 $tag, 379 array ( $function['function'][0], 'action' ), 380 $priority 381 ); 382 } 383 } 384 } 385 386 $this->assertFalse( has_action( $tag ) ); 387 } 388 389 390 /** 391 * Test the ArrayAccess methods of WP_Hook 392 * 393 * @ticket 17817 394 */ 395 function test_array_access_of_wp_filter_global() { 396 global $wp_filter; 397 $tag = rand_str(); 398 399 add_action( $tag, '__return_null', 11, 1 ); 400 401 $this->assertTrue( isset( $wp_filter[ $tag ][11] ) ); 402 $this->assertArrayHasKey( '__return_null', $wp_filter[ $tag ][11] ); 403 404 unset( $wp_filter[ $tag ][11] ); 405 $this->assertFalse( has_action( $tag, '__return_null' ) ); 406 407 $wp_filter[ $tag ][11] = array( 408 '__return_null' => array( 409 'function' => '__return_null', 410 'accepted_args' => 1 411 ) 412 ); 413 $this->assertEquals( 11, has_action( $tag, '__return_null' ) ); 414 415 } 416 417 /** 261 418 * Make sure current_action() behaves as current_filter() 262 419 * 263 420 * @ticket 14994 -
tests/phpunit/tests/filters.php
294 294 remove_all_filters( $tag, 12 ); 295 295 $this->assertFalse( has_filter( $tag ) ); 296 296 } 297 298 /**299 * @ticket 29070300 */301 function test_has_filter_doesnt_reset_wp_filter() {302 add_action( 'action_test_has_filter_doesnt_reset_wp_filter', '__return_null', 1 );303 add_action( 'action_test_has_filter_doesnt_reset_wp_filter', '__return_null', 2 );304 add_action( 'action_test_has_filter_doesnt_reset_wp_filter', '__return_null', 3 );305 add_action( 'action_test_has_filter_doesnt_reset_wp_filter', array( $this, '_action_test_has_filter_doesnt_reset_wp_filter' ), 4 );306 307 do_action( 'action_test_has_filter_doesnt_reset_wp_filter' );308 }309 function _action_test_has_filter_doesnt_reset_wp_filter() {310 global $wp_filter;311 312 has_action( 'action_test_has_filter_doesnt_reset_wp_filter', '_function_that_doesnt_exist' );313 314 $filters = current( $wp_filter['action_test_has_filter_doesnt_reset_wp_filter'] );315 $the_ = current( $filters );316 $this->assertEquals( $the_['function'], array( $this, '_action_test_has_filter_doesnt_reset_wp_filter' ) );317 }318 297 }