| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Plugin API: WP_Hook class 4 * 5 * @package WordPress 6 * @subpackage Plugin 7 * @since 4.7.0 8 */ 9 10 /** 11 * Core class used to implement action and filter hook functionality. 12 * 13 * @since 4.7.0 14 * 15 * @see Iterator 16 * @see ArrayAccess 17 * 18 * @phpstan-type Hook_Callback array{ 19 * function: callable, 20 * accepted_args: int, 21 * } 22 * 23 * @phpstan-implements Iterator<int, array<non-decimal-int-string, Hook_Callback>> 24 * @phpstan-implements ArrayAccess<int, array<non-decimal-int-string, Hook_Callback>> 25 */ 26 #[AllowDynamicProperties] 27 final class WP_Hook implements Iterator, ArrayAccess { 28 29 /** 30 * Hook callbacks keyed by priority. 31 * 32 * @since 4.7.0 33 * @var array 34 * @phpstan-var array<int, array<non-decimal-int-string, Hook_Callback>> 35 */ 36 public $callbacks = array(); 37 38 /** 39 * Priorities list. 40 * 41 * @since 6.4.0 42 * @var list<int> 43 */ 44 protected $priorities = array(); 45 46 /** 47 * The priority keys of actively running iterations of a hook. 48 * 49 * @since 4.7.0 50 * @var array<int, list<int>> 51 */ 52 private $iterations = array(); 53 54 /** 55 * The current priority of actively running iterations of a hook. 56 * 57 * @since 4.7.0 58 * @var array<int, int> 59 */ 60 private $current_priority = array(); 61 62 /** 63 * Number of levels this hook can be recursively called. 64 * 65 * @since 4.7.0 66 * @var int 67 */ 68 private $nesting_level = 0; 69 70 /** 71 * Flag for if we're currently doing an action, rather than a filter. 72 * 73 * @since 4.7.0 74 * @var bool 75 */ 76 private $doing_action = false; 77 78 /** 79 * Adds a callback function to a filter hook. 80 * 81 * @since 4.7.0 82 * 83 * @param string $hook_name The name of the filter to add the callback to. 84 * @param callable $callback The callback to be run when the filter is applied. 85 * @param int $priority The order in which the functions associated with a particular filter 86 * are executed. Lower numbers correspond with earlier execution, 87 * and functions with the same priority are executed in the order 88 * in which they were added to the filter. 89 * @param int $accepted_args The number of arguments the function accepts. 90 */ 91 public function add_filter( $hook_name, $callback, $priority, $accepted_args ) { 92 if ( null === $priority ) { 93 $priority = 0; 94 } 95 96 $idx = _wp_filter_build_unique_id( $hook_name, $callback, $priority ); 97 if ( null === $idx ) { 98 return; 99 } 100 101 $priority_existed = isset( $this->callbacks[ $priority ] ); 102 103 $this->callbacks[ $priority ][ $idx ] = array( 104 'function' => $callback, 105 'accepted_args' => (int) $accepted_args, 106 ); 107 108 // If we're adding a new priority to the list, put them back in sorted order. 109 if ( ! $priority_existed && count( $this->callbacks ) > 1 ) { 110 ksort( $this->callbacks, SORT_NUMERIC ); 111 } 112 113 $this->priorities = array_keys( $this->callbacks ); 114 115 if ( $this->nesting_level > 0 ) { 116 $this->resort_active_iterations( $priority, $priority_existed ); 117 } 118 } 119 120 /** 121 * Handles resetting callback priority keys mid-iteration. 122 * 123 * @since 4.7.0 124 * 125 * @param false|int $new_priority Optional. The priority of the new filter being added. Default false, 126 * for no priority being added. 127 * @param bool $priority_existed Optional. Flag for whether the priority already existed before the new 128 * filter was added. Default false. 129 */ 130 private function resort_active_iterations( $new_priority = false, $priority_existed = false ) { 131 $new_priorities = $this->priorities; 132 133 // If there are no remaining hooks, clear out all running iterations. 134 if ( ! $new_priorities ) { 135 foreach ( $this->iterations as $index => $iteration ) { 136 $this->iterations[ $index ] = $new_priorities; 137 } 138 139 return; 140 } 141 142 $min = min( $new_priorities ); 143 144 foreach ( $this->iterations as $index => &$iteration ) { 145 $current = current( $iteration ); 146 147 // If we're already at the end of this iteration, just leave the array pointer where it is. 148 if ( false === $current ) { 149 continue; 150 } 151 152 $iteration = $new_priorities; 153 154 if ( $current < $min ) { 155 array_unshift( $iteration, $current ); 156 continue; 157 } 158 159 while ( current( $iteration ) < $current ) { 160 if ( false === next( $iteration ) ) { 161 break; 162 } 163 } 164 165 // If we have a new priority that didn't exist, but ::apply_filters() or ::do_action() thinks it's the current priority... 166 if ( $new_priority === $this->current_priority[ $index ] && ! $priority_existed ) { 167 /* 168 * ...and the new priority is the same as what $this->iterations thinks is the previous 169 * priority, we need to move back to it. 170 */ 171 172 if ( false === current( $iteration ) ) { 173 // If we've already moved off the end of the array, go back to the last element. 174 $prev = end( $iteration ); 175 } else { 176 // Otherwise, just go back to the previous element. 177 $prev = prev( $iteration ); 178 } 179 180 if ( false === $prev ) { 181 // Start of the array. Reset, and go about our day. 182 reset( $iteration ); 183 } elseif ( $new_priority !== $prev ) { 184 // Previous wasn't the same. Move forward again. 185 next( $iteration ); 186 } 187 } 188 } 189 190 unset( $iteration ); 191 } 192 193 /** 194 * Removes a callback function from a filter hook. 195 * 196 * @since 4.7.0 197 * 198 * @param string $hook_name The filter hook to which the function to be removed is hooked. 199 * @param callable|string|array $callback The callback to be removed from running when the filter is applied. 200 * This method can be called unconditionally to speculatively remove 201 * a callback that may or may not exist. 202 * @param int $priority The exact priority used when adding the original filter callback. 203 * @return bool Whether the callback existed before it was removed. 204 * @phpstan-param Maybe_Callable $callback 205 */ 206 public function remove_filter( $hook_name, $callback, $priority ) { 207 if ( null === $priority ) { 208 $priority = 0; 209 } 210 211 $function_key = _wp_filter_build_unique_id( $hook_name, $callback, $priority ); 212 213 $exists = isset( $function_key, $this->callbacks[ $priority ][ $function_key ] ); 214 215 if ( $exists ) { 216 unset( $this->callbacks[ $priority ][ $function_key ] ); 217 218 if ( ! $this->callbacks[ $priority ] ) { 219 unset( $this->callbacks[ $priority ] ); 220 221 $this->priorities = array_keys( $this->callbacks ); 222 223 if ( $this->nesting_level > 0 ) { 224 $this->resort_active_iterations(); 225 } 226 } 227 } 228 229 return $exists; 230 } 231 232 /** 233 * Checks if a specific callback has been registered for this hook. 234 * 235 * When using the `$callback` argument, this function may return a non-boolean value 236 * that evaluates to false (e.g. 0), so use the `===` operator for testing the return value. 237 * 238 * @since 4.7.0 239 * @since 6.9.0 Added the `$priority` parameter. 240 * 241 * @param string $hook_name Optional. The name of the filter hook. Default empty. 242 * @param callable|string|array|false $callback Optional. The callback to check for. 243 * This method can be called unconditionally to speculatively check 244 * a callback that may or may not exist. Default false. 245 * @param int|false $priority Optional. The specific priority at which to check for the callback. 246 * Default false. 247 * @return bool|int If `$callback` is omitted, returns boolean for whether the hook has 248 * anything registered. When checking a specific function, the priority 249 * of that hook is returned, or false if the function is not attached. 250 * If `$callback` and `$priority` are both provided, a boolean is returned 251 * for whether the specific function is registered at that priority. 252 * @phpstan-param Maybe_Callable|false $callback 253 */ 254 public function has_filter( $hook_name = '', $callback = false, $priority = false ) { 255 if ( false === $callback ) { 256 return $this->has_filters(); 257 } 258 259 $function_key = _wp_filter_build_unique_id( $hook_name, $callback, is_int( $priority ) ? $priority : 10 ); 260 261 if ( ! $function_key ) { 262 return false; 263 } 264 265 if ( is_int( $priority ) ) { 266 return isset( $this->callbacks[ $priority ][ $function_key ] ); 267 } 268 269 foreach ( $this->callbacks as $callback_priority => $callbacks ) { 270 if ( isset( $callbacks[ $function_key ] ) ) { 271 return $callback_priority; 272 } 273 } 274 275 return false; 276 } 277 278 /** 279 * Checks if any callbacks have been registered for this hook. 280 * 281 * @since 4.7.0 282 * 283 * @return bool True if callbacks have been registered for the current hook, otherwise false. 284 */ 285 public function has_filters() { 286 foreach ( $this->callbacks as $callbacks ) { 287 if ( $callbacks ) { 288 return true; 289 } 290 } 291 292 return false; 293 } 294 295 /** 296 * Removes all callbacks from the current filter. 297 * 298 * @since 4.7.0 299 * 300 * @param int|false $priority Optional. The priority number to remove. Default false. 301 */ 302 public function remove_all_filters( $priority = false ) { 303 if ( ! $this->callbacks ) { 304 return; 305 } 306 307 if ( false === $priority ) { 308 $this->callbacks = array(); 309 $this->priorities = array(); 310 } elseif ( isset( $this->callbacks[ $priority ] ) ) { 311 unset( $this->callbacks[ $priority ] ); 312 $this->priorities = array_keys( $this->callbacks ); 313 } 314 315 if ( $this->nesting_level > 0 ) { 316 $this->resort_active_iterations(); 317 } 318 } 319 320 /** 321 * Calls the callback functions that have been added to a filter hook. 322 * 323 * @since 4.7.0 324 * 325 * @param mixed $value The value to filter. 326 * @param list<mixed> $args Additional parameters to pass to the callback functions. 327 * When filtering, this array is expected to include $value 328 * at index 0. When called for an action by WP_Hook::do_action(), 329 * it contains only the action arguments. 330 * @return mixed The filtered value after all hooked functions are applied to it. 331 */ 332 public function apply_filters( $value, $args ) { 333 if ( ! $this->callbacks ) { 334 return $value; 335 } 336 337 $nesting_level = $this->nesting_level++; 338 339 $this->iterations[ $nesting_level ] = $this->priorities; 340 341 $num_args = count( $args ); 342 343 do { 344 $priority = current( $this->iterations[ $nesting_level ] ); 345 346 if ( false === $priority ) { 347 // This is not expected to occur since the hook is known to have callbacks at one or more priorities. 348 break; 349 } 350 351 $this->current_priority[ $nesting_level ] = $priority; 352 353 foreach ( $this->callbacks[ $priority ] as $the_ ) { 354 if ( ! $this->doing_action ) { 355 $args[0] = $value; 356 } 357 358 // Avoid the array_slice() if possible. 359 if ( 0 === $the_['accepted_args'] ) { 360 $value = call_user_func( $the_['function'] ); 361 } elseif ( $the_['accepted_args'] >= $num_args ) { 362 $value = call_user_func_array( $the_['function'], $args ); 363 } else { 364 $value = call_user_func_array( $the_['function'], array_slice( $args, 0, $the_['accepted_args'] ) ); 365 } 366 } 367 } while ( false !== next( $this->iterations[ $nesting_level ] ) ); 368 369 unset( $this->iterations[ $nesting_level ] ); 370 unset( $this->current_priority[ $nesting_level ] ); 371 372 --$this->nesting_level; 373 374 return $value; 375 } 376 377 /** 378 * Calls the callback functions that have been added to an action hook. 379 * 380 * @since 4.7.0 381 * 382 * @param list<mixed> $args Parameters to pass to the callback functions. 383 */ 384 public function do_action( $args ) { 385 $this->doing_action = true; 386 $this->apply_filters( '', $args ); 387 388 // If there are recursive calls to the current action, we haven't finished it until we get to the last one. 389 if ( ! $this->nesting_level ) { 390 $this->doing_action = false; 391 } 392 } 393 394 /** 395 * Processes the functions hooked into the 'all' hook. 396 * 397 * @since 4.7.0 398 * 399 * @param list<mixed> $args Arguments to pass to the hook callbacks. Passed by reference. 400 */ 401 public function do_all_hook( &$args ) { 402 $nesting_level = $this->nesting_level++; 403 $this->iterations[ $nesting_level ] = $this->priorities; 404 405 do { 406 $priority = current( $this->iterations[ $nesting_level ] ); 407 408 foreach ( $this->callbacks[ $priority ] as $the_ ) { 409 call_user_func_array( $the_['function'], $args ); 410 } 411 } while ( false !== next( $this->iterations[ $nesting_level ] ) ); 412 413 unset( $this->iterations[ $nesting_level ] ); 414 --$this->nesting_level; 415 } 416 417 /** 418 * Return the current priority level of the currently running iteration of the hook. 419 * 420 * @since 4.7.0 421 * 422 * @return int|false If the hook is running, return the current priority level. 423 * If it isn't running, return false. 424 */ 425 public function current_priority() { 426 if ( false === current( $this->iterations ) ) { 427 return false; 428 } 429 430 return current( current( $this->iterations ) ); 431 } 432 433 /** 434 * Normalizes filters set up before WordPress has initialized to WP_Hook objects. 435 * 436 * The `$filters` parameter should be an array keyed by hook name, with values 437 * containing either: 438 * 439 * - A `WP_Hook` instance 440 * - An array of callbacks keyed by their priorities 441 * 442 * Examples: 443 * 444 * $filters = array( 445 * 'wp_fatal_error_handler_enabled' => array( 446 * 10 => array( 447 * array( 448 * 'accepted_args' => 0, 449 * 'function' => function() { 450 * return false; 451 * }, 452 * ), 453 * ), 454 * ), 455 * ); 456 * 457 * @since 4.7.0 458 * 459 * @param array $filters Filters to normalize. See documentation above for details. 460 * @phpstan-param array<string, WP_Hook|array<int, array<Hook_Callback>>> $filters 461 * @return array<string, WP_Hook> Array of normalized filters keyed by hook name. 462 */ 463 public static function build_preinitialized_hooks( $filters ) { 464 /** @var array<string, WP_Hook> $normalized */ 465 $normalized = array(); 466 467 foreach ( $filters as $hook_name => $callback_groups ) { 468 if ( $callback_groups instanceof WP_Hook ) { 469 $normalized[ $hook_name ] = $callback_groups; 470 continue; 471 } 472 473 $hook = new WP_Hook(); 474 475 // Loop through callback groups. 476 foreach ( $callback_groups as $priority => $callbacks ) { 477 478 // Loop through callbacks. 479 foreach ( $callbacks as $cb ) { 480 $hook->add_filter( $hook_name, $cb['function'], $priority, $cb['accepted_args'] ); 481 } 482 } 483 484 $normalized[ $hook_name ] = $hook; 485 } 486 487 return $normalized; 488 } 489 490 /** 491 * Determines whether an offset value exists. 492 * 493 * @since 4.7.0 494 * 495 * @link https://www.php.net/manual/en/arrayaccess.offsetexists.php 496 * 497 * @param int $offset An offset to check for. 498 * @return bool True if the offset exists, false otherwise. 499 */ 500 #[ReturnTypeWillChange] 501 public function offsetExists( $offset ) { 502 return isset( $this->callbacks[ $offset ] ); 503 } 504 505 /** 506 * Retrieves a value at a specified offset. 507 * 508 * @since 4.7.0 509 * 510 * @link https://www.php.net/manual/en/arrayaccess.offsetget.php 511 * 512 * @param int $offset The offset to retrieve. 513 * @return array|null If set, the value at the specified offset, null otherwise. 514 * @phpstan-return array<non-decimal-int-string, Hook_Callback>|null 515 */ 516 #[ReturnTypeWillChange] 517 public function offsetGet( $offset ) { 518 return $this->callbacks[ $offset ] ?? null; 519 } 520 521 /** 522 * Sets a value at a specified offset. 523 * 524 * @since 4.7.0 525 * 526 * @link https://www.php.net/manual/en/arrayaccess.offsetset.php 527 * 528 * @param int|null $offset The offset to assign the value to. 529 * @param array $value The value to set. 530 * @phpstan-param array<non-decimal-int-string, Hook_Callback> $value 531 */ 532 #[ReturnTypeWillChange] 533 public function offsetSet( $offset, $value ) { 534 if ( is_null( $offset ) ) { 535 $this->callbacks[] = $value; 536 } else { 537 $this->callbacks[ $offset ] = $value; 538 } 539 540 $this->priorities = array_keys( $this->callbacks ); 541 } 542 543 /** 544 * Unsets a specified offset. 545 * 546 * @since 4.7.0 547 * 548 * @link https://www.php.net/manual/en/arrayaccess.offsetunset.php 549 * 550 * @param int $offset The offset to unset. 551 */ 552 #[ReturnTypeWillChange] 553 public function offsetUnset( $offset ) { 554 unset( $this->callbacks[ $offset ] ); 555 $this->priorities = array_keys( $this->callbacks ); 556 } 557 558 /** 559 * Returns the current element. 560 * 561 * @since 4.7.0 562 * 563 * @link https://www.php.net/manual/en/iterator.current.php 564 * 565 * @return array|false Array of callbacks at current priority, false if there are no more elements. 566 * @phpstan-return array<non-decimal-int-string, Hook_Callback>|false 567 */ 568 #[ReturnTypeWillChange] 569 public function current() { 570 return current( $this->callbacks ); 571 } 572 573 /** 574 * Moves forward to the next element. 575 * 576 * @since 4.7.0 577 * 578 * @link https://www.php.net/manual/en/iterator.next.php 579 * 580 * @return array|false Array of callbacks at next priority, false if there are no more elements. 581 * @phpstan-return array<non-decimal-int-string, Hook_Callback>|false 582 */ 583 #[ReturnTypeWillChange] 584 public function next() { 585 return next( $this->callbacks ); 586 } 587 588 /** 589 * Returns the key of the current element. 590 * 591 * @since 4.7.0 592 * 593 * @link https://www.php.net/manual/en/iterator.key.php 594 * 595 * @return int|null Returns current priority on success, or NULL on failure 596 */ 597 #[ReturnTypeWillChange] 598 public function key() { 599 return key( $this->callbacks ); 600 } 601 602 /** 603 * Checks if current position is valid. 604 * 605 * @since 4.7.0 606 * 607 * @link https://www.php.net/manual/en/iterator.valid.php 608 * 609 * @return bool Whether the current position is valid. 610 */ 611 #[ReturnTypeWillChange] 612 public function valid() { 613 return key( $this->callbacks ) !== null; 614 } 615 616 /** 617 * Rewinds the Iterator to the first element. 618 * 619 * @since 4.7.0 620 * 621 * @link https://www.php.net/manual/en/iterator.rewind.php 622 */ 623 #[ReturnTypeWillChange] 624 public function rewind() { 625 reset( $this->callbacks ); 626 } 627 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Wed Sep 16 08:20:31 2026 | Cross-referenced by PHPXref |