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