Ticket #17817: 17817.7.patch
File 17817.7.patch, 17.2 KB (added by , 10 years ago) |
---|
-
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; 24 25 if ( ! isset( $wp_filter ) ) 23 require( ABSPATH . '/wp-includes/class-wp-hook.php' ); 24 /** @var WP_Hook[] $wp_filter */ 25 global $wp_filter, $wp_actions, $wp_current_filter; 26 27 if ( !empty( $wp_filter ) ) { 28 $wp_filter = WP_Hook::build_preinitialized_hooks( $wp_filter ); 29 } else { 26 30 $wp_filter = array(); 31 } 27 32 28 33 if ( ! isset( $wp_actions ) ) 29 34 $wp_actions = array(); 30 35 31 if ( ! isset( $merged_filters ) )32 $merged_filters = array();33 34 36 if ( ! isset( $wp_current_filter ) ) 35 37 $wp_current_filter = array(); 36 38 … … 66 68 * @since 0.71 67 69 * 68 70 * @global array $wp_filter A multidimensional array of all hooks and the callbacks hooked to them. 69 * @global array $merged_filters Tracks the tags that need to be merged for later. If the hook is added,70 * it doesn't need to run through that process.71 71 * 72 72 * @param string $tag The name of the filter to hook the $function_to_add callback to. 73 73 * @param callback $function_to_add The callback to be run when the filter is applied. … … 80 80 * @return boolean true 81 81 */ 82 82 function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) { 83 global $wp_filter, $merged_filters; 83 global $wp_filter; 84 if ( !isset($wp_filter[$tag]) ) { 85 $wp_filter[$tag] = new WP_Hook(); 86 } 87 $wp_filter[$tag]->add_filter( $function_to_add, $priority, $accepted_args, $tag ); 84 88 85 $idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);86 $wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);87 unset( $merged_filters[ $tag ] );88 89 return true; 89 90 } 90 91 … … 105 106 * return value. 106 107 */ 107 108 function has_filter($tag, $function_to_check = false) { 108 // Don't reset the internal array pointer 109 $wp_filter = $GLOBALS['wp_filter']; 110 111 $has = ! empty( $wp_filter[ $tag ] ); 109 global $wp_filter; 112 110 113 // Make sure at least one priority has a filter callback 114 if ( $has ) { 115 $exists = false; 116 foreach ( $wp_filter[ $tag ] as $callbacks ) { 117 if ( ! empty( $callbacks ) ) { 118 $exists = true; 119 break; 120 } 121 } 122 123 if ( ! $exists ) { 124 $has = false; 125 } 126 } 111 $has = isset($wp_filter[$tag]) && $wp_filter[$tag]->has_filters(); 127 112 128 113 if ( false === $function_to_check || false == $has ) 129 114 return $has; … … 131 116 if ( !$idx = _wp_filter_build_unique_id($tag, $function_to_check, false) ) 132 117 return false; 133 118 134 foreach ( (array) array_keys($wp_filter[$tag]) as $priority ) { 135 if ( isset($wp_filter[$tag][$priority][$idx]) ) 136 return $priority; 137 } 138 139 return false; 119 return $wp_filter[$tag]->has_filter($idx); 140 120 } 141 121 142 122 /** … … 167 147 * @since 0.71 168 148 * 169 149 * @global array $wp_filter Stores all of the filters. 170 * @global array $merged_filters Merges the filter hooks using this function.171 150 * @global array $wp_current_filter Stores the list of current filters with the current one last. 172 151 * 173 152 * @param string $tag The name of the filter hook. … … 176 155 * @return mixed The filtered value after all hooked functions are applied to it. 177 156 */ 178 157 function apply_filters( $tag, $value ) { 179 global $wp_filter, $ merged_filters, $wp_current_filter;158 global $wp_filter, $wp_current_filter; 180 159 181 160 $args = array(); 182 161 … … 185 164 $wp_current_filter[] = $tag; 186 165 $args = func_get_args(); 187 166 _wp_call_all_hook($args); 167 array_shift($args); 188 168 } 189 169 190 170 if ( !isset($wp_filter[$tag]) ) { … … 196 176 if ( !isset($wp_filter['all']) ) 197 177 $wp_current_filter[] = $tag; 198 178 199 // Sort. 200 if ( !isset( $merged_filters[ $tag ] ) ) { 201 ksort($wp_filter[$tag]); 202 $merged_filters[ $tag ] = true; 203 } 204 205 reset( $wp_filter[ $tag ] ); 206 207 if ( empty($args) ) 179 if ( empty($args) ) { 208 180 $args = func_get_args(); 181 array_shift($args); 182 } 209 183 210 do { 211 foreach( (array) current($wp_filter[$tag]) as $the_ ) 212 if ( !is_null($the_['function']) ){ 213 $args[1] = $value; 214 $value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $the_['accepted_args'])); 215 } 216 217 } while ( next($wp_filter[$tag]) !== false ); 184 $value = $wp_filter[$tag]->apply_filters( $value, $args ); 218 185 219 186 array_pop( $wp_current_filter ); 220 187 … … 230 197 * @since 3.0.0 231 198 * 232 199 * @global array $wp_filter Stores all of the filters 233 * @global array $merged_filters Merges the filter hooks using this function.234 200 * @global array $wp_current_filter Stores the list of current filters with the current one last 235 201 * 236 202 * @param string $tag The name of the filter hook. … … 238 204 * @return mixed The filtered value after all hooked functions are applied to it. 239 205 */ 240 206 function apply_filters_ref_array($tag, $args) { 241 global $wp_filter, $ merged_filters, $wp_current_filter;207 global $wp_filter, $wp_current_filter; 242 208 243 209 // Do 'all' actions first 244 210 if ( isset($wp_filter['all']) ) { … … 256 222 if ( !isset($wp_filter['all']) ) 257 223 $wp_current_filter[] = $tag; 258 224 259 // Sort 260 if ( !isset( $merged_filters[ $tag ] ) ) { 261 ksort($wp_filter[$tag]); 262 $merged_filters[ $tag ] = true; 263 } 264 265 reset( $wp_filter[ $tag ] ); 266 267 do { 268 foreach( (array) current($wp_filter[$tag]) as $the_ ) 269 if ( !is_null($the_['function']) ) 270 $args[0] = call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args'])); 271 272 } while ( next($wp_filter[$tag]) !== false ); 225 $value = $wp_filter[$tag]->apply_filters( $args[0], $args ); 273 226 274 227 array_pop( $wp_current_filter ); 275 228 276 return $ args[0];229 return $value; 277 230 } 278 231 279 232 /** … … 295 248 * @return boolean Whether the function existed before it was removed. 296 249 */ 297 250 function remove_filter( $tag, $function_to_remove, $priority = 10 ) { 298 $function_to_remove = _wp_filter_build_unique_id( $tag, $function_to_remove, $priority ); 299 300 $r = isset( $GLOBALS['wp_filter'][ $tag ][ $priority ][ $function_to_remove ] ); 251 global $wp_filter; 301 252 302 if ( true === $r ) { 303 unset( $GLOBALS['wp_filter'][ $tag ][ $priority ][ $function_to_remove ] ); 304 if ( empty( $GLOBALS['wp_filter'][ $tag ][ $priority ] ) ) { 305 unset( $GLOBALS['wp_filter'][ $tag ][ $priority ] ); 253 $r = false; 254 if ( isset($wp_filter[$tag]) ) { 255 $function_to_remove = _wp_filter_build_unique_id($tag, $function_to_remove, $priority); 256 $r = $wp_filter[$tag]->remove_filter($function_to_remove, $priority); 257 if ( empty($wp_filter[$tag]->callbacks) ) { 258 unset($wp_filter[$tag]); 306 259 } 307 if ( empty( $GLOBALS['wp_filter'][ $tag ] ) ) {308 $GLOBALS['wp_filter'][ $tag ] = array();309 }310 unset( $GLOBALS['merged_filters'][ $tag ] );311 260 } 312 261 313 262 return $r; … … 323 272 * @return bool True when finished. 324 273 */ 325 274 function remove_all_filters( $tag, $priority = false ) { 326 global $wp_filter, $merged_filters; 327 328 if ( isset( $wp_filter[ $tag ]) ) { 329 if ( false !== $priority && isset( $wp_filter[ $tag ][ $priority ] ) ) { 330 $wp_filter[ $tag ][ $priority ] = array(); 331 } else { 332 $wp_filter[ $tag ] = array(); 333 } 334 } 275 global $wp_filter; 335 276 336 if ( isset( $merged_filters[ $tag ] ) ) { 337 unset( $merged_filters[ $tag ] ); 277 if( isset($wp_filter[$tag]) ) { 278 $wp_filter[$tag]->remove_all_filters($priority); 279 unset($wp_filter[$tag]); 338 280 } 339 281 340 282 return true; … … 460 402 * @return null Will return null if $tag does not exist in $wp_filter array. 461 403 */ 462 404 function do_action($tag, $arg = '') { 463 global $wp_filter, $wp_actions, $ merged_filters, $wp_current_filter;405 global $wp_filter, $wp_actions, $wp_current_filter; 464 406 465 407 if ( ! isset($wp_actions[$tag]) ) 466 408 $wp_actions[$tag] = 1; … … 491 433 for ( $a = 2; $a < func_num_args(); $a++ ) 492 434 $args[] = func_get_arg($a); 493 435 494 // Sort 495 if ( !isset( $merged_filters[ $tag ] ) ) { 496 ksort($wp_filter[$tag]); 497 $merged_filters[ $tag ] = true; 498 } 499 500 reset( $wp_filter[ $tag ] ); 501 502 do { 503 foreach ( (array) current($wp_filter[$tag]) as $the_ ) 504 if ( !is_null($the_['function']) ) 505 call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args'])); 506 507 } while ( next($wp_filter[$tag]) !== false ); 436 $wp_filter[$tag]->do_action( $args ); 508 437 509 438 array_pop($wp_current_filter); 510 439 } … … 543 472 * @return null Will return null if $tag does not exist in $wp_filter array 544 473 */ 545 474 function do_action_ref_array($tag, $args) { 546 global $wp_filter, $wp_actions, $ merged_filters, $wp_current_filter;475 global $wp_filter, $wp_actions, $wp_current_filter; 547 476 548 477 if ( ! isset($wp_actions[$tag]) ) 549 478 $wp_actions[$tag] = 1; … … 566 495 if ( !isset($wp_filter['all']) ) 567 496 $wp_current_filter[] = $tag; 568 497 569 // Sort 570 if ( !isset( $merged_filters[ $tag ] ) ) { 571 ksort($wp_filter[$tag]); 572 $merged_filters[ $tag ] = true; 573 } 574 575 reset( $wp_filter[ $tag ] ); 576 577 do { 578 foreach( (array) current($wp_filter[$tag]) as $the_ ) 579 if ( !is_null($the_['function']) ) 580 call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args'])); 581 582 } while ( next($wp_filter[$tag]) !== false ); 498 $wp_filter[$tag]->do_action( $args ); 583 499 584 500 array_pop($wp_current_filter); 585 501 } … … 838 754 */ 839 755 function _wp_call_all_hook($args) { 840 756 global $wp_filter; 841 842 reset( $wp_filter['all'] ); 843 do { 844 foreach( (array) current($wp_filter['all']) as $the_ ) 845 if ( !is_null($the_['function']) ) 846 call_user_func_array($the_['function'], $args); 847 848 } while ( next($wp_filter['all']) !== false ); 757 $wp_filter['all']->do_all_hook($args); 849 758 } 850 759 851 760 /** -
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
79 79 * @return void 80 80 */ 81 81 protected function _backup_hooks() { 82 $globals = array( ' merged_filters', 'wp_actions', 'wp_current_filter', 'wp_filter' );82 $globals = array( 'wp_actions', 'wp_current_filter', 'wp_filter' ); 83 83 foreach ( $globals as $key ) { 84 84 self::$hooks_saved[ $key ] = $GLOBALS[ $key ]; 85 85 } … … 96 96 * @return void 97 97 */ 98 98 protected function _restore_hooks() { 99 $globals = array( ' merged_filters', 'wp_actions', 'wp_current_filter', 'wp_filter' );99 $globals = array( 'wp_actions', 'wp_current_filter', 'wp_filter' ); 100 100 foreach ( $globals as $key ) { 101 101 if ( isset( self::$hooks_saved[ $key ] ) ) { 102 102 $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 function test_action_args_3() { 122 $a1 = new MockAction(); 123 $a2 = new MockAction(); 124 $a3 = new MockAction(); 125 $tag = rand_str(); 126 $val1 = rand_str(); 127 $val2 = rand_str(); 128 129 // a1 accepts two arguments, a2 doesn't, a3 accepts two arguments 130 add_action($tag, array(&$a1, 'action'), 10, 2); 131 add_action($tag, array(&$a2, 'action')); 132 add_action($tag, array(&$a3, 'action'), 10, 2); 133 // call the action with two arguments 134 do_action($tag, $val1, $val2); 135 136 $call_count = $a1->get_call_count(); 137 // a1 should be called with both args 138 $this->assertEquals(1, $call_count); 139 $argsvar1 = $a1->get_args(); 140 $this->assertEquals( array( $val1, $val2 ), array_pop( $argsvar1 ) ); 141 142 // a2 should be called with one only 143 $this->assertEquals(1, $a2->get_call_count()); 144 $argsvar2 = $a2->get_args(); 145 $this->assertEquals( array( $val1 ), array_pop( $argsvar2 ) ); 146 147 // a3 should be called with both args 148 $this->assertEquals(1, $a3->get_call_count()); 149 $argsvar3 = $a3->get_args(); 150 $this->assertEquals( array( $val1, $val2 ), array_pop( $argsvar3 ) ); 151 } 152 117 153 function test_action_priority() { 118 154 $a = new MockAction(); 119 155 $tag = rand_str(); … … 258 294 } 259 295 260 296 /** 297 * @ticket 17817 298 */ 299 function test_action_recursion() { 300 $tag = rand_str(); 301 $a = new MockAction(); 302 $b = new MockAction(); 303 304 add_action( $tag, array($a, 'action'), 11, 1 ); 305 add_action( $tag, array($b, 'action'), 13, 1 ); 306 add_action( $tag, array($this, 'action_that_causes_recursion'), 12, 1 ); 307 do_action( $tag, $tag ); 308 309 $this->assertEquals( 2, $a->get_call_count(), 'recursive actions should call all callbacks with earlier priority' ); 310 $this->assertEquals( 2, $b->get_call_count(), 'recursive actions should call callbacks with later priority' ); 311 } 312 313 function action_that_causes_recursion( $tag ) { 314 static $recursing = FALSE; 315 if ( !$recursing ) { 316 $recursing = TRUE; 317 do_action( $tag, $tag ); 318 } 319 $recursing = FALSE; 320 } 321 322 /** 323 * @ticket 9968 324 */ 325 function test_action_callback_manipulation_while_running() { 326 $tag = rand_str(); 327 $a = new MockAction(); 328 $b = new MockAction(); 329 $c = new MockAction(); 330 $d = new MockAction(); 331 $e = new MockAction(); 332 333 add_action( $tag, array($a, 'action'), 11, 2 ); 334 add_action( $tag, array($this, 'action_that_manipulates_a_running_hook'), 12, 2 ); 335 add_action( $tag, array($b, 'action'), 12, 2 ); 336 337 do_action( $tag, $tag, array($a,$b,$c,$d,$e) ); 338 do_action( $tag, $tag, array($a,$b,$c,$d,$e) ); 339 340 $this->assertEquals( 2, $a->get_call_count(), 'callbacks should run unless otherwise instructed' ); 341 $this->assertEquals( 1, $b->get_call_count(), 'callback removed by same priority callback should still get called' ); 342 $this->assertEquals( 1, $c->get_call_count(), 'callback added by same priority callback should not get called' ); 343 $this->assertEquals( 2, $d->get_call_count(), 'callback added by earlier priority callback should get called' ); 344 $this->assertEquals( 1, $e->get_call_count(), 'callback added by later priority callback should not get called' ); 345 } 346 347 function action_that_manipulates_a_running_hook( $tag, $mocks ) { 348 remove_action( $tag, array($mocks[1], 'action'), 12, 2 ); 349 add_action( $tag, array($mocks[2], 'action' ), 12, 2 ); 350 add_action( $tag, array($mocks[3], 'action' ), 13, 2 ); 351 add_action( $tag, array($mocks[4], 'action' ), 10, 2 ); 352 } 353 354 /** 355 * @ticket 17817 356 * 357 * This specificaly addresses the concern raised at 358 * https://core.trac.wordpress.org/ticket/17817#comment:52 359 */ 360 function test_remove_anonymous_callback() { 361 $tag = rand_str(); 362 $a = new MockAction(); 363 add_action( $tag, array( $a, 'action' ), 12, 1 ); 364 $this->assertTrue( has_action( $tag ) ); 365 366 $hook = $GLOBALS['wp_filter'][ $tag ]; 367 368 // From http://wordpress.stackexchange.com/a/57088/6445 369 foreach ( $hook as $priority => $filter ) { 370 foreach ( $filter as $identifier => $function ) { 371 if ( is_array( $function) 372 && is_a( $function['function'][0], 'MockAction' ) 373 && 'action' === $function['function'][1] 374 ) { 375 remove_filter( 376 $tag, 377 array ( $function['function'][0], 'action' ), 378 $priority 379 ); 380 } 381 } 382 } 383 384 $this->assertFalse( has_action( $tag ) ); 385 } 386 387 /** 261 388 * Make sure current_action() behaves as current_filter() 262 389 * 263 390 * @ticket 14994 -
tests/phpunit/tests/filters.php
293 293 remove_all_filters( $tag, 12 ); 294 294 $this->assertFalse( has_filter( $tag ) ); 295 295 } 296 297 /**298 * @ticket 29070299 */300 function test_has_filter_doesnt_reset_wp_filter() {301 add_action( 'action_test_has_filter_doesnt_reset_wp_filter', '__return_null', 1 );302 add_action( 'action_test_has_filter_doesnt_reset_wp_filter', '__return_null', 2 );303 add_action( 'action_test_has_filter_doesnt_reset_wp_filter', '__return_null', 3 );304 add_action( 'action_test_has_filter_doesnt_reset_wp_filter', array( $this, '_action_test_has_filter_doesnt_reset_wp_filter' ), 4 );305 306 do_action( 'action_test_has_filter_doesnt_reset_wp_filter' );307 }308 function _action_test_has_filter_doesnt_reset_wp_filter() {309 global $wp_filter;310 311 has_action( 'action_test_has_filter_doesnt_reset_wp_filter', '_function_that_doesnt_exist' );312 313 $filters = current( $wp_filter['action_test_has_filter_doesnt_reset_wp_filter'] );314 $the_ = current( $filters );315 $this->assertEquals( $the_['function'], array( $this, '_action_test_has_filter_doesnt_reset_wp_filter' ) );316 }317 296 }