wp/wp-includes/class-wp-hook.php
changeset 18 be944660c56a
parent 16 a86126ab1dd4
child 19 3d72ae0968f4
equal deleted inserted replaced
17:34716fd837a4 18:be944660c56a
    48 	 * @var int
    48 	 * @var int
    49 	 */
    49 	 */
    50 	private $nesting_level = 0;
    50 	private $nesting_level = 0;
    51 
    51 
    52 	/**
    52 	/**
    53 	 * Flag for if we're current doing an action, rather than a filter.
    53 	 * Flag for if we're currently doing an action, rather than a filter.
    54 	 *
    54 	 *
    55 	 * @since 4.7.0
    55 	 * @since 4.7.0
    56 	 * @var bool
    56 	 * @var bool
    57 	 */
    57 	 */
    58 	private $doing_action = false;
    58 	private $doing_action = false;
    59 
    59 
    60 	/**
    60 	/**
    61 	 * Hooks a function or method to a specific filter action.
    61 	 * Adds a callback function to a filter hook.
    62 	 *
    62 	 *
    63 	 * @since 4.7.0
    63 	 * @since 4.7.0
    64 	 *
    64 	 *
    65 	 * @param string   $tag             The name of the filter to hook the $function_to_add callback to.
    65 	 * @param string   $hook_name     The name of the filter to add the callback to.
    66 	 * @param callable $function_to_add The callback to be run when the filter is applied.
    66 	 * @param callable $callback      The callback to be run when the filter is applied.
    67 	 * @param int      $priority        The order in which the functions associated with a particular action
    67 	 * @param int      $priority      The order in which the functions associated with a particular filter
    68 	 *                                  are executed. Lower numbers correspond with earlier execution,
    68 	 *                                are executed. Lower numbers correspond with earlier execution,
    69 	 *                                  and functions with the same priority are executed in the order
    69 	 *                                and functions with the same priority are executed in the order
    70 	 *                                  in which they were added to the action.
    70 	 *                                in which they were added to the filter.
    71 	 * @param int      $accepted_args   The number of arguments the function accepts.
    71 	 * @param int      $accepted_args The number of arguments the function accepts.
    72 	 */
    72 	 */
    73 	public function add_filter( $tag, $function_to_add, $priority, $accepted_args ) {
    73 	public function add_filter( $hook_name, $callback, $priority, $accepted_args ) {
    74 		$idx = _wp_filter_build_unique_id( $tag, $function_to_add, $priority );
    74 		$idx = _wp_filter_build_unique_id( $hook_name, $callback, $priority );
    75 
    75 
    76 		$priority_existed = isset( $this->callbacks[ $priority ] );
    76 		$priority_existed = isset( $this->callbacks[ $priority ] );
    77 
    77 
    78 		$this->callbacks[ $priority ][ $idx ] = array(
    78 		$this->callbacks[ $priority ][ $idx ] = array(
    79 			'function'      => $function_to_add,
    79 			'function'      => $callback,
    80 			'accepted_args' => $accepted_args,
    80 			'accepted_args' => $accepted_args,
    81 		);
    81 		);
    82 
    82 
    83 		// If we're adding a new priority to the list, put them back in sorted order.
    83 		// If we're adding a new priority to the list, put them back in sorted order.
    84 		if ( ! $priority_existed && count( $this->callbacks ) > 1 ) {
    84 		if ( ! $priority_existed && count( $this->callbacks ) > 1 ) {
    93 	/**
    93 	/**
    94 	 * Handles resetting callback priority keys mid-iteration.
    94 	 * Handles resetting callback priority keys mid-iteration.
    95 	 *
    95 	 *
    96 	 * @since 4.7.0
    96 	 * @since 4.7.0
    97 	 *
    97 	 *
    98 	 * @param bool|int $new_priority     Optional. The priority of the new filter being added. Default false,
    98 	 * @param false|int $new_priority     Optional. The priority of the new filter being added. Default false,
    99 	 *                                   for no priority being added.
    99 	 *                                    for no priority being added.
   100 	 * @param bool     $priority_existed Optional. Flag for whether the priority already existed before the new
   100 	 * @param bool      $priority_existed Optional. Flag for whether the priority already existed before the new
   101 	 *                                   filter was added. Default false.
   101 	 *                                    filter was added. Default false.
   102 	 */
   102 	 */
   103 	private function resort_active_iterations( $new_priority = false, $priority_existed = false ) {
   103 	private function resort_active_iterations( $new_priority = false, $priority_existed = false ) {
   104 		$new_priorities = array_keys( $this->callbacks );
   104 		$new_priorities = array_keys( $this->callbacks );
   105 
   105 
   106 		// If there are no remaining hooks, clear out all running iterations.
   106 		// If there are no remaining hooks, clear out all running iterations.
   107 		if ( ! $new_priorities ) {
   107 		if ( ! $new_priorities ) {
   108 			foreach ( $this->iterations as $index => $iteration ) {
   108 			foreach ( $this->iterations as $index => $iteration ) {
   109 				$this->iterations[ $index ] = $new_priorities;
   109 				$this->iterations[ $index ] = $new_priorities;
   110 			}
   110 			}
       
   111 
   111 			return;
   112 			return;
   112 		}
   113 		}
   113 
   114 
   114 		$min = min( $new_priorities );
   115 		$min = min( $new_priorities );
       
   116 
   115 		foreach ( $this->iterations as $index => &$iteration ) {
   117 		foreach ( $this->iterations as $index => &$iteration ) {
   116 			$current = current( $iteration );
   118 			$current = current( $iteration );
       
   119 
   117 			// If we're already at the end of this iteration, just leave the array pointer where it is.
   120 			// If we're already at the end of this iteration, just leave the array pointer where it is.
   118 			if ( false === $current ) {
   121 			if ( false === $current ) {
   119 				continue;
   122 				continue;
   120 			}
   123 			}
   121 
   124 
   144 					$prev = end( $iteration );
   147 					$prev = end( $iteration );
   145 				} else {
   148 				} else {
   146 					// Otherwise, just go back to the previous element.
   149 					// Otherwise, just go back to the previous element.
   147 					$prev = prev( $iteration );
   150 					$prev = prev( $iteration );
   148 				}
   151 				}
       
   152 
   149 				if ( false === $prev ) {
   153 				if ( false === $prev ) {
   150 					// Start of the array. Reset, and go about our day.
   154 					// Start of the array. Reset, and go about our day.
   151 					reset( $iteration );
   155 					reset( $iteration );
   152 				} elseif ( $new_priority !== $prev ) {
   156 				} elseif ( $new_priority !== $prev ) {
   153 					// Previous wasn't the same. Move forward again.
   157 					// Previous wasn't the same. Move forward again.
   154 					next( $iteration );
   158 					next( $iteration );
   155 				}
   159 				}
   156 			}
   160 			}
   157 		}
   161 		}
       
   162 
   158 		unset( $iteration );
   163 		unset( $iteration );
   159 	}
   164 	}
   160 
   165 
   161 	/**
   166 	/**
   162 	 * Unhooks a function or method from a specific filter action.
   167 	 * Removes a callback function from a filter hook.
   163 	 *
   168 	 *
   164 	 * @since 4.7.0
   169 	 * @since 4.7.0
   165 	 *
   170 	 *
   166 	 * @param string   $tag                The filter hook to which the function to be removed is hooked.
   171 	 * @param string   $hook_name The filter hook to which the function to be removed is hooked.
   167 	 * @param callable $function_to_remove The callback to be removed from running when the filter is applied.
   172 	 * @param callable $callback  The callback to be removed from running when the filter is applied.
   168 	 * @param int      $priority           The exact priority used when adding the original filter callback.
   173 	 * @param int      $priority  The exact priority used when adding the original filter callback.
   169 	 * @return bool Whether the callback existed before it was removed.
   174 	 * @return bool Whether the callback existed before it was removed.
   170 	 */
   175 	 */
   171 	public function remove_filter( $tag, $function_to_remove, $priority ) {
   176 	public function remove_filter( $hook_name, $callback, $priority ) {
   172 		$function_key = _wp_filter_build_unique_id( $tag, $function_to_remove, $priority );
   177 		$function_key = _wp_filter_build_unique_id( $hook_name, $callback, $priority );
   173 
   178 
   174 		$exists = isset( $this->callbacks[ $priority ][ $function_key ] );
   179 		$exists = isset( $this->callbacks[ $priority ][ $function_key ] );
       
   180 
   175 		if ( $exists ) {
   181 		if ( $exists ) {
   176 			unset( $this->callbacks[ $priority ][ $function_key ] );
   182 			unset( $this->callbacks[ $priority ][ $function_key ] );
       
   183 
   177 			if ( ! $this->callbacks[ $priority ] ) {
   184 			if ( ! $this->callbacks[ $priority ] ) {
   178 				unset( $this->callbacks[ $priority ] );
   185 				unset( $this->callbacks[ $priority ] );
       
   186 
   179 				if ( $this->nesting_level > 0 ) {
   187 				if ( $this->nesting_level > 0 ) {
   180 					$this->resort_active_iterations();
   188 					$this->resort_active_iterations();
   181 				}
   189 				}
   182 			}
   190 			}
   183 		}
   191 		}
       
   192 
   184 		return $exists;
   193 		return $exists;
   185 	}
   194 	}
   186 
   195 
   187 	/**
   196 	/**
   188 	 * Checks if a specific action has been registered for this hook.
   197 	 * Checks if a specific callback has been registered for this hook.
   189 	 *
   198 	 *
   190 	 * @since 4.7.0
   199 	 * When using the `$callback` argument, this function may return a non-boolean value
   191 	 *
   200 	 * that evaluates to false (e.g. 0), so use the `===` operator for testing the return value.
   192 	 * @param string        $tag               Optional. The name of the filter hook. Default empty.
   201 	 *
   193 	 * @param callable|bool $function_to_check Optional. The callback to check for. Default false.
   202 	 * @since 4.7.0
   194 	 * @return bool|int The priority of that hook is returned, or false if the function is not attached.
   203 	 *
   195 	 */
   204 	 * @param string         $hook_name Optional. The name of the filter hook. Default empty.
   196 	public function has_filter( $tag = '', $function_to_check = false ) {
   205 	 * @param callable|false $callback  Optional. The callback to check for. Default false.
   197 		if ( false === $function_to_check ) {
   206 	 * @return bool|int If `$callback` is omitted, returns boolean for whether the hook has
       
   207 	 *                  anything registered. When checking a specific function, the priority
       
   208 	 *                  of that hook is returned, or false if the function is not attached.
       
   209 	 */
       
   210 	public function has_filter( $hook_name = '', $callback = false ) {
       
   211 		if ( false === $callback ) {
   198 			return $this->has_filters();
   212 			return $this->has_filters();
   199 		}
   213 		}
   200 
   214 
   201 		$function_key = _wp_filter_build_unique_id( $tag, $function_to_check, false );
   215 		$function_key = _wp_filter_build_unique_id( $hook_name, $callback, false );
       
   216 
   202 		if ( ! $function_key ) {
   217 		if ( ! $function_key ) {
   203 			return false;
   218 			return false;
   204 		}
   219 		}
   205 
   220 
   206 		foreach ( $this->callbacks as $priority => $callbacks ) {
   221 		foreach ( $this->callbacks as $priority => $callbacks ) {
   223 		foreach ( $this->callbacks as $callbacks ) {
   238 		foreach ( $this->callbacks as $callbacks ) {
   224 			if ( $callbacks ) {
   239 			if ( $callbacks ) {
   225 				return true;
   240 				return true;
   226 			}
   241 			}
   227 		}
   242 		}
       
   243 
   228 		return false;
   244 		return false;
   229 	}
   245 	}
   230 
   246 
   231 	/**
   247 	/**
   232 	 * Removes all callbacks from the current filter.
   248 	 * Removes all callbacks from the current filter.
   233 	 *
   249 	 *
   234 	 * @since 4.7.0
   250 	 * @since 4.7.0
   235 	 *
   251 	 *
   236 	 * @param int|bool $priority Optional. The priority number to remove. Default false.
   252 	 * @param int|false $priority Optional. The priority number to remove. Default false.
   237 	 */
   253 	 */
   238 	public function remove_all_filters( $priority = false ) {
   254 	public function remove_all_filters( $priority = false ) {
   239 		if ( ! $this->callbacks ) {
   255 		if ( ! $this->callbacks ) {
   240 			return;
   256 			return;
   241 		}
   257 		}
   327 		$nesting_level                      = $this->nesting_level++;
   343 		$nesting_level                      = $this->nesting_level++;
   328 		$this->iterations[ $nesting_level ] = array_keys( $this->callbacks );
   344 		$this->iterations[ $nesting_level ] = array_keys( $this->callbacks );
   329 
   345 
   330 		do {
   346 		do {
   331 			$priority = current( $this->iterations[ $nesting_level ] );
   347 			$priority = current( $this->iterations[ $nesting_level ] );
       
   348 
   332 			foreach ( $this->callbacks[ $priority ] as $the_ ) {
   349 			foreach ( $this->callbacks[ $priority ] as $the_ ) {
   333 				call_user_func_array( $the_['function'], $args );
   350 				call_user_func_array( $the_['function'], $args );
   334 			}
   351 			}
   335 		} while ( false !== next( $this->iterations[ $nesting_level ] ) );
   352 		} while ( false !== next( $this->iterations[ $nesting_level ] ) );
   336 
   353 
   341 	/**
   358 	/**
   342 	 * Return the current priority level of the currently running iteration of the hook.
   359 	 * Return the current priority level of the currently running iteration of the hook.
   343 	 *
   360 	 *
   344 	 * @since 4.7.0
   361 	 * @since 4.7.0
   345 	 *
   362 	 *
   346 	 * @return int|false If the hook is running, return the current priority level. If it isn't running, return false.
   363 	 * @return int|false If the hook is running, return the current priority level.
       
   364 	 *                   If it isn't running, return false.
   347 	 */
   365 	 */
   348 	public function current_priority() {
   366 	public function current_priority() {
   349 		if ( false === current( $this->iterations ) ) {
   367 		if ( false === current( $this->iterations ) ) {
   350 			return false;
   368 			return false;
   351 		}
   369 		}
   354 	}
   372 	}
   355 
   373 
   356 	/**
   374 	/**
   357 	 * Normalizes filters set up before WordPress has initialized to WP_Hook objects.
   375 	 * Normalizes filters set up before WordPress has initialized to WP_Hook objects.
   358 	 *
   376 	 *
   359 	 * @since 4.7.0
   377 	 * The `$filters` parameter should be an array keyed by hook name, with values
   360 	 *
   378 	 * containing either:
   361 	 * @param array $filters Filters to normalize.
   379 	 *
       
   380 	 *  - A `WP_Hook` instance
       
   381 	 *  - An array of callbacks keyed by their priorities
       
   382 	 *
       
   383 	 * Examples:
       
   384 	 *
       
   385 	 *     $filters = array(
       
   386 	 *         'wp_fatal_error_handler_enabled' => array(
       
   387 	 *             10 => array(
       
   388 	 *                 array(
       
   389 	 *                     'accepted_args' => 0,
       
   390 	 *                     'function'      => function() {
       
   391 	 *                         return false;
       
   392 	 *                     },
       
   393 	 *                 ),
       
   394 	 *             ),
       
   395 	 *         ),
       
   396 	 *     );
       
   397 	 *
       
   398 	 * @since 4.7.0
       
   399 	 *
       
   400 	 * @param array $filters Filters to normalize. See documentation above for details.
   362 	 * @return WP_Hook[] Array of normalized filters.
   401 	 * @return WP_Hook[] Array of normalized filters.
   363 	 */
   402 	 */
   364 	public static function build_preinitialized_hooks( $filters ) {
   403 	public static function build_preinitialized_hooks( $filters ) {
   365 		/** @var WP_Hook[] $normalized */
   404 		/** @var WP_Hook[] $normalized */
   366 		$normalized = array();
   405 		$normalized = array();
   367 
   406 
   368 		foreach ( $filters as $tag => $callback_groups ) {
   407 		foreach ( $filters as $hook_name => $callback_groups ) {
   369 			if ( is_object( $callback_groups ) && $callback_groups instanceof WP_Hook ) {
   408 			if ( is_object( $callback_groups ) && $callback_groups instanceof WP_Hook ) {
   370 				$normalized[ $tag ] = $callback_groups;
   409 				$normalized[ $hook_name ] = $callback_groups;
   371 				continue;
   410 				continue;
   372 			}
   411 			}
       
   412 
   373 			$hook = new WP_Hook();
   413 			$hook = new WP_Hook();
   374 
   414 
   375 			// Loop through callback groups.
   415 			// Loop through callback groups.
   376 			foreach ( $callback_groups as $priority => $callbacks ) {
   416 			foreach ( $callback_groups as $priority => $callbacks ) {
   377 
   417 
   378 				// Loop through callbacks.
   418 				// Loop through callbacks.
   379 				foreach ( $callbacks as $cb ) {
   419 				foreach ( $callbacks as $cb ) {
   380 					$hook->add_filter( $tag, $cb['function'], $priority, $cb['accepted_args'] );
   420 					$hook->add_filter( $hook_name, $cb['function'], $priority, $cb['accepted_args'] );
   381 				}
   421 				}
   382 			}
   422 			}
   383 			$normalized[ $tag ] = $hook;
   423 
   384 		}
   424 			$normalized[ $hook_name ] = $hook;
       
   425 		}
       
   426 
   385 		return $normalized;
   427 		return $normalized;
   386 	}
   428 	}
   387 
   429 
   388 	/**
   430 	/**
   389 	 * Determines whether an offset value exists.
   431 	 * Determines whether an offset value exists.
   488 	 *
   530 	 *
   489 	 * @since 4.7.0
   531 	 * @since 4.7.0
   490 	 *
   532 	 *
   491 	 * @link https://www.php.net/manual/en/iterator.valid.php
   533 	 * @link https://www.php.net/manual/en/iterator.valid.php
   492 	 *
   534 	 *
   493 	 * @return boolean
   535 	 * @return bool Whether the current position is valid.
   494 	 */
   536 	 */
   495 	public function valid() {
   537 	public function valid() {
   496 		return key( $this->callbacks ) !== null;
   538 		return key( $this->callbacks ) !== null;
   497 	}
   539 	}
   498 
   540