Wordpress 2.6.2-scripts
[autoinstalls/wordpress.git] / wp-includes / plugin.php
1 <?php
2 /**
3  * The plugin API is located in this file, which allows for creating actions
4  * and filters and hooking functions, and methods. The functions or methods will
5  * then be run when the action or filter is called.
6  *
7  * The API callback examples reference functions, but can be methods of classes.
8  * To hook methods, you'll need to pass an array one of two ways.
9  *
10  * Any of the syntaxes explained in the PHP documentation for the
11  * {@link http://us2.php.net/manual/en/language.pseudo-types.php#language.types.callback 'callback'}
12  * type are valid.
13  *
14  * Also see the {@link http://codex.wordpress.org/Plugin_API Plugin API} for more information
15  * and examples on how to use a lot of these functions.
16  *
17  * @package WordPress
18  * @subpackage Plugin
19  * @since 1.5
20  */
21
22 /**
23  * add_filter() - Hooks a function or method to a specific filter action.
24  *
25  * Filters are the hooks that WordPress launches to modify text of various types
26  * before adding it to the database or sending it to the browser screen. Plugins
27  * can specify that one or more of its PHP functions is executed to
28  * modify specific types of text at these times, using the Filter API.
29  *
30  * To use the API, the following code should be used to bind a callback to the filter
31  * <code>
32  * function example_hook($example) { echo $example; }
33  *
34  * add_filter('example_filter', 'example_hook');
35  * </code>
36  *
37  * In WordPress 1.5.1+, hooked functions can take extra arguments that are set when
38  * the matching do_action() or apply_filters() call is run. The <tt>$accepted_args
39  * allow for calling functions only when the number of args match. Hooked functions
40  * can take extra arguments that are set when the matching <tt>do_action()</tt> or
41  * <tt>apply_filters()</tt> call is run. For example, the action <tt>comment_id_not_found</tt>
42  * will pass any functions that hook onto it the ID of the requested comment.
43  *
44  * <strong>Note:</strong> the function will return true no matter if the function was hooked
45  * fails or not. There are no checks for whether the function exists beforehand and no checks
46  * to whether the <tt>$function_to_add is even a string. It is up to you to take care and
47  * this is done for optimization purposes, so everything is as quick as possible.
48  *
49  * @package WordPress
50  * @subpackage Plugin
51  * @since 0.71
52  * @global array $wp_filter Stores all of the filters added in the form of
53  *      wp_filter['tag']['array of priorities']['array of functions serialized']['array of ['array (functions, accepted_args)]']
54  * @global array $merged_filters Tracks the tags that need to be merged for later. If the hook is added, it doesn't need to run through that process.
55  *
56  * @param string $tag The name of the filter to hook the <tt>$function_to_add</tt> to.
57  * @param callback $function_to_add The name of the function to be called when the filter is applied.
58  * @param int $priority optional. Used to specify the order in which the functions associated with a particular action are executed (default: 10). Lower numbers correspond with earlier execution, and functions with the same priority are executed in the order in which they were added to the action.
59  * @param int $accepted_args optional. The number of arguments the function accept (default 1).
60  * @return boolean true
61  */
62 function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
63         global $wp_filter, $merged_filters;
64
65         $idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
66         $wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);
67         unset( $merged_filters[ $tag ] );
68         return true;
69 }
70
71 /**
72  * has_filter() - Check if any filter has been registered for a hook.
73  *
74  * @package WordPress
75  * @subpackage Plugin
76  * @since 2.5
77  * @global array $wp_filter Stores all of the filters
78  *
79  * @param string $tag The name of the filter hook.
80  * @param callback $function_to_check optional.  If specified, return the priority of that function on this hook or false if not attached.
81  * @return int|boolean Optionally returns the priority on that hook for the specified function.
82  */
83 function has_filter($tag, $function_to_check = false) {
84         global $wp_filter;
85
86         $has = !empty($wp_filter[$tag]);
87         if ( false === $function_to_check || false == $has )
88                 return $has;
89
90         if ( !$idx = _wp_filter_build_unique_id($tag, $function_to_check, false) )
91                 return false;
92
93         foreach ( array_keys($wp_filter[$tag]) as $priority ) {
94                 if ( isset($wp_filter[$tag][$priority][$idx]) )
95                         return $priority;
96         }
97
98         return false;
99 }
100
101 /**
102  * apply_filters() - Call the functions added to a filter hook.
103  *
104  * The callback functions attached to filter hook <tt>$tag</tt> are invoked by
105  * calling this function. This function can be used to create a new filter hook
106  * by simply calling this function with the name of the new hook specified using
107  * the <tt>$tag</a> parameter.
108  *
109  * The function allows for additional arguments to be added and passed to hooks.
110  * <code>
111  * function example_hook($string, $arg1, $arg2)
112  * {
113  *              //Do stuff
114  *              return $string;
115  * }
116  * $value = apply_filters('example_filter', 'filter me', 'arg1', 'arg2');
117  * </code>
118  *
119  * @package WordPress
120  * @subpackage Plugin
121  * @since 0.71
122  * @global array $wp_filter Stores all of the filters
123  * @global array $merge_filters Merges the filter hooks using this function.
124  * @global array $wp_current_filter stores the list of current filters with the current one last
125  *
126  * @param string $tag The name of the filter hook.
127  * @param mixed $value The value on which the filters hooked to <tt>$tag</tt> are applied on.
128  * @param mixed $var,... Additional variables passed to the functions hooked to <tt>$tag</tt>.
129  * @return mixed The filtered value after all hooked functions are applied to it.
130  */
131 function apply_filters($tag, $value) {
132         global $wp_filter, $merged_filters, $wp_current_filter;
133
134         $args = array();
135         $wp_current_filter[] = $tag;
136
137         // Do 'all' actions first
138         if ( isset($wp_filter['all']) ) {
139                 $args = func_get_args();
140                 _wp_call_all_hook($args);
141         }
142
143         if ( !isset($wp_filter[$tag]) ) {
144                 array_pop($wp_current_filter);
145                 return $value;
146         }
147
148         // Sort
149         if ( !isset( $merged_filters[ $tag ] ) ) {
150                 ksort($wp_filter[$tag]);
151                 $merged_filters[ $tag ] = true;
152         }
153
154         reset( $wp_filter[ $tag ] );
155
156         if ( empty($args) )
157                 $args = func_get_args();
158
159         do {
160                 foreach( (array) current($wp_filter[$tag]) as $the_ )
161                         if ( !is_null($the_['function']) ){
162                                 $args[1] = $value;
163                                 $value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $the_['accepted_args']));
164                         }
165
166         } while ( next($wp_filter[$tag]) !== false );
167
168         array_pop( $wp_current_filter );
169
170         return $value;
171 }
172
173 /**
174  * remove_filter() - Removes a function from a specified filter hook.
175  *
176  * This function removes a function attached to a specified filter hook. This
177  * method can be used to remove default functions attached to a specific filter
178  * hook and possibly replace them with a substitute.
179  *
180  * To remove a hook, the <tt>$function_to_remove</tt> and <tt>$priority</tt> arguments
181  * must match when the hook was added. This goes for both filters and actions. No warning
182  * will be given on removal failure.
183  *
184  * @package WordPress
185  * @subpackage Plugin
186  * @since 1.2
187  *
188  * @param string $tag The filter hook to which the function to be removed is hooked.
189  * @param callback $function_to_remove The name of the function which should be removed.
190  * @param int $priority optional. The priority of the function (default: 10).
191  * @param int $accepted_args optional. The number of arguments the function accpets (default: 1).
192  * @return boolean Whether the function existed before it was removed.
193  */
194 function remove_filter($tag, $function_to_remove, $priority = 10, $accepted_args = 1) {
195         $function_to_remove = _wp_filter_build_unique_id($tag, $function_to_remove, $priority);
196
197         $r = isset($GLOBALS['wp_filter'][$tag][$priority][$function_to_remove]);
198
199         if ( true === $r) {
200                 unset($GLOBALS['wp_filter'][$tag][$priority][$function_to_remove]);
201                 if ( empty($GLOBALS['wp_filter'][$tag][$priority]) )
202                         unset($GLOBALS['wp_filter'][$tag][$priority]);
203                 unset($GLOBALS['merged_filters'][$tag]);
204         }
205
206         return $r;
207 }
208
209
210 /**
211  * current_filter() - Return the name of the current filter or action.
212  *
213  * @package WordPress
214  * @subpackage Plugin
215  * @since 2.5
216  *
217  * @return string Hook name of the current filter or action.
218  */
219 function current_filter() {
220         global $wp_current_filter;
221         return end( $wp_current_filter );
222 }
223
224
225 /**
226  * add_action() - Hooks a function on to a specific action.
227  *
228  * Actions are the hooks that the WordPress core launches at specific points
229  * during execution, or when specific events occur. Plugins can specify that
230  * one or more of its PHP functions are executed at these points, using the
231  * Action API.
232  *
233  * @uses add_filter() Adds an action. Parameter list and functionality are the same.
234  *
235  * @package WordPress
236  * @subpackage Plugin
237  * @since 1.2
238  *
239  * @param string $tag The name of the action to which the <tt>$function_to-add</tt> is hooked.
240  * @param callback $function_to_add The name of the function you wish to be called.
241  * @param int $priority optional. Used to specify the order in which the functions associated with a particular action are executed (default: 10). Lower numbers correspond with earlier execution, and functions with the same priority are executed in the order in which they were added to the action.
242  * @param int $accepted_args optional. The number of arguments the function accept (default 1).
243  */
244 function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
245         return add_filter($tag, $function_to_add, $priority, $accepted_args);
246 }
247
248
249 /**
250  * do_action() - Execute functions hooked on a specific action hook.
251  *
252  * This function invokes all functions attached to action hook <tt>$tag</tt>.
253  * It is possible to create new action hooks by simply calling this function,
254  * specifying the name of the new hook using the <tt>$tag</tt> parameter.
255  *
256  * You can pass extra arguments to the hooks, much like you can with apply_filters().
257  *
258  * @see apply_filters() This function works similar with the exception that nothing is
259  * returned and only the functions or methods are called.
260  *
261  * @package WordPress
262  * @subpackage Plugin
263  * @since 1.2
264  * @global array $wp_filter Stores all of the filters
265  * @global array $wp_actions Increments the amount of times action was triggered.
266  *
267  * @param string $tag The name of the action to be executed.
268  * @param mixed $arg,... Optional additional arguments which are passed on to the functions hooked to the action.
269  * @return null Will return null if $tag does not exist in $wp_filter array
270  */
271 function do_action($tag, $arg = '') {
272         global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter;
273
274         if ( is_array($wp_actions) )
275                 $wp_actions[] = $tag;
276         else
277                 $wp_actions = array($tag);
278
279         $wp_current_filter[] = $tag;
280
281         // Do 'all' actions first
282         if ( isset($wp_filter['all']) ) {
283                 $all_args = func_get_args();
284                 _wp_call_all_hook($all_args);
285         }
286
287         if ( !isset($wp_filter[$tag]) ) {
288                 array_pop($wp_current_filter);
289                 return;
290         }
291
292         $args = array();
293         if ( is_array($arg) && 1 == count($arg) && is_object($arg[0]) ) // array(&$this)
294                 $args[] =& $arg[0];
295         else
296                 $args[] = $arg;
297         for ( $a = 2; $a < func_num_args(); $a++ )
298                 $args[] = func_get_arg($a);
299
300         // Sort
301         if ( !isset( $merged_filters[ $tag ] ) ) {
302                 ksort($wp_filter[$tag]);
303                 $merged_filters[ $tag ] = true;
304         }
305
306         reset( $wp_filter[ $tag ] );
307
308         do {
309                 foreach ( (array) current($wp_filter[$tag]) as $the_ )
310                         if ( !is_null($the_['function']) )
311                                 call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
312
313         } while ( next($wp_filter[$tag]) !== false );
314
315         array_pop($wp_current_filter);
316 }
317
318 /**
319  * did_action() - Return the number times an action is fired.
320  *
321  * @package WordPress
322  * @subpackage Plugin
323  * @since 2.1
324  * @global array $wp_actions Increments the amount of times action was triggered.
325  *
326  * @param string $tag The name of the action hook.
327  * @return int The number of times action hook <tt>$tag</tt> is fired
328  */
329 function did_action($tag) {
330         global $wp_actions;
331
332         if ( empty($wp_actions) )
333                 return 0;
334
335         return count(array_keys($wp_actions, $tag));
336 }
337
338 /**
339  * do_action_ref_array() - Execute functions hooked on a specific action hook, specifying arguments in an array.
340  *
341  * @see do_action() This function is identical, but the arguments passed to
342  * the functions hooked to <tt>$tag</tt> are supplied using an array.
343  *
344  * @package WordPress
345  * @subpackage Plugin
346  * @since 2.1
347  * @global array $wp_filter Stores all of the filters
348  * @global array $wp_actions Increments the amount of times action was triggered.
349  *
350  * @param string $tag The name of the action to be executed.
351  * @param array $args The arguments supplied to the functions hooked to <tt>$tag</tt>
352  * @return null Will return null if $tag does not exist in $wp_filter array
353  */
354 function do_action_ref_array($tag, $args) {
355         global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter;
356
357         if ( !is_array($wp_actions) )
358                 $wp_actions = array($tag);
359         else
360                 $wp_actions[] = $tag;
361
362         $wp_current_filter[] = $tag;
363
364         // Do 'all' actions first
365         if ( isset($wp_filter['all']) ) {
366                 $all_args = func_get_args();
367                 _wp_call_all_hook($all_args);
368         }
369
370         if ( !isset($wp_filter[$tag]) ) {
371                 array_pop($wp_current_filter);
372                 return;
373         }
374
375         // Sort
376         if ( !isset( $merged_filters[ $tag ] ) ) {
377                 ksort($wp_filter[$tag]);
378                 $merged_filters[ $tag ] = true;
379         }
380
381         reset( $wp_filter[ $tag ] );
382
383         do {
384                 foreach( (array) current($wp_filter[$tag]) as $the_ )
385                         if ( !is_null($the_['function']) )
386                                 call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
387
388         } while ( next($wp_filter[$tag]) !== false );
389
390         array_pop($wp_current_filter);
391 }
392
393 /**
394  * has_action() - Check if any action has been registered for a hook.
395  *
396  * @package WordPress
397  * @subpackage Plugin
398  * @since 2.5
399  * @see has_filter() has_action() is an alias of has_filter().
400  *
401  * @param string $tag The name of the action hook.
402  * @param callback $function_to_check optional.  If specified, return the priority of that function on this hook or false if not attached.
403  * @return int|boolean Optionally returns the priority on that hook for the specified function.
404  */
405 function has_action($tag, $function_to_check = false) {
406         return has_filter($tag, $function_to_check);
407 }
408
409 /**
410  * remove_action() - Removes a function from a specified action hook.
411  *
412  * This function removes a function attached to a specified action hook. This
413  * method can be used to remove default functions attached to a specific filter
414  * hook and possibly replace them with a substitute.
415  *
416  * @package WordPress
417  * @subpackage Plugin
418  * @since 1.2
419  *
420  * @param string $tag The action hook to which the function to be removed is hooked.
421  * @param callback $function_to_remove The name of the function which should be removed.
422  * @param int $priority optional The priority of the function (default: 10).
423  * @param int $accepted_args optional. The number of arguments the function accpets (default: 1).
424  * @return boolean Whether the function is removed.
425  */
426 function remove_action($tag, $function_to_remove, $priority = 10, $accepted_args = 1) {
427         return remove_filter($tag, $function_to_remove, $priority, $accepted_args);
428 }
429
430 //
431 // Functions for handling plugins.
432 //
433
434 /**
435  * plugin_basename() - Gets the basename of a plugin.
436  *
437  * This method extracts the name of a plugin from its filename.
438  *
439  * @package WordPress
440  * @subpackage Plugin
441  * @since 1.5
442  *
443  * @access private
444  *
445  * @param string $file The filename of plugin.
446  * @return string The name of a plugin.
447  * @uses WP_PLUGIN_DIR
448  */
449 function plugin_basename($file) {
450         $file = str_replace('\\','/',$file); // sanitize for Win32 installs
451         $file = preg_replace('|/+|','/', $file); // remove any duplicate slash
452         $plugin_dir = str_replace('\\','/',WP_PLUGIN_DIR); // sanitize for Win32 installs
453         $plugin_dir = preg_replace('|/+|','/', $plugin_dir); // remove any duplicate slash
454         $file = preg_replace('|^' . preg_quote($plugin_dir, '|') . '/|','',$file); // get relative path from plugins dir
455         return $file;
456 }
457
458 /**
459  * register_activation_hook() - Hook a function on a plugin activation action hook.
460  *
461  * When a plugin is activated, the action 'activate_PLUGINNAME' hook is
462  * activated. In the name of this hook, PLUGINNAME is replaced with the name of
463  * the plugin, including the optional subdirectory. For example, when the plugin
464  * is located in <tt>wp-content/plugin/sampleplugin/sample.php</tt>, then the
465  * name of this hook will become 'activate_sampleplugin/sample.php'
466  * When the plugin consists of only one file and is (as by default) located at
467  * <tt>wp-content/plugin/sample.php</tt> the name of this hook will be
468  * 'activate_sample.php'.
469  *
470  * @package WordPress
471  * @subpackage Plugin
472  * @since 2.0
473  *
474  * @access private
475  *
476  * @param string $file The filename of the plugin including the path.
477  * @param string $function the function hooked to the 'activate_PLUGIN' action.
478  */
479 function register_activation_hook($file, $function) {
480         $file = plugin_basename($file);
481         add_action('activate_' . $file, $function);
482 }
483
484 /**
485  * register_deactivation_hook() - Hook a function on a plugin deactivation action hook.
486  *
487  * When a plugin is deactivated, the action 'deactivate_PLUGINNAME' hook is
488  * deactivated. In the name of this hook, PLUGINNAME is replaced with the name of
489  * the plugin, including the optional subdirectory. For example, when the plugin
490  * is located in <tt>wp-content/plugin/sampleplugin/sample.php</tt>, then the
491  * name of this hook will become 'activate_sampleplugin/sample.php'.
492  * When the plugin consists of only one file and is (as by default) located at
493  * <tt>wp-content/plugin/sample.php</tt> the name of this hook will be
494  * 'activate_sample.php'.
495  *
496  * @package WordPress
497  * @subpackage Plugin
498  * @since 2.0
499  *
500  * @access private
501  *
502  * @param string $file The filename of the plugin including the path.
503  * @param string $function the function hooked to the 'activate_PLUGIN' action.
504  */
505 function register_deactivation_hook($file, $function) {
506         $file = plugin_basename($file);
507         add_action('deactivate_' . $file, $function);
508 }
509
510 /**
511  * _wp_call_all_hook() - Calls the 'all' hook, which will process the functions hooked into it.
512  *
513  * The 'all' hook passes all of the arguments or parameters that were used for the
514  * hook, which this function was called for.
515  *
516  * This function is used internally for apply_filters(), do_action(), and do_action_ref_array()
517  * and is not meant to be used from outside those functions. This function does not check for the
518  * existence of the all hook, so it will fail unless the all hook exists prior to this function call.
519  *
520  * @package WordPress
521  * @subpackage Plugin
522  * @since 2.5
523  * @access private
524  *
525  * @uses $wp_filter Used to process all of the functions in the 'all' hook
526  *
527  * @param array $args The collected parameters from the hook that was called.
528  * @param string $hook Optional. The hook name that was used to call the 'all' hook.
529  */
530 function _wp_call_all_hook($args) {
531         global $wp_filter;
532
533         reset( $wp_filter['all'] );
534         do {
535                 foreach( (array) current($wp_filter['all']) as $the_ )
536                         if ( !is_null($the_['function']) )
537                                 call_user_func_array($the_['function'], $args);
538
539         } while ( next($wp_filter['all']) !== false );
540 }
541
542 /**
543  * _wp_filter_build_unique_id() - Build Unique ID for storage and retrieval
544  *
545  * The old way to serialize the callback caused issues and this function is the
546  * solution. It works by checking for objects and creating an a new property in
547  * the class to keep track of the object and new objects of the same class that
548  * need to be added.
549  *
550  * It also allows for the removal of actions and filters for objects after they
551  * change class properties. It is possible to include the property $wp_filter_id
552  * in your class and set it to "null" or a number to bypass the workaround. However
553  * this will prevent you from adding new classes and any new classes will overwrite
554  * the previous hook by the same class.
555  *
556  * Functions and static method callbacks are just returned as strings and shouldn't
557  * have any speed penalty.
558  *
559  * @package WordPress
560  * @subpackage Plugin
561  * @since 2.2.3
562  *
563  * @link http://trac.wordpress.org/ticket/3875
564  *
565  * @access private
566  *
567  * @global array $wp_filter Storage for all of the filters and actions
568  * @param string $tag Used in counting how many hooks were applied
569  * @param string|array $function Used for creating unique id
570  * @param int|bool $priority Used in counting how many hooks were applied.  If === false and $function is an object reference, we return the unique id only if it already has one, false otherwise.
571  * @param string $type filter or action
572  * @return string Unique ID for usage as array key
573  */
574 function _wp_filter_build_unique_id($tag, $function, $priority) {
575         global $wp_filter;
576
577         // If function then just skip all of the tests and not overwrite the following.
578         if ( is_string($function) )
579                 return $function;
580         // Object Class Calling
581         else if (is_object($function[0]) ) {
582                 $obj_idx = get_class($function[0]).$function[1];
583                 if ( !isset($function[0]->wp_filter_id) ) {
584                         if ( false === $priority )
585                                 return false;
586                         $count = count((array)$wp_filter[$tag][$priority]);
587                         $function[0]->wp_filter_id = $count;
588                         $obj_idx .= $count;
589                         unset($count);
590                 } else
591                         $obj_idx .= $function[0]->wp_filter_id;
592                 return $obj_idx;
593         }
594         // Static Calling
595         else if ( is_string($function[0]) )
596                 return $function[0].$function[1];
597 }
598
599 ?>