Ticket #17817: 17817.8.patch
File 17817.8.patch, 25.7 KB (added by , 10 years ago) |
---|
-
src/wp-includes/class-wp-hook.php
1 <?php 2 3 /** 4 * Class WP_Hook 5 */ 6 class WP_Hook implements IteratorAggregate, Countable { 7 public $callbacks = array(); 8 9 /** @var array The priority keys of actively running iterations of a hook */ 10 private $iterations = array(); 11 12 /** @var int How recursively has this hook been called? */ 13 private $nesting_level = 0; 14 15 /** 16 * Hook a function or method to a specific filter action. 17 * 18 * @param callback $function_to_add The callback to be run when the filter is applied. 19 * @param int $priority (optional) The order in which the functions associated with a particular action are executed. 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. 20 * Default 10. 21 * @param int $accepted_args (optional) The number of arguments the function accepts. 22 * Default 1. 23 * @param string $tag The name of the filter to hook the $function_to_add callback to. 24 * @return void 25 */ 26 public function add_filter( $function_to_add, $priority, $accepted_args, $tag ) { 27 $idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority); 28 $priority_existed = isset($this->callbacks[$priority]); 29 $this->callbacks[$priority][$idx] = array( 'function' => $function_to_add, 'accepted_args' => $accepted_args ); 30 if ( !$priority_existed && count($this->callbacks) > 1 ) { 31 ksort($this->callbacks, SORT_NUMERIC); 32 } 33 34 if ( $this->nesting_level > 0 ) { 35 $this->resort_active_iterations(); 36 } 37 } 38 39 /** 40 * When a hook's callbacks are changed mid-iteration, the priority 41 * keys need to be reset, with the array pointer at the correct 42 * location 43 * 44 * @return void 45 */ 46 private function resort_active_iterations() { 47 $new_priorities = array_keys($this->callbacks); 48 49 // if there are no remaining hooks, clear out all running iterations 50 if ( empty($new_priorities) ) { 51 foreach ( $this->iterations as $index => $iteration ) { 52 $this->iterations[$index] = $new_priorities; 53 } 54 return; 55 } 56 57 $min = min($new_priorities); 58 foreach ( $this->iterations as $index => $iteration ) { 59 $current = current($iteration); 60 $this->iterations[$index] = $new_priorities; 61 62 if ( $current < $min ) { 63 array_unshift( $this->iterations[$index], $current ); 64 continue; 65 } 66 while ( current($this->iterations[$index]) < $current ) { 67 if ( next($this->iterations[$index]) === FALSE ) { 68 break; 69 }; 70 } 71 } 72 } 73 74 /** 75 * @param string $function_key 76 * @param int $priority 77 * 78 * @return bool Whether the callback existed before it was removed 79 */ 80 public function remove_filter( $function_key, $priority ) { 81 $exists = isset($this->callbacks[$priority][$function_key]); 82 if ( $exists ) { 83 unset($this->callbacks[$priority][$function_key]); 84 if ( empty($this->callbacks[$priority]) ) { 85 unset($this->callbacks[$priority]); 86 if ( $this->nesting_level > 0 ) { 87 $this->resort_active_iterations(); 88 } 89 } 90 } 91 return $exists; 92 } 93 94 /** 95 * Check if an specific action has been registered for this hook. 96 * 97 * @param string $function_key The hashed index of the filter 98 * @return mixed The priority of that hook is returned, or false if the function is not attached. 99 */ 100 public function has_filter( $function_key ) { 101 foreach ( $this->callbacks as $priority => &$callbacks ) { 102 if ( isset($callbacks[$function_key]) ) { 103 return $priority; 104 } 105 } 106 return false; 107 } 108 109 /** 110 * Check if any callbacks have been registered for this hook 111 * 112 * @return bool 113 */ 114 public function has_filters() { 115 foreach ( $this->callbacks as &$callbacks ) { 116 if ( !empty($callbacks) ) { 117 return TRUE; 118 } 119 } 120 return FALSE; 121 } 122 123 /** 124 * Remove all of the callbacks from the filter. 125 * 126 * @param int|bool $priority The priority number to remove. 127 * @return void 128 */ 129 public function remove_all_filters( $priority = false ) { 130 if ( empty($this->callbacks) ) { 131 return; 132 } 133 if( false !== $priority && isset($this->callbacks[$priority]) ) { 134 unset($this->callbacks[$priority]); 135 } else { 136 $this->callbacks = array(); 137 } 138 if ( $this->nesting_level > 0 ) { 139 $this->resort_active_iterations(); 140 } 141 } 142 143 /** 144 * Call the functions added to a filter hook. 145 * 146 * @param mixed $value The value to filter. 147 * @param array $args Arguments to pass to callbacks 148 * 149 * @return mixed The filtered value after all hooked functions are applied to it 150 */ 151 public function apply_filters( $value, &$args ) { 152 if ( empty($this->callbacks) ) { 153 return $value; 154 } 155 $nesting_level = $this->nesting_level++; 156 $this->iterations[$nesting_level] = array_keys($this->callbacks); 157 158 do { 159 $priority = current($this->iterations[$nesting_level]); 160 foreach ( $this->callbacks[$priority] as $the_ ) { 161 $args[0] = $value; 162 $value = call_user_func_array( $the_['function'], array_slice( $args, 0, (int)$the_['accepted_args'] ) ); 163 } 164 } while ( next($this->iterations[$nesting_level]) !== FALSE ); 165 166 unset($this->iterations[$nesting_level]); 167 $this->nesting_level--; 168 return $value; 169 } 170 171 /** 172 * Execute functions hooked on a specific action hook. 173 * 174 * @param mixed $args Arguments to pass to callbacks 175 * @return void 176 */ 177 public function do_action( &$args ) { 178 179 if ( empty($this->callbacks) ) { 180 return; 181 } 182 $nesting_level = $this->nesting_level++; 183 $this->iterations[$nesting_level] = array_keys($this->callbacks); 184 $num_args = count($args); 185 186 do { 187 $priority = current($this->iterations[$nesting_level]); 188 foreach ( $this->callbacks[$priority] as $the_ ) { 189 $func =& $the_['function']; 190 if ( $the_['accepted_args'] == 0 ) { 191 $func_args = array(); 192 } elseif ( $the_['accepted_args'] >= $num_args ) { 193 $func_args = $args; 194 } else { 195 $func_args = array_slice( $args, 0, (int) $the_['accepted_args'] ); 196 } 197 call_user_func_array($func, $func_args ); 198 } 199 } while ( next($this->iterations[$nesting_level]) !== FALSE ); 200 201 unset($this->iterations[$nesting_level]); 202 $this->nesting_level--; 203 } 204 205 /** 206 * Process the functions hooked into the 'all' hook. 207 * 208 * @param array $args Arguments to pass to callbacks 209 * @return void 210 */ 211 public function do_all_hook( &$args ) { 212 $nesting_level = $this->nesting_level++; 213 $this->iterations[$nesting_level] = array_keys($this->callbacks); 214 215 do { 216 $priority = current($this->iterations[$nesting_level]); 217 foreach ( $this->callbacks[$priority] as $the_ ) { 218 call_user_func_array($the_['function'], $args ); 219 } 220 } while ( next($this->iterations[$nesting_level]) !== FALSE ); 221 222 unset($this->iterations[$nesting_level]); 223 $this->nesting_level--; 224 } 225 226 /** 227 * Retrieve an external iterator 228 * 229 * Provided for backwards compatibility with plugins that iterate over the 230 * $wp_filter global 231 * 232 * @link http://php.net/manual/en/iteratoraggregate.getiterator.php 233 * @return Traversable An instance of an object implementing Iterator or 234 * Traversable 235 */ 236 public function getIterator() { 237 return new ArrayIterator($this->callbacks); 238 } 239 240 /** 241 * Count elements of an object 242 * 243 * Provided for backwards compatibility with plugins that access the 244 * $wp_filter global 245 * 246 * @link http://php.net/manual/en/countable.count.php 247 * @return int The custom count as an integer. 248 * 249 * The return value is cast to an integer. 250 */ 251 public function count() { 252 return count($this->callbacks); 253 } 254 255 /** 256 * Some plugins may set up filters before WordPress has initialized. 257 * Normalize them to WP_Hook objects. 258 * 259 * @param array $filters 260 * @return WP_Hook[] 261 */ 262 public static function build_preinitialized_hooks( $filters ) { 263 /** @var WP_Hook[] $normalized */ 264 $normalized = array(); 265 foreach ( $filters as $tag => $callback_groups ) { 266 if ( is_object($callback_groups) && $callback_groups instanceof WP_Hook ) { 267 $normalized[$tag] = $callback_groups; 268 continue; 269 } 270 $hook = new WP_Hook(); 271 foreach ( $callback_groups as $priority => $callbacks ) { 272 foreach ( $callbacks as $cb ) { 273 $hook->add_filter( $cb['function'], $priority, $cb['accepted_args'], $tag ); 274 } 275 } 276 $normalized[$tag] = $hook; 277 } 278 return $normalized; 279 } 280 281 } 282 -
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 }