Ticket #17817: 17817.13.patch
File 17817.13.patch, 30.0 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..2ca730e
- + 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 we're adding a new priority to the list, put them back in sorted order 47 if ( ! $priority_existed && count( $this->callbacks ) > 1 ) { 48 ksort( $this->callbacks, SORT_NUMERIC ); 49 } 50 51 if ( $this->nesting_level > 0 ) { 52 $this->resort_active_iterations(); 53 } 54 } 55 56 /** 57 * When a hook's callbacks are changed mid-iteration, the priority 58 * keys need to be reset, with the array pointer at the correct location 59 */ 60 private function resort_active_iterations() { 61 $new_priorities = array_keys( $this->callbacks ); 62 63 // if there are no remaining hooks, clear out all running iterations 64 if ( ! $new_priorities ) { 65 foreach ( $this->iterations as $index => $iteration ) { 66 $this->iterations[ $index ] = $new_priorities; 67 } 68 return; 69 } 70 71 $min = min( $new_priorities ); 72 foreach ( $this->iterations as $index => $iteration ) { 73 $current = current( $iteration ); 74 $this->iterations[ $index ] = $new_priorities; 75 76 if ( $current < $min ) { 77 array_unshift( $this->iterations[ $index ], $current ); 78 continue; 79 } 80 while ( current( $this->iterations[ $index ] ) < $current ) { 81 if ( false === next( $this->iterations[ $index ] ) ) { 82 break; 83 }; 84 } 85 } 86 } 87 88 /** 89 * @param callable $function_to_remove 90 * @param int $priority 91 * @param string $tag The filter hook to which the function to be removed is hooked. 92 * Used for building the callback ID when SPL is not available. 93 * 94 * @return bool Whether the callback existed before it was removed 95 */ 96 public function remove_filter( $function_to_remove, $priority, $tag ) { 97 $function_key = _wp_filter_build_unique_id( $tag, $function_to_remove, $priority ); 98 99 $exists = isset( $this->callbacks[ $priority ][ $function_key ] ); 100 if ( $exists ) { 101 unset( $this->callbacks[ $priority ][ $function_key ] ); 102 if ( ! $this->callbacks[ $priority ] ) { 103 unset( $this->callbacks[ $priority ] ); 104 if ( $this->nesting_level > 0 ) { 105 $this->resort_active_iterations(); 106 } 107 } 108 } 109 return $exists; 110 } 111 112 /** 113 * Check if a specific action has been registered for this hook. 114 * 115 * @param callable|bool $function_to_check Optional. The callback to check for. Default false. 116 * @param string $tag The name of the filter hook. 117 * Used for building the callback ID when SPL is not available. 118 * @return bool|int The priority of that hook is returned, or false if the function is not attached. 119 */ 120 public function has_filter( $function_to_check = false, $tag = '' ) { 121 if ( false === $function_to_check ) { 122 return $this->has_filters(); 123 } 124 125 $function_key = _wp_filter_build_unique_id( $tag, $function_to_check, false ); 126 if ( !$function_key ) { 127 return false; 128 } 129 130 foreach ( $this->callbacks as $priority => $callbacks ) { 131 if ( isset( $callbacks[ $function_key ] ) ) { 132 return $priority; 133 } 134 } 135 136 return false; 137 } 138 139 /** 140 * Check if any callbacks have been registered for this hook 141 * 142 * @return bool 143 */ 144 public function has_filters() { 145 foreach ( $this->callbacks as $callbacks ) { 146 if ( $callbacks ) { 147 return true; 148 } 149 } 150 return false; 151 } 152 153 /** 154 * Remove all of the callbacks from the filter. 155 * 156 * @param int|bool $priority The priority number to remove. 157 */ 158 public function remove_all_filters( $priority = false ) { 159 if ( ! $this->callbacks ) { 160 return; 161 } 162 163 if ( false === $priority ) { 164 $this->callbacks = array(); 165 } else if ( isset( $this->callbacks[ $priority ] ) ) { 166 unset( $this->callbacks[ $priority ] ); 167 } 168 169 if ( $this->nesting_level > 0 ) { 170 $this->resort_active_iterations(); 171 } 172 } 173 174 /** 175 * Call the functions added to a filter hook. 176 * 177 * @param mixed $value The value to filter. 178 * @param array $args Arguments to pass to callbacks 179 * 180 * @return mixed The filtered value after all hooked functions are applied to it 181 */ 182 public function apply_filters( $value, $args ) { 183 if ( ! $this->callbacks ) { 184 return $value; 185 } 186 $nesting_level = $this->nesting_level++; 187 $this->iterations[ $nesting_level ] = array_keys( $this->callbacks ); 188 $num_args = count( $args ); 189 190 do { 191 $priority = current( $this->iterations[ $nesting_level ] ); 192 foreach ( $this->callbacks[ $priority ] as $the_ ) { 193 $args[ 0 ] = $value; 194 // avoid the array_slice if possible 195 if ( $the_['accepted_args'] == 0 ) { 196 $value = call_user_func_array( $the_['function'], array() ); 197 } elseif ( $the_['accepted_args'] >= $num_args ) { 198 $value = call_user_func_array( $the_['function'], $args ); 199 } else { 200 $value = call_user_func_array( $the_['function'], array_slice( $args, 0, (int)$the_['accepted_args'] ) ); 201 } 202 } 203 } while ( false !== next( $this->iterations[ $nesting_level ] ) ); 204 205 unset( $this->iterations[ $nesting_level ] ); 206 $this->nesting_level--; 207 return $value; 208 } 209 210 /** 211 * Execute functions hooked on a specific action hook. 212 * 213 * @param mixed $args Arguments to pass to callbacks 214 */ 215 public function do_action( $args ) { 216 if ( ! $this->callbacks ) { 217 return; 218 } 219 $nesting_level = $this->nesting_level++; 220 $this->iterations[ $nesting_level ] = array_keys( $this->callbacks ); 221 $num_args = count( $args ); 222 223 do { 224 $priority = current( $this->iterations[ $nesting_level ] ); 225 foreach ( $this->callbacks[ $priority ] as $the_ ) { 226 // avoid the array_slice if possible 227 if ( $the_['accepted_args'] == 0 ) { 228 call_user_func_array( $the_['function'], array() ); 229 } elseif ( $the_['accepted_args'] >= $num_args ) { 230 call_user_func_array( $the_['function'], $args ); 231 } else { 232 call_user_func_array( $the_['function'], array_slice( $args, 0, (int)$the_['accepted_args'] ) ); 233 } 234 } 235 } while ( next( $this->iterations[ $nesting_level ] ) !== false ); 236 237 unset( $this->iterations[ $nesting_level ] ); 238 $this->nesting_level--; 239 } 240 241 /** 242 * Process the functions hooked into the 'all' hook. 243 * 244 * @param array $args Arguments to pass to callbacks 245 */ 246 public function do_all_hook( &$args ) { 247 $nesting_level = $this->nesting_level++; 248 $this->iterations[ $nesting_level ] = array_keys( $this->callbacks ); 249 250 do { 251 $priority = current( $this->iterations[ $nesting_level ] ); 252 foreach ( $this->callbacks[ $priority ] as $the_ ) { 253 call_user_func_array( $the_['function'], $args ); 254 } 255 } while ( false !== next( $this->iterations[ $nesting_level ] ) ); 256 257 unset( $this->iterations[ $nesting_level ] ); 258 $this->nesting_level--; 259 } 260 261 /** 262 * Retrieve an external iterator 263 * 264 * Provided for backwards compatibility with plugins that iterate over the 265 * $wp_filter global 266 * 267 * @link http://php.net/manual/en/iteratoraggregate.getiterator.php 268 * @return Traversable An instance of an object implementing Iterator or 269 * Traversable 270 */ 271 public function getIterator() { 272 return new ArrayIterator( $this->callbacks ); 273 } 274 275 /** 276 * Some plugins may set up filters before WordPress has initialized. 277 * Normalize them to WP_Hook objects. 278 * 279 * @param array $filters 280 * @return WP_Hook[] 281 */ 282 public static function build_preinitialized_hooks( $filters ) { 283 /** @var WP_Hook[] $normalized */ 284 $normalized = array(); 285 foreach ( $filters as $tag => $callback_groups ) { 286 if ( is_object( $callback_groups ) && $callback_groups instanceof WP_Hook ) { 287 $normalized[ $tag ] = $callback_groups; 288 continue; 289 } 290 $hook = new WP_Hook(); 291 foreach ( $callback_groups as $priority => $callbacks ) { 292 foreach ( $callbacks as $cb ) { 293 $hook->add_filter( $cb['function'], $priority, $cb['accepted_args'], $tag ); 294 } 295 } 296 $normalized[ $tag ] = $hook; 297 } 298 return $normalized; 299 } 300 301 /** 302 * Whether an offset exists 303 * 304 * @link http://php.net/manual/en/arrayaccess.offsetexists.php 305 * 306 * @param mixed $offset An offset to check for. 307 * @return boolean true on success or false on failure. 308 */ 309 public function offsetExists( $offset ) { 310 return isset( $this->callbacks[ $offset ] ); 311 } 312 313 /** 314 * Offset to retrieve 315 * 316 * @link http://php.net/manual/en/arrayaccess.offsetget.php 317 * 318 * @param mixed $offset The offset to retrieve. 319 * @return mixed 320 */ 321 public function offsetGet( $offset ) { 322 return isset( $this->callbacks[ $offset ] ) ? $this->callbacks[ $offset ] : null; 323 } 324 325 /** 326 * Offset to set 327 * 328 * @link http://php.net/manual/en/arrayaccess.offsetset.php 329 * 330 * @param mixed $offset The offset to assign the value to. 331 * @param mixed $value The value to set. 332 */ 333 public function offsetSet( $offset, $value ) { 334 if ( is_null( $offset ) ) { 335 $this->callbacks[] = $value; 336 } else { 337 $this->callbacks[ $offset ] = $value; 338 } 339 } 340 341 /** 342 * Offset to unset 343 * 344 * @link http://php.net/manual/en/arrayaccess.offsetunset.php 345 * 346 * @param mixed $offset The offset to unset. 347 */ 348 public function offsetUnset( $offset ) { 349 unset( $this->callbacks[ $offset ] ); 350 } 351 } -
src/wp-includes/plugin.php
diff --git src/wp-includes/plugin.php src/wp-includes/plugin.php index e5e5288..50d110a 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 . WPINC . '/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 ( $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 ) ) 65 67 * @since 0.71 66 68 * 67 69 * @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.70 70 * 71 71 * @param string $tag The name of the filter to hook the $function_to_add callback to. 72 72 * @param callback $function_to_add The callback to be run when the filter is applied. … … if ( ! isset( $wp_current_filter ) ) 79 79 * @return boolean true 80 80 */ 81 81 function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) { 82 global $wp_filter, $merged_filters; 82 global $wp_filter; 83 if ( ! isset( $wp_filter[ $tag ] ) ) { 84 $wp_filter[ $tag ] = new WP_Hook(); 85 } 86 $wp_filter[ $tag ]->add_filter( $function_to_add, $priority, $accepted_args, $tag ); 83 87 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 ] );87 88 return true; 88 89 } 89 90 … … function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 104 105 * return value. 105 106 */ 106 107 function has_filter($tag, $function_to_check = false) { 107 // Don't reset the internal array pointer 108 $wp_filter = $GLOBALS['wp_filter']; 109 110 $has = ! empty( $wp_filter[ $tag ] ); 111 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 } 125 } 126 127 if ( false === $function_to_check || false == $has ) 128 return $has; 108 global $wp_filter; 129 109 130 if ( ! $idx = _wp_filter_build_unique_id($tag, $function_to_check, false) )110 if ( ! isset( $wp_filter[ $tag ] ) ) { 131 111 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 112 } 137 113 138 return false;114 return $wp_filter[ $tag ]->has_filter( $function_to_check, $tag ); 139 115 } 140 116 141 117 /** … … function has_filter($tag, $function_to_check = false) { 166 142 * @since 0.71 167 143 * 168 144 * @global array $wp_filter Stores all of the filters. 169 * @global array $merged_filters Merges the filter hooks using this function.170 145 * @global array $wp_current_filter Stores the list of current filters with the current one last. 171 146 * 172 147 * @param string $tag The name of the filter hook. … … function has_filter($tag, $function_to_check = false) { 175 150 * @return mixed The filtered value after all hooked functions are applied to it. 176 151 */ 177 152 function apply_filters( $tag, $value ) { 178 global $wp_filter, $ merged_filters, $wp_current_filter;153 global $wp_filter, $wp_current_filter; 179 154 180 155 $args = array(); 181 156 … … function apply_filters( $tag, $value ) { 195 170 if ( !isset($wp_filter['all']) ) 196 171 $wp_current_filter[] = $tag; 197 172 198 // Sort. 199 if ( !isset( $merged_filters[ $tag ] ) ) { 200 ksort($wp_filter[$tag]); 201 $merged_filters[ $tag ] = true; 202 } 203 204 reset( $wp_filter[ $tag ] ); 205 206 if ( empty($args) ) 173 if ( empty( $args ) ) { 207 174 $args = func_get_args(); 175 } 208 176 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 } 177 array_shift( $args ); // don't pass the tag name to WP_Hook 215 178 216 } while ( next($wp_filter[$tag]) !== false);179 $filtered = $wp_filter[ $tag ]->apply_filters( $value, $args ); 217 180 218 181 array_pop( $wp_current_filter ); 219 182 220 return $ value;183 return $filtered; 221 184 } 222 185 223 186 /** … … function apply_filters( $tag, $value ) { 229 192 * functions hooked to `$tag` are supplied using an array. 230 193 * 231 194 * @global array $wp_filter Stores all of the filters 232 * @global array $merged_filters Merges the filter hooks using this function.233 195 * @global array $wp_current_filter Stores the list of current filters with the current one last 234 196 * 235 197 * @param string $tag The name of the filter hook. … … function apply_filters( $tag, $value ) { 237 199 * @return mixed The filtered value after all hooked functions are applied to it. 238 200 */ 239 201 function apply_filters_ref_array($tag, $args) { 240 global $wp_filter, $ merged_filters, $wp_current_filter;202 global $wp_filter, $wp_current_filter; 241 203 242 204 // Do 'all' actions first 243 205 if ( isset($wp_filter['all']) ) { … … function apply_filters_ref_array($tag, $args) { 255 217 if ( !isset($wp_filter['all']) ) 256 218 $wp_current_filter[] = $tag; 257 219 258 // Sort 259 if ( !isset( $merged_filters[ $tag ] ) ) { 260 ksort($wp_filter[$tag]); 261 $merged_filters[ $tag ] = true; 262 } 263 264 reset( $wp_filter[ $tag ] ); 265 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 ); 220 $filtered = $wp_filter[ $tag ]->apply_filters( $args[0], $args ); 272 221 273 222 array_pop( $wp_current_filter ); 274 223 275 return $ args[0];224 return $filtered; 276 225 } 277 226 278 227 /** … … function apply_filters_ref_array($tag, $args) { 294 243 * @return boolean Whether the function existed before it was removed. 295 244 */ 296 245 function remove_filter( $tag, $function_to_remove, $priority = 10 ) { 297 $function_to_remove = _wp_filter_build_unique_id( $tag, $function_to_remove, $priority ); 298 299 $r = isset( $GLOBALS['wp_filter'][ $tag ][ $priority ][ $function_to_remove ] ); 246 global $wp_filter; 300 247 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 ] ); 305 } 306 if ( empty( $GLOBALS['wp_filter'][ $tag ] ) ) { 307 $GLOBALS['wp_filter'][ $tag ] = array(); 248 $r = false; 249 if ( isset( $wp_filter[ $tag ] ) ) { 250 $r = $wp_filter[ $tag ]->remove_filter( $function_to_remove, $priority, $tag ); 251 if ( ! $wp_filter[ $tag ]->callbacks ) { 252 unset( $wp_filter[ $tag ] ); 308 253 } 309 unset( $GLOBALS['merged_filters'][ $tag ] );310 254 } 311 255 312 256 return $r; … … function remove_filter( $tag, $function_to_remove, $priority = 10 ) { 322 266 * @return bool True when finished. 323 267 */ 324 268 function remove_all_filters( $tag, $priority = false ) { 325 global $wp_filter , $merged_filters;269 global $wp_filter; 326 270 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(); 271 if( isset( $wp_filter[ $tag ] ) ) { 272 $wp_filter[ $tag ]->remove_all_filters( $priority ); 273 if ( ! $wp_filter[ $tag ]->has_filters() ) { 274 unset( $wp_filter[ $tag ] ); 332 275 } 333 276 } 334 277 335 if ( isset( $merged_filters[ $tag ] ) ) {336 unset( $merged_filters[ $tag ] );337 }338 339 278 return true; 340 279 } 341 280 … … function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) 451 390 * @return null Will return null if $tag does not exist in $wp_filter array. 452 391 */ 453 392 function do_action($tag, $arg = '') { 454 global $wp_filter, $wp_actions, $ merged_filters, $wp_current_filter;393 global $wp_filter, $wp_actions, $wp_current_filter; 455 394 456 395 if ( ! isset($wp_actions[$tag]) ) 457 396 $wp_actions[$tag] = 1; … … function do_action($tag, $arg = '') { 482 421 for ( $a = 2, $num = func_num_args(); $a < $num; $a++ ) 483 422 $args[] = func_get_arg($a); 484 423 485 // Sort 486 if ( !isset( $merged_filters[ $tag ] ) ) { 487 ksort($wp_filter[$tag]); 488 $merged_filters[ $tag ] = true; 489 } 490 491 reset( $wp_filter[ $tag ] ); 492 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 ); 424 $wp_filter[ $tag ]->do_action( $args ); 499 425 500 426 array_pop($wp_current_filter); 501 427 } … … function did_action($tag) { 534 460 * @return null Will return null if `$tag` does not exist in `$wp_filter` array. 535 461 */ 536 462 function do_action_ref_array($tag, $args) { 537 global $wp_filter, $wp_actions, $ merged_filters, $wp_current_filter;463 global $wp_filter, $wp_actions, $wp_current_filter; 538 464 539 465 if ( ! isset($wp_actions[$tag]) ) 540 466 $wp_actions[$tag] = 1; … … function do_action_ref_array($tag, $args) { 557 483 if ( !isset($wp_filter['all']) ) 558 484 $wp_current_filter[] = $tag; 559 485 560 // Sort 561 if ( !isset( $merged_filters[ $tag ] ) ) { 562 ksort($wp_filter[$tag]); 563 $merged_filters[ $tag ] = true; 564 } 565 566 reset( $wp_filter[ $tag ] ); 567 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 ); 486 $wp_filter[ $tag ]->do_action( $args ); 574 487 575 488 array_pop($wp_current_filter); 576 489 } … … function register_uninstall_hook( $file, $callback ) { 825 738 */ 826 739 function _wp_call_all_hook($args) { 827 740 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 ); 741 $wp_filter['all']->do_all_hook( $args ); 836 742 } 837 743 838 744 /** -
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 81f9686..ee3053b 100644
class WP_UnitTestCase extends PHPUnit_Framework_TestCase { 140 140 * @return void 141 141 */ 142 142 protected function _backup_hooks() { 143 $globals = array( ' merged_filters', 'wp_actions', 'wp_current_filter', 'wp_filter' );143 $globals = array( 'wp_actions', 'wp_current_filter' ); 144 144 foreach ( $globals as $key ) { 145 145 self::$hooks_saved[ $key ] = $GLOBALS[ $key ]; 146 146 } 147 self::$hooks_saved['wp_filter'] = array(); 148 foreach ( $GLOBALS['wp_filter'] as $hook_name => $hook_object ) { 149 self::$hooks_saved['wp_filter'][ $hook_name ] = clone( $hook_object ); 150 } 147 151 } 148 152 149 153 /** … … class WP_UnitTestCase extends PHPUnit_Framework_TestCase { 157 161 * @return void 158 162 */ 159 163 protected function _restore_hooks() { 160 $globals = array( ' merged_filters', 'wp_actions', 'wp_current_filter', 'wp_filter' );164 $globals = array( 'wp_actions', 'wp_current_filter' ); 161 165 foreach ( $globals as $key ) { 162 166 if ( isset( self::$hooks_saved[ $key ] ) ) { 163 167 $GLOBALS[ $key ] = self::$hooks_saved[ $key ]; 164 168 } 165 169 } 170 if ( isset( self::$hooks_saved['wp_filter'] ) ) { 171 $GLOBALS['wp_filter'] = array(); 172 foreach ( self::$hooks_saved['wp_filter'] as $hook_name => $hook_object ) { 173 $GLOBALS['wp_filter'][ $hook_name ] = clone( $hook_object ); 174 } 175 } 166 176 } 167 177 168 178 function flush_cache() { -
tests/phpunit/tests/actions.php
diff --git tests/phpunit/tests/actions.php tests/phpunit/tests/actions.php index 583c8ce..cdbe3bd 100644
class Tests_Actions extends WP_UnitTestCase { 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(); … … class Tests_Actions extends WP_UnitTestCase { 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( '__return_null' => array( 'function' => '__return_null', 'accepted_args' => 1 ) ); 408 $this->assertEquals( 11, has_action( $tag, '__return_null' ) ); 409 } 410 411 /** 261 412 * Make sure current_action() behaves as current_filter() 262 413 * 263 414 * @ticket 14994 -
tests/phpunit/tests/filters.php
diff --git tests/phpunit/tests/filters.php tests/phpunit/tests/filters.php index d9eda55..d768a68 100644
class Tests_Filters extends WP_UnitTestCase { 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 }