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 ) { |
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. |