Ticket #17817: 17817.2.patch
File 17817.2.patch, 15.0 KB (added by , 11 years ago) |
---|
-
src/wp-includes/plugin.php
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8
20 20 */ 21 21 22 22 // Initialize the filter globals. 23 global $wp_filter, $wp_actions, $ merged_filters, $wp_current_filter;23 global $wp_filter, $wp_actions, $wp_current_filter; 24 24 25 25 if ( ! isset( $wp_filter ) ) 26 26 $wp_filter = array(); … … 28 28 if ( ! isset( $wp_actions ) ) 29 29 $wp_actions = array(); 30 30 31 if ( ! isset( $merged_filters ) )32 $merged_filters = array();33 34 31 if ( ! isset( $wp_current_filter ) ) 35 32 $wp_current_filter = array(); 36 33 … … 67 64 * @subpackage Plugin 68 65 * 69 66 * @global array $wp_filter A multidimensional array of all hooks and the callbacks hooked to them. 70 * @global array $merged_filters Tracks the tags that need to be merged for later. If the hook is added, it doesn't need to run through that process.71 67 * 72 68 * @since 0.71 73 69 * … … 80 76 * @return boolean true 81 77 */ 82 78 function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) { 83 global $wp_filter , $merged_filters;79 global $wp_filter; 84 80 85 81 $idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority); 86 82 $wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args); 87 unset( $merged_filters[ $tag ] );88 83 return true; 89 84 } 90 85 … … 150 145 * @subpackage Plugin 151 146 * 152 147 * @global array $wp_filter Stores all of the filters 153 * @global array $merged_filters Merges the filter hooks using this function.154 148 * @global array $wp_current_filter stores the list of current filters with the current one last 155 149 * 156 150 * @since 0.71 … … 161 155 * @return mixed The filtered value after all hooked functions are applied to it. 162 156 */ 163 157 function apply_filters( $tag, $value ) { 164 global $wp_filter, $ merged_filters, $wp_current_filter;158 global $wp_filter, $wp_current_filter; 165 159 166 160 $args = array(); 167 161 … … 181 175 if ( !isset($wp_filter['all']) ) 182 176 $wp_current_filter[] = $tag; 183 177 184 // Sort185 if ( !isset( $merged_filters[ $tag ] ) ) {186 ksort($wp_filter[$tag]);187 $merged_filters[ $tag ] = true;188 }189 190 reset( $wp_filter[ $tag ] );191 192 178 if ( empty($args) ) 193 179 $args = func_get_args(); 194 180 195 do { 196 foreach( (array) current($wp_filter[$tag]) as $the_ ) 197 if ( !is_null($the_['function']) ){ 198 $args[1] = $value; 199 $value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $the_['accepted_args'])); 200 } 181 $iterator = new WP_Hook_Iterator($tag); 182 foreach ( $iterator as $the_ ) { 183 if ( !is_null($the_['function']) ) { 184 $args[1] = $value; 185 $value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $the_['accepted_args'])); 186 } 187 } 201 188 202 } while ( next($wp_filter[$tag]) !== false );203 204 189 array_pop( $wp_current_filter ); 205 190 206 191 return $value; … … 216 201 * @subpackage Plugin 217 202 * @since 3.0.0 218 203 * @global array $wp_filter Stores all of the filters 219 * @global array $merged_filters Merges the filter hooks using this function.220 204 * @global array $wp_current_filter stores the list of current filters with the current one last 221 205 * 222 206 * @param string $tag The name of the filter hook. … … 224 208 * @return mixed The filtered value after all hooked functions are applied to it. 225 209 */ 226 210 function apply_filters_ref_array($tag, $args) { 227 global $wp_filter, $ merged_filters, $wp_current_filter;211 global $wp_filter, $wp_current_filter; 228 212 229 213 // Do 'all' actions first 230 214 if ( isset($wp_filter['all']) ) { … … 242 226 if ( !isset($wp_filter['all']) ) 243 227 $wp_current_filter[] = $tag; 244 228 245 // Sort 246 if ( !isset( $merged_filters[ $tag ] ) ) { 247 ksort($wp_filter[$tag]); 248 $merged_filters[ $tag ] = true; 249 } 229 $iterator = new WP_Hook_Iterator($tag); 230 foreach ( $iterator as $the_ ) { 231 if ( !is_null($the_['function']) ) { 232 $args[0] = call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args'])); 233 } 234 } 250 235 251 reset( $wp_filter[ $tag ] );252 253 do {254 foreach( (array) current($wp_filter[$tag]) as $the_ )255 if ( !is_null($the_['function']) )256 $args[0] = call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));257 258 } while ( next($wp_filter[$tag]) !== false );259 260 236 array_pop( $wp_current_filter ); 261 237 262 238 return $args[0]; … … 308 284 * @return bool True when finished. 309 285 */ 310 286 function remove_all_filters($tag, $priority = false) { 311 global $wp_filter , $merged_filters;287 global $wp_filter; 312 288 313 289 if( isset($wp_filter[$tag]) ) { 314 290 if( false !== $priority && isset($wp_filter[$tag][$priority]) ) … … 317 293 unset($wp_filter[$tag]); 318 294 } 319 295 320 if( isset($merged_filters[$tag]) )321 unset($merged_filters[$tag]);322 323 296 return true; 324 297 } 325 298 … … 384 357 * @return null Will return null if $tag does not exist in $wp_filter array 385 358 */ 386 359 function do_action($tag, $arg = '') { 387 global $wp_filter, $wp_actions, $ merged_filters, $wp_current_filter;360 global $wp_filter, $wp_actions, $wp_current_filter; 388 361 389 362 if ( ! isset($wp_actions[$tag]) ) 390 363 $wp_actions[$tag] = 1; … … 415 388 for ( $a = 2; $a < func_num_args(); $a++ ) 416 389 $args[] = func_get_arg($a); 417 390 418 // Sort 419 if ( !isset( $merged_filters[ $tag ] ) ) { 420 ksort($wp_filter[$tag]); 421 $merged_filters[ $tag ] = true; 422 } 423 424 reset( $wp_filter[ $tag ] ); 425 426 do { 427 foreach ( (array) current($wp_filter[$tag]) as $the_ ) 428 if ( !is_null($the_['function']) ) 429 call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args'])); 430 431 } while ( next($wp_filter[$tag]) !== false ); 432 391 $iterator = new WP_Hook_Iterator($tag); 392 foreach ( $iterator as $the_ ) { 393 if ( !is_null($the_['function']) ) { 394 call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args'])); 395 } 396 } 433 397 array_pop($wp_current_filter); 434 398 } 435 399 … … 470 434 * @return null Will return null if $tag does not exist in $wp_filter array 471 435 */ 472 436 function do_action_ref_array($tag, $args) { 473 global $wp_filter, $wp_actions, $ merged_filters, $wp_current_filter;437 global $wp_filter, $wp_actions, $wp_current_filter; 474 438 475 439 if ( ! isset($wp_actions[$tag]) ) 476 440 $wp_actions[$tag] = 1; … … 493 457 if ( !isset($wp_filter['all']) ) 494 458 $wp_current_filter[] = $tag; 495 459 496 // Sort 497 if ( !isset( $merged_filters[ $tag ] ) ) { 498 ksort($wp_filter[$tag]); 499 $merged_filters[ $tag ] = true; 500 } 460 $iterator = new WP_Hook_Iterator($tag); 461 foreach ( $iterator as $the_ ) { 462 if ( !is_null($the_['function']) ) { 463 call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args'])); 464 } 465 } 501 466 502 reset( $wp_filter[ $tag ] );503 504 do {505 foreach( (array) current($wp_filter[$tag]) as $the_ )506 if ( !is_null($the_['function']) )507 call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));508 509 } while ( next($wp_filter[$tag]) !== false );510 511 467 array_pop($wp_current_filter); 512 468 } 513 469 … … 730 686 * @param array $args The collected parameters from the hook that was called. 731 687 */ 732 688 function _wp_call_all_hook($args) { 733 global $wp_filter; 734 735 reset( $wp_filter['all'] ); 736 do { 737 foreach( (array) current($wp_filter['all']) as $the_ ) 738 if ( !is_null($the_['function']) ) 739 call_user_func_array($the_['function'], $args); 740 741 } while ( next($wp_filter['all']) !== false ); 689 $iterator = new WP_Hook_Iterator('all'); 690 foreach ( $iterator as $the_ ) { 691 if ( !is_null($the_['function']) ) { 692 call_user_func_array($the_['function'], $args); 693 } 694 } 742 695 } 743 696 744 697 /** -
src/wp-settings.php
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8
65 65 require( ABSPATH . WPINC . '/functions.php' ); 66 66 require( ABSPATH . WPINC . '/class-wp.php' ); 67 67 require( ABSPATH . WPINC . '/class-wp-error.php' ); 68 require( ABSPATH . WPINC . '/class-wp-hook-iterator.php' ); 68 69 require( ABSPATH . WPINC . '/plugin.php' ); 69 70 require( ABSPATH . WPINC . '/pomo/mo.php' ); 70 71 -
src/wp-includes/class-wp-hook-iterator.php
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8
1 <?php 2 3 /** 4 * Class WP_Hook_Iterator 5 */ 6 class WP_Hook_Iterator implements Iterator { 7 private $hook = ''; 8 private $current_callback = NULL; 9 private $current_priority = NULL; 10 private $callbacks_for_current_priority = array(); 11 12 public function __construct( $hook ) { 13 $this->hook = $hook; 14 $this->rewind(); 15 } 16 17 /** 18 * Return the current element 19 * 20 * @link http://php.net/manual/en/iterator.current.php 21 * @return mixed Can return any type. 22 */ 23 public function current() { 24 return $this->current_callback; 25 } 26 27 /** 28 * Move forward to next element 29 * @link http://php.net/manual/en/iterator.next.php 30 * @return void Any returned value is ignored. 31 */ 32 public function next() { 33 $this->current_callback = NULL; 34 $next = next( $this->callbacks_for_current_priority ); 35 36 if ( $next === FALSE ) { 37 do { 38 $this->increment_priority(); 39 } while ( empty($this->callbacks_for_current_priority) && isset( $this->current_priority) ); 40 41 $next = reset( $this->callbacks_for_current_priority ); 42 } 43 44 if ( !empty($next) ) { 45 $this->current_callback = $next; 46 } 47 } 48 49 private function increment_priority() { 50 $this->current_priority = $this->get_next_priority(); 51 if ( isset($this->current_priority) ) { 52 $this->callbacks_for_current_priority = $this->get_callbacks($this->current_priority); 53 } else { 54 $this->callbacks_for_current_priority = array(); 55 } 56 } 57 58 private function get_next_priority() { 59 global $wp_filter; 60 if ( empty($wp_filter[$this->hook]) ) { 61 return NULL; 62 } 63 64 $priorities = array_keys($wp_filter[$this->hook]); 65 66 if ( !isset($this->current_priority) ) { 67 return min($priorities); // start at the beginning 68 } 69 70 $next = NULL; 71 72 // get the next greater priority 73 // this runs every time so that callbacks can be added at arbitrary times 74 foreach ( $priorities as $p ) { 75 if ( $p > $this->current_priority && ( !isset($next) || $p < $next ) ) { 76 $next = $p; 77 } 78 } 79 80 return $next; 81 } 82 83 private function get_callbacks( $priority ) { 84 global $wp_filter; 85 if ( isset($wp_filter[$this->hook][$priority]) && is_array($wp_filter[$this->hook][$priority]) ) { 86 return $wp_filter[$this->hook][$priority]; 87 } 88 return array(); 89 } 90 91 /** 92 * Return the key of the current element 93 * @link http://php.net/manual/en/iterator.key.php 94 * @return mixed scalar on success, or null on failure. 95 */ 96 public function key() { 97 if ( empty($this->current_callback) ) { 98 return NULL; 99 } 100 return _wp_filter_build_unique_id($this->hook, $this->current_callback, $this->current_priority); 101 } 102 103 /** 104 * Checks if current position is valid 105 * @link http://php.net/manual/en/iterator.valid.php 106 * @return boolean The return value will be casted to boolean and then evaluated. 107 * Returns true on success or false on failure. 108 */ 109 public function valid() { 110 return !empty($this->current_callback); 111 } 112 113 /** 114 * Rewind the Iterator to the first element 115 * @link http://php.net/manual/en/iterator.rewind.php 116 * @return void Any returned value is ignored. 117 */ 118 public function rewind() { 119 $this->current_priority = NULL; 120 $this->current_callback = NULL; 121 $this->callbacks_for_current_priority = array(); 122 $this->next(); 123 } 124 } -
tests/phpunit/tests/actions.php
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8
256 256 function action_self_removal() { 257 257 remove_action( 'test_action_self_removal', array( $this, 'action_self_removal' ) ); 258 258 } 259 260 /** 261 * @ticket 17817 262 */ 263 function test_action_recursion() { 264 $tag = rand_str(); 265 $a = new MockAction(); 266 $b = new MockAction(); 267 268 add_action( $tag, array($a, 'action'), 11, 1 ); 269 add_action( $tag, array($b, 'action'), 13, 1 ); 270 add_action( $tag, array($this, 'action_that_causes_recursion'), 12, 1 ); 271 do_action( $tag, $tag ); 272 273 $this->assertEquals( 2, $a->get_call_count(), 'recursive actions should call all callbacks with earlier priority' ); 274 $this->assertEquals( 2, $b->get_call_count(), 'recursive actions should call callbacks with later priority' ); 275 } 276 277 function action_that_causes_recursion( $tag ) { 278 static $recursing = FALSE; 279 if ( !$recursing ) { 280 $recursing = TRUE; 281 do_action( $tag, $tag ); 282 } 283 $recursing = FALSE; 284 } 285 286 /** 287 * @ticket 9968 288 */ 289 function test_action_callback_manipulation_while_running() { 290 $tag = rand_str(); 291 $a = new MockAction(); 292 $b = new MockAction(); 293 $c = new MockAction(); 294 $d = new MockAction(); 295 $e = new MockAction(); 296 297 add_action( $tag, array($a, 'action'), 11, 2 ); 298 add_action( $tag, array($this, 'action_that_manipulates_a_running_hook'), 12, 2 ); 299 add_action( $tag, array($b, 'action'), 12, 2 ); 300 301 do_action( $tag, $tag, array($a,$b,$c,$d,$e) ); 302 do_action( $tag, $tag, array($a,$b,$c,$d,$e) ); 303 304 $this->assertEquals( 2, $a->get_call_count(), 'callbacks should run unless otherwise instructed' ); 305 $this->assertEquals( 1, $b->get_call_count(), 'callback removed by same priority callback should still get called' ); 306 $this->assertEquals( 1, $c->get_call_count(), 'callback added by same priority callback should not get called' ); 307 $this->assertEquals( 2, $d->get_call_count(), 'callback added by earlier priority callback should get called' ); 308 $this->assertEquals( 1, $e->get_call_count(), 'callback added by later priority callback should not get called' ); 309 } 310 311 function action_that_manipulates_a_running_hook( $tag, $mocks ) { 312 remove_action( $tag, array($mocks[1], 'action'), 12, 2 ); 313 add_action( $tag, array($mocks[2], 'action' ), 12, 2 ); 314 add_action( $tag, array($mocks[3], 'action' ), 13, 2 ); 315 add_action( $tag, array($mocks[4], 'action' ), 10, 2 ); 316 } 259 317 } -
tests/phpunit/includes/functions.php
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8
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