WordPress 3.7.2-scripts
[autoinstalls/wordpress.git] / wp-includes / cron.php
1 <?php
2 /**
3  * WordPress CRON API
4  *
5  * @package WordPress
6  */
7
8 /**
9  * Schedules a hook to run only once.
10  *
11  * Schedules a hook which will be executed once by the WordPress actions core at
12  * a time which you specify. The action will fire off when someone visits your
13  * WordPress site, if the schedule time has passed.
14  *
15  * @since 2.1.0
16  * @link http://codex.wordpress.org/Function_Reference/wp_schedule_single_event
17  *
18  * @param int $timestamp Timestamp for when to run the event.
19  * @param string $hook Action hook to execute when cron is run.
20  * @param array $args Optional. Arguments to pass to the hook's callback function.
21  */
22 function wp_schedule_single_event( $timestamp, $hook, $args = array()) {
23         // don't schedule a duplicate if there's already an identical event due in the next 10 minutes
24         $next = wp_next_scheduled($hook, $args);
25         if ( $next && $next <= $timestamp + 10 * MINUTE_IN_SECONDS )
26                 return;
27
28         $crons = _get_cron_array();
29         $event = (object) array( 'hook' => $hook, 'timestamp' => $timestamp, 'schedule' => false, 'args' => $args );
30         $event = apply_filters('schedule_event', $event);
31
32         // A plugin disallowed this event
33         if ( ! $event )
34                 return false;
35
36         $key = md5(serialize($event->args));
37
38         $crons[$event->timestamp][$event->hook][$key] = array( 'schedule' => $event->schedule, 'args' => $event->args );
39         uksort( $crons, "strnatcasecmp" );
40         _set_cron_array( $crons );
41 }
42
43 /**
44  * Schedule a periodic event.
45  *
46  * Schedules a hook which will be executed by the WordPress actions core on a
47  * specific interval, specified by you. The action will trigger when someone
48  * visits your WordPress site, if the scheduled time has passed.
49  *
50  * Valid values for the recurrence are hourly, daily and twicedaily. These can
51  * be extended using the cron_schedules filter in wp_get_schedules().
52  *
53  * Use wp_next_scheduled() to prevent duplicates
54  *
55  * @since 2.1.0
56  *
57  * @param int $timestamp Timestamp for when to run the event.
58  * @param string $recurrence How often the event should recur.
59  * @param string $hook Action hook to execute when cron is run.
60  * @param array $args Optional. Arguments to pass to the hook's callback function.
61  * @return bool|null False on failure, null when complete with scheduling event.
62  */
63 function wp_schedule_event( $timestamp, $recurrence, $hook, $args = array()) {
64         $crons = _get_cron_array();
65         $schedules = wp_get_schedules();
66
67         if ( !isset( $schedules[$recurrence] ) )
68                 return false;
69
70         $event = (object) array( 'hook' => $hook, 'timestamp' => $timestamp, 'schedule' => $recurrence, 'args' => $args, 'interval' => $schedules[$recurrence]['interval'] );
71         $event = apply_filters('schedule_event', $event);
72
73         // A plugin disallowed this event
74         if ( ! $event )
75                 return false;
76
77         $key = md5(serialize($event->args));
78
79         $crons[$event->timestamp][$event->hook][$key] = array( 'schedule' => $event->schedule, 'args' => $event->args, 'interval' => $event->interval );
80         uksort( $crons, "strnatcasecmp" );
81         _set_cron_array( $crons );
82 }
83
84 /**
85  * Reschedule a recurring event.
86  *
87  * @since 2.1.0
88  *
89  * @param int $timestamp Timestamp for when to run the event.
90  * @param string $recurrence How often the event should recur.
91  * @param string $hook Action hook to execute when cron is run.
92  * @param array $args Optional. Arguments to pass to the hook's callback function.
93  * @return bool|null False on failure. Null when event is rescheduled.
94  */
95 function wp_reschedule_event( $timestamp, $recurrence, $hook, $args = array()) {
96         $crons = _get_cron_array();
97         $schedules = wp_get_schedules();
98         $key = md5(serialize($args));
99         $interval = 0;
100
101         // First we try to get it from the schedule
102         if ( 0 == $interval )
103                 $interval = $schedules[$recurrence]['interval'];
104         // Now we try to get it from the saved interval in case the schedule disappears
105         if ( 0 == $interval )
106                 $interval = $crons[$timestamp][$hook][$key]['interval'];
107         // Now we assume something is wrong and fail to schedule
108         if ( 0 == $interval )
109                 return false;
110
111         $now = time();
112
113         if ( $timestamp >= $now )
114                 $timestamp = $now + $interval;
115         else
116                 $timestamp = $now + ($interval - (($now - $timestamp) % $interval));
117
118         wp_schedule_event( $timestamp, $recurrence, $hook, $args );
119 }
120
121 /**
122  * Unschedule a previously scheduled cron job.
123  *
124  * The $timestamp and $hook parameters are required, so that the event can be
125  * identified.
126  *
127  * @since 2.1.0
128  *
129  * @param int $timestamp Timestamp for when to run the event.
130  * @param string $hook Action hook, the execution of which will be unscheduled.
131  * @param array $args Arguments to pass to the hook's callback function.
132  * Although not passed to a callback function, these arguments are used
133  * to uniquely identify the scheduled event, so they should be the same
134  * as those used when originally scheduling the event.
135  */
136 function wp_unschedule_event( $timestamp, $hook, $args = array() ) {
137         $crons = _get_cron_array();
138         $key = md5(serialize($args));
139         unset( $crons[$timestamp][$hook][$key] );
140         if ( empty($crons[$timestamp][$hook]) )
141                 unset( $crons[$timestamp][$hook] );
142         if ( empty($crons[$timestamp]) )
143                 unset( $crons[$timestamp] );
144         _set_cron_array( $crons );
145 }
146
147 /**
148  * Unschedule all cron jobs attached to a specific hook.
149  *
150  * @since 2.1.0
151  *
152  * @param string $hook Action hook, the execution of which will be unscheduled.
153  * @param array $args Optional. Arguments that were to be pass to the hook's callback function.
154  */
155 function wp_clear_scheduled_hook( $hook, $args = array() ) {
156         // Backward compatibility
157         // Previously this function took the arguments as discrete vars rather than an array like the rest of the API
158         if ( !is_array($args) ) {
159                 _deprecated_argument( __FUNCTION__, '3.0', __('This argument has changed to an array to match the behavior of the other cron functions.') );
160                 $args = array_slice( func_get_args(), 1 );
161         }
162
163         // This logic duplicates wp_next_scheduled()
164         // It's required due to a scenario where wp_unschedule_event() fails due to update_option() failing,
165         // and, wp_next_scheduled() returns the same schedule in an infinite loop.
166         $crons = _get_cron_array();
167         if ( empty( $crons ) )
168                 return;
169
170         $key = md5( serialize( $args ) );
171         foreach ( $crons as $timestamp => $cron ) {
172                 if ( isset( $cron[ $hook ][ $key ] ) ) {
173                         wp_unschedule_event( $timestamp, $hook, $args );
174                 }
175         }
176 }
177
178 /**
179  * Retrieve the next timestamp for a cron event.
180  *
181  * @since 2.1.0
182  *
183  * @param string $hook Action hook to execute when cron is run.
184  * @param array $args Optional. Arguments to pass to the hook's callback function.
185  * @return bool|int The UNIX timestamp of the next time the scheduled event will occur.
186  */
187 function wp_next_scheduled( $hook, $args = array() ) {
188         $crons = _get_cron_array();
189         $key = md5(serialize($args));
190         if ( empty($crons) )
191                 return false;
192         foreach ( $crons as $timestamp => $cron ) {
193                 if ( isset( $cron[$hook][$key] ) )
194                         return $timestamp;
195         }
196         return false;
197 }
198
199 /**
200  * Send request to run cron through HTTP request that doesn't halt page loading.
201  *
202  * @since 2.1.0
203  *
204  * @return null Cron could not be spawned, because it is not needed to run.
205  */
206 function spawn_cron( $gmt_time = 0 ) {
207
208         if ( ! $gmt_time )
209                 $gmt_time = microtime( true );
210
211         if ( defined('DOING_CRON') || isset($_GET['doing_wp_cron']) )
212                 return;
213
214         /*
215         * multiple processes on multiple web servers can run this code concurrently
216         * try to make this as atomic as possible by setting doing_cron switch
217         */
218         $lock = get_transient('doing_cron');
219
220         if ( $lock > $gmt_time + 10 * MINUTE_IN_SECONDS )
221                 $lock = 0;
222
223         // don't run if another process is currently running it or more than once every 60 sec.
224         if ( $lock + WP_CRON_LOCK_TIMEOUT > $gmt_time )
225                 return;
226
227         //sanity check
228         $crons = _get_cron_array();
229         if ( !is_array($crons) )
230                 return;
231
232         $keys = array_keys( $crons );
233         if ( isset($keys[0]) && $keys[0] > $gmt_time )
234                 return;
235
236         if ( defined('ALTERNATE_WP_CRON') && ALTERNATE_WP_CRON ) {
237                 if ( !empty($_POST) || defined('DOING_AJAX') )
238                         return;
239
240                 $doing_wp_cron = sprintf( '%.22F', $gmt_time );
241                 set_transient( 'doing_cron', $doing_wp_cron );
242
243                 ob_start();
244                 wp_redirect( add_query_arg( 'doing_wp_cron', $doing_wp_cron, wp_unslash( $_SERVER['REQUEST_URI'] ) ) );
245                 echo ' ';
246
247                 // flush any buffers and send the headers
248                 while ( @ob_end_flush() );
249                 flush();
250
251                 WP_DEBUG ? include_once( ABSPATH . 'wp-cron.php' ) : @include_once( ABSPATH . 'wp-cron.php' );
252                 return;
253         }
254
255         $doing_wp_cron = sprintf( '%.22F', $gmt_time );
256         set_transient( 'doing_cron', $doing_wp_cron );
257
258         $cron_request = apply_filters( 'cron_request', array(
259                 'url' => site_url( 'wp-cron.php?doing_wp_cron=' . $doing_wp_cron ),
260                 'key' => $doing_wp_cron,
261                 'args' => array( 'timeout' => 0.01, 'blocking' => false, 'sslverify' => apply_filters( 'https_local_ssl_verify', true ) )
262         ) );
263
264         wp_remote_post( $cron_request['url'], $cron_request['args'] );
265 }
266
267 /**
268  * Run scheduled callbacks or spawn cron for all scheduled events.
269  *
270  * @since 2.1.0
271  *
272  * @return null When doesn't need to run Cron.
273  */
274 function wp_cron() {
275
276         // Prevent infinite loops caused by lack of wp-cron.php
277         if ( strpos($_SERVER['REQUEST_URI'], '/wp-cron.php') !== false || ( defined('DISABLE_WP_CRON') && DISABLE_WP_CRON ) )
278                 return;
279
280         if ( false === $crons = _get_cron_array() )
281                 return;
282
283         $gmt_time = microtime( true );
284         $keys = array_keys( $crons );
285         if ( isset($keys[0]) && $keys[0] > $gmt_time )
286                 return;
287
288         $schedules = wp_get_schedules();
289         foreach ( $crons as $timestamp => $cronhooks ) {
290                 if ( $timestamp > $gmt_time ) break;
291                 foreach ( (array) $cronhooks as $hook => $args ) {
292                         if ( isset($schedules[$hook]['callback']) && !call_user_func( $schedules[$hook]['callback'] ) )
293                                 continue;
294                         spawn_cron( $gmt_time );
295                         break 2;
296                 }
297         }
298 }
299
300 /**
301  * Retrieve supported and filtered Cron recurrences.
302  *
303  * The supported recurrences are 'hourly' and 'daily'. A plugin may add more by
304  * hooking into the 'cron_schedules' filter. The filter accepts an array of
305  * arrays. The outer array has a key that is the name of the schedule or for
306  * example 'weekly'. The value is an array with two keys, one is 'interval' and
307  * the other is 'display'.
308  *
309  * The 'interval' is a number in seconds of when the cron job should run. So for
310  * 'hourly', the time is 3600 or 60*60. For weekly, the value would be
311  * 60*60*24*7 or 604800. The value of 'interval' would then be 604800.
312  *
313  * The 'display' is the description. For the 'weekly' key, the 'display' would
314  * be <code>__('Once Weekly')</code>.
315  *
316  * For your plugin, you will be passed an array. you can easily add your
317  * schedule by doing the following.
318  * <code>
319  * // filter parameter variable name is 'array'
320  *      $array['weekly'] = array(
321  *              'interval' => 604800,
322  *              'display' => __('Once Weekly')
323  *      );
324  * </code>
325  *
326  * @since 2.1.0
327  *
328  * @return array
329  */
330 function wp_get_schedules() {
331         $schedules = array(
332                 'hourly'     => array( 'interval' => HOUR_IN_SECONDS,      'display' => __( 'Once Hourly' ) ),
333                 'twicedaily' => array( 'interval' => 12 * HOUR_IN_SECONDS, 'display' => __( 'Twice Daily' ) ),
334                 'daily'      => array( 'interval' => DAY_IN_SECONDS,       'display' => __( 'Once Daily' ) ),
335         );
336         return array_merge( apply_filters( 'cron_schedules', array() ), $schedules );
337 }
338
339 /**
340  * Retrieve Cron schedule for hook with arguments.
341  *
342  * @since 2.1.0
343  *
344  * @param string $hook Action hook to execute when cron is run.
345  * @param array $args Optional. Arguments to pass to the hook's callback function.
346  * @return string|bool False, if no schedule. Schedule on success.
347  */
348 function wp_get_schedule($hook, $args = array()) {
349         $crons = _get_cron_array();
350         $key = md5(serialize($args));
351         if ( empty($crons) )
352                 return false;
353         foreach ( $crons as $timestamp => $cron ) {
354                 if ( isset( $cron[$hook][$key] ) )
355                         return $cron[$hook][$key]['schedule'];
356         }
357         return false;
358 }
359
360 //
361 // Private functions
362 //
363
364 /**
365  * Retrieve cron info array option.
366  *
367  * @since 2.1.0
368  * @access private
369  *
370  * @return array CRON info array.
371  */
372 function _get_cron_array()  {
373         $cron = get_option('cron');
374         if ( ! is_array($cron) )
375                 return false;
376
377         if ( !isset($cron['version']) )
378                 $cron = _upgrade_cron_array($cron);
379
380         unset($cron['version']);
381
382         return $cron;
383 }
384
385 /**
386  * Updates the CRON option with the new CRON array.
387  *
388  * @since 2.1.0
389  * @access private
390  *
391  * @param array $cron Cron info array from {@link _get_cron_array()}.
392  */
393 function _set_cron_array($cron) {
394         $cron['version'] = 2;
395         update_option( 'cron', $cron );
396 }
397
398 /**
399  * Upgrade a Cron info array.
400  *
401  * This function upgrades the Cron info array to version 2.
402  *
403  * @since 2.1.0
404  * @access private
405  *
406  * @param array $cron Cron info array from {@link _get_cron_array()}.
407  * @return array An upgraded Cron info array.
408  */
409 function _upgrade_cron_array($cron) {
410         if ( isset($cron['version']) && 2 == $cron['version'])
411                 return $cron;
412
413         $new_cron = array();
414
415         foreach ( (array) $cron as $timestamp => $hooks) {
416                 foreach ( (array) $hooks as $hook => $args ) {
417                         $key = md5(serialize($args['args']));
418                         $new_cron[$timestamp][$hook][$key] = $args;
419                 }
420         }
421
422         $new_cron['version'] = 2;
423         update_option( 'cron', $new_cron );
424         return $new_cron;
425 }