Ticket #17817: 17817.6.patch
File 17817.6.patch, 25.3 KB (added by , 10 years ago) |
---|
-
new file src/wp-includes/class-wp-hook.php
diff --git src/wp-includes/class-wp-hook.php src/wp-includes/class-wp-hook.php new file mode 100644 index 0000000..748e439
- + 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 //reset($this->callbacks[$priority][$idx]); 31 if ( !$priority_existed && count($this->callbacks) > 1 ) { 32 ksort($this->callbacks, SORT_NUMERIC); 33 } 34 35 if ( $this->nesting_level > 0 ) { 36 $this->resort_active_iterations(); 37 } 38 } 39 40 /** 41 * When a hook's callbacks are changed mid-iteration, the priority 42 * keys need to be reset, with the array pointer at the correct 43 * location 44 * 45 * @return void 46 */ 47 private function resort_active_iterations() { 48 $new_priorities = array_keys($this->callbacks); 49 50 // if there are no remaining hooks, clear out all running iterations 51 if ( empty($new_priorities) ) { 52 foreach ( $this->iterations as $index => $iteration ) { 53 $this->iterations[$index] = $new_priorities; 54 } 55 return; 56 } 57 58 $min = min($new_priorities); 59 foreach ( $this->iterations as $index => $iteration ) { 60 $current = current($iteration); 61 $this->iterations[$index] = $new_priorities; 62 63 if ( $current < $min ) { 64 array_unshift( $this->iterations[$index], $current ); 65 continue; 66 } 67 while ( current($this->iterations[$index]) < $current ) { 68 if ( next($this->iterations[$index]) === FALSE ) { 69 break; 70 }; 71 } 72 } 73 } 74 75 /** 76 * @param string $function_key 77 * @param int $priority 78 * 79 * @return bool Whether the callback existed before it was removed 80 */ 81 public function remove_filter( $function_key, $priority ) { 82 $exists = isset($this->callbacks[$priority][$function_key]); 83 if ( $exists ) { 84 unset($this->callbacks[$priority][$function_key]); 85 if ( empty($this->callbacks[$priority]) ) { 86 unset($this->callbacks[$priority]); 87 if ( $this->nesting_level > 0 ) { 88 $this->resort_active_iterations(); 89 } 90 } 91 } 92 return $exists; 93 } 94 95 /** 96 * Check if an specific action has been registered for this hook. 97 * 98 * @param string $function_key The hashed index of the filter 99 * @return mixed The priority of that hook is returned, or false if the function is not attached. 100 */ 101 public function has_filter( $function_key ) { 102 foreach ( $this->callbacks as $priority => &$callbacks ) { 103 if ( isset($callbacks[$function_key]) ) { 104 return $priority; 105 } 106 } 107 return false; 108 } 109 110 /** 111 * Check if any callbacks have been registered for this hook 112 * 113 * @return bool 114 */ 115 public function has_filters() { 116 foreach ( $this->callbacks as &$callbacks ) { 117 if ( !empty($callbacks) ) { 118 return TRUE; 119 } 120 } 121 return FALSE; 122 } 123 124 /** 125 * Remove all of the callbacks from the filter. 126 * 127 * @param int|bool $priority The priority number to remove. 128 * @return void 129 */ 130 public function remove_all_filters( $priority = false ) { 131 if ( empty($this->callbacks) ) { 132 return; 133 } 134 if( false !== $priority && isset($this->callbacks[$priority]) ) { 135 unset($this->callbacks[$priority]); 136 } else { 137 $this->callbacks = array(); 138 } 139 if ( $this->nesting_level > 0 ) { 140 $this->resort_active_iterations(); 141 } 142 } 143 144 /** 145 * Call the functions added to a filter hook. 146 * 147 * @param mixed $value The value to filter. 148 * @param array $args Arguments to pass to callbacks 149 * 150 * @return mixed The filtered value after all hooked functions are applied to it 151 */ 152 public function apply_filters( $value, &$args ) { 153 if ( empty($this->callbacks) ) { 154 return $value; 155 } 156 $nesting_level = $this->nesting_level++; 157 $this->iterations[$nesting_level] = array_keys($this->callbacks); 158 159 do { 160 $priority = current($this->iterations[$nesting_level]); 161 if ( $priority === false ) { 162 $debug = 'test'; 163 } 164 foreach ( $this->callbacks[$priority] as $the_ ) { 165 $args[0] = $value; 166 $value = call_user_func_array( $the_['function'], array_slice( $args, 0, (int)$the_['accepted_args'] ) ); 167 } 168 } while ( next($this->iterations[$nesting_level]) !== FALSE ); 169 170 unset($this->iterations[$nesting_level]); 171 $this->nesting_level--; 172 return $value; 173 } 174 175 /** 176 * Execute functions hooked on a specific action hook. 177 * 178 * @param mixed $args Arguments to pass to callbacks 179 * @return void 180 */ 181 public function do_action( &$args ) { 182 if ( empty($this->callbacks) ) { 183 return; 184 } 185 $nesting_level = $this->nesting_level++; 186 $this->iterations[$nesting_level] = array_keys($this->callbacks); 187 $num_args = count($args); 188 189 do { 190 $priority = current($this->iterations[$nesting_level]); 191 foreach ( $this->callbacks[$priority] as $the_ ) { 192 $func =& $the_['function']; 193 if ( $the_['accepted_args'] == 0 ) { 194 $func_args = array(); 195 } elseif ( $the_['accepted_args'] >= $num_args ) { 196 $func_args =& $args; 197 } else { 198 $func_args = array_slice( $args, 0, (int) $the_['accepted_args'] ); 199 } 200 call_user_func_array($func, $func_args ); 201 } 202 } while ( next($this->iterations[$nesting_level]) !== FALSE ); 203 204 unset($this->iterations[$nesting_level]); 205 $this->nesting_level--; 206 } 207 208 /** 209 * Process the functions hooked into the 'all' hook. 210 * 211 * @param $args Arguments to pass to callbacks 212 * @return void 213 */ 214 public function do_all_hook( &$args ) { 215 $nesting_level = $this->nesting_level++; 216 $this->iterations[$nesting_level] = array_keys($this->callbacks); 217 218 do { 219 $priority = current($this->iterations[$nesting_level]); 220 foreach ( $this->callbacks[$priority] as $the_ ) { 221 call_user_func_array($the_['function'], $args ); 222 } 223 } while ( next($this->iterations[$nesting_level]) !== FALSE ); 224 225 unset($this->iterations[$nesting_level]); 226 $this->nesting_level--; 227 } 228 229 /** 230 * Retrieve an external iterator 231 * 232 * Provided for backwards compatibility with plugins that iterate over the 233 * $wp_filter global 234 * 235 * @link http://php.net/manual/en/iteratoraggregate.getiterator.php 236 * @return Traversable An instance of an object implementing Iterator or 237 * Traversable 238 */ 239 public function getIterator() { 240 return new ArrayIterator($this->callbacks); 241 } 242 243 /** 244 * Count elements of an object 245 * 246 * Provided for backwards compatibility with plugins that access the 247 * $wp_filter global 248 * 249 * @link http://php.net/manual/en/countable.count.php 250 * @return int The custom count as an integer. 251 * 252 * The return value is cast to an integer. 253 */ 254 public function count() { 255 return count($this->callbacks); 256 } 257 258 /** 259 * Some plugins may set up filters before WordPress has initialized. 260 * Normalize them to WP_Hook objects. 261 * 262 * @param array $filters 263 * @return WP_Hook[] 264 */ 265 public static function build_preinitialized_hooks( $filters ) { 266 /** @var WP_Hook[] $normalized */ 267 $normalized = array(); 268 foreach ( $filters as $tag => $callback_groups ) { 269 if ( is_object($callback_groups) && $callback_groups instanceof WP_Hook ) { 270 $normalized[$tag] = $callback_groups; 271 continue; 272 } 273 $hook = new WP_Hook(); 274 foreach ( $callback_groups as $priority => $callbacks ) { 275 foreach ( $callbacks as $cb ) { 276 $hook->add_filter( $cb['function'], $priority, $cb['accepted_args'], $tag ); 277 } 278 } 279 $normalized[$tag] = $hook; 280 } 281 return $normalized; 282 } 283 284 } 285 -
src/wp-includes/plugin.php
diff --git src/wp-includes/plugin.php src/wp-includes/plugin.php index d616a2a..f94730b 100644
20 20 */ 21 21 22 22 // Initialize the filter globals. 23 global $wp_filter, $wp_actions, $merged_filters, $wp_current_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; 24 26 25 if ( ! isset( $wp_filter ) ) 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 … … if ( ! isset( $wp_current_filter ) ) 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. … … if ( ! isset( $wp_current_filter ) ) 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 … … function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 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 ] ); 112 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 } 109 global $wp_filter; 122 110 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; … … function has_filter($tag, $function_to_check = false) { 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 /** … … function has_filter($tag, $function_to_check = false) { 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. … … function has_filter($tag, $function_to_check = false) { 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 … … function apply_filters( $tag, $value ) { 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]) ) { … … function apply_filters( $tag, $value ) { 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 … … function apply_filters( $tag, $value ) { 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. … … function apply_filters( $tag, $value ) { 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']) ) { … … function apply_filters_ref_array($tag, $args) { 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 /** … … function apply_filters_ref_array($tag, $args) { 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 ] ); 306 } 307 if ( empty( $GLOBALS['wp_filter'][ $tag ] ) ) { 308 $GLOBALS['wp_filter'][ $tag ] = array(); 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]); 309 259 } 310 unset( $GLOBALS['merged_filters'][ $tag ] );311 260 } 312 261 313 262 return $r; … … function remove_filter( $tag, $function_to_remove, $priority = 10 ) { 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; … … function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) 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; … … function do_action($tag, $arg = '') { 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 } … … function did_action($tag) { 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; … … function do_action_ref_array($tag, $args) { 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 } … … function register_uninstall_hook( $file, $callback ) { 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
diff --git tests/phpunit/includes/functions.php tests/phpunit/includes/functions.php index e8d30cb..64f28f7 100644
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
diff --git tests/phpunit/includes/testcase.php tests/phpunit/includes/testcase.php index 1e8ea11..bac2538 100644
class WP_UnitTestCase extends PHPUnit_Framework_TestCase { 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 } … … class WP_UnitTestCase extends PHPUnit_Framework_TestCase { 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
diff --git tests/phpunit/tests/actions.php tests/phpunit/tests/actions.php index 583c8ce..fc1e89c 100644
class Tests_Actions extends WP_UnitTestCase { 258 258 } 259 259 260 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 } 317 318 /** 319 * @ticket 17817 320 * 321 * This specificaly addresses the concern raised at 322 * https://core.trac.wordpress.org/ticket/17817#comment:52 323 */ 324 function test_remove_anonymous_callback() { 325 $tag = rand_str(); 326 $a = new MockAction(); 327 add_action( $tag, array( $a, 'action' ), 12, 1 ); 328 $this->assertTrue( has_action( $tag ) ); 329 330 $hook = $GLOBALS['wp_filter'][ $tag ]; 331 332 // From http://wordpress.stackexchange.com/a/57088/6445 333 foreach ( $hook as $priority => $filter ) { 334 foreach ( $filter as $identifier => $function ) { 335 if ( is_array( $function) 336 && is_a( $function['function'][0], 'MockAction' ) 337 && 'action' === $function['function'][1] 338 ) { 339 remove_filter( 340 $tag, 341 array ( $function['function'][0], 'action' ), 342 $priority 343 ); 344 } 345 } 346 } 347 348 $this->assertFalse( has_action( $tag ) ); 349 } 350 351 /** 261 352 * Make sure current_action() behaves as current_filter() 262 353 * 263 354 * @ticket 14994 -
tests/phpunit/tests/filters.php
diff --git tests/phpunit/tests/filters.php tests/phpunit/tests/filters.php index b4ee12f..34fdf56 100644
class Tests_Filters extends WP_UnitTestCase { 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 }