PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 12.0.3
Jetpack – WP Security, Backup, Speed, & Growth v12.0.3
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
jetpack / json-endpoints / jetpack / class.jetpack-json-api-cron-endpoint.php

class.jetpack-json-api-cron-endpoint.php in Jetpack – WP Security, Backup, Speed, & Growth 12.0.3, at json-endpoints/jetpack/class.jetpack-json-api-cron-endpoint.php

334 lines 9.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3 /**
4 * Cron endpoint class.
5 *
6 * GET /sites/%s/cron
7 */
8 class Jetpack_JSON_API_Cron_Endpoint extends Jetpack_JSON_API_Endpoint {
9
10 /**
11 * Needed capabilities.
12 *
13 * @var string
14 */
15 protected $needed_capabilities = 'manage_options';
16
17 /**
18 * Validate the call.
19 *
20 * @param int $_blog_id - the blog ID.
21 * @param array $capability - the capabilities of the user.
22 * @param bool $check_manage_active - parameter is for making the method signature compatible with its parent class method.
23 */
24 protected function validate_call( $_blog_id, $capability, $check_manage_active = true ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
25 return parent::validate_call( $_blog_id, $capability, false );
26 }
27
28 /**
29 * Return the result of current timestamp.
30 */
31 protected function result() {
32 return array(
33 'cron_array' => _get_cron_array(),
34 'current_timestamp' => time(),
35 );
36 }
37
38 /**
39 * Sanitize the hook.
40 *
41 * @param string $hook - the hook.
42 *
43 * @return string
44 */
45 protected function sanitize_hook( $hook ) {
46 return preg_replace( '/[^A-Za-z0-9-_]/', '', $hook );
47 }
48
49 /**
50 * Resolve arguments.
51 *
52 * @return array
53 */
54 protected function resolve_arguments() {
55 $args = $this->input();
56 return isset( $args['arguments'] ) ? json_decode( $args['arguments'] ) : array();
57 }
58
59 /**
60 * Check the cron lock.
61 *
62 * @param float $gmt_time - the time in GMT.
63 *
64 * @return string|int|WP_Error WP_Error if cron was locked in the `WP_CRON_LOCK_TIMEOUT` seconds before `gmt_time`, int or string otherwise.
65 */
66 protected function is_cron_locked( $gmt_time ) {
67 // The cron lock: a unix timestamp from when the cron was spawned.
68 $doing_cron_transient = $this->get_cron_lock();
69 if ( $doing_cron_transient && ( $doing_cron_transient + WP_CRON_LOCK_TIMEOUT > $gmt_time ) ) {
70 return new WP_Error( 'cron-is-locked', 'Current there is a cron already happening.', 403 );
71 }
72 return $doing_cron_transient;
73 }
74
75 /**
76 * Check if we can unlock the cron transient.
77 *
78 * @param string $doing_wp_cron - if we're doing the wp_cron.
79 */
80 protected function maybe_unlock_cron( $doing_wp_cron ) {
81 if ( $this->get_cron_lock() === $doing_wp_cron ) {
82 delete_transient( 'doing_cron' );
83 }
84 }
85
86 /**
87 * Set the cron lock.
88 *
89 * @return string
90 */
91 protected function lock_cron() {
92 $lock = sprintf( '%.22F', microtime( true ) );
93 set_transient( 'doing_cron', $lock );
94 return $lock;
95 }
96
97 /**
98 * Get scheduled.
99 *
100 * @param string $hook - the hook.
101 * @param array $args - the arguments.
102 *
103 * @return array
104 */
105 protected function get_schedules( $hook, $args ) {
106 $crons = _get_cron_array();
107 $key = md5( serialize( $args ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
108 if ( empty( $crons ) ) {
109 return array();
110 }
111 $found = array();
112 foreach ( $crons as $timestamp => $cron ) {
113 if ( isset( $cron[ $hook ][ $key ] ) ) {
114 $found[] = $timestamp;
115 }
116 }
117
118 return $found;
119 }
120
121 /**
122 * This function is based on the one found in wp-cron.php with a similar name
123 *
124 * @return int
125 */
126 protected function get_cron_lock() {
127 global $wpdb;
128
129 $value = 0;
130 if ( wp_using_ext_object_cache() ) {
131 /*
132 * Skip local cache and force re-fetch of doing_cron transient
133 * in case another process updated the cache.
134 */
135 $value = wp_cache_get( 'doing_cron', 'transient', true );
136 } else {
137 $row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", '_transient_doing_cron' ) );
138 if ( is_object( $row ) ) {
139 $value = $row->option_value;
140 }
141 }
142 return $value;
143 }
144 }
145
146 /**
147 * Cron post endpoint class.
148 *
149 * POST /sites/%s/cron
150 */
151 class Jetpack_JSON_API_Cron_Post_Endpoint extends Jetpack_JSON_API_Cron_Endpoint { // phpcs:ignore Generic.Files.OneObjectStructurePerFile.MultipleFound, Generic.Classes.OpeningBraceSameLine.ContentAfterBrace
152
153 /**
154 * The result.
155 *
156 * @return array|WP_Error
157 */
158 protected function result() {
159 define( 'DOING_CRON', true );
160 set_time_limit( 0 );
161 $args = $this->input();
162 $crons = _get_cron_array();
163 if ( false === $crons ) {
164 return new WP_Error( 'no-cron-event', 'Currently there are no cron events', 400 );
165 }
166
167 $timestamps_to_run = array_keys( $crons );
168 $gmt_time = microtime( true );
169
170 if ( isset( $timestamps_to_run[0] ) && $timestamps_to_run[0] > $gmt_time ) {
171 return new WP_Error( 'no-cron-event', 'Currently there are no cron events ready to be run', 400 );
172 }
173
174 $locked = $this->is_cron_locked( $gmt_time );
175 if ( is_wp_error( $locked ) ) {
176 return $locked;
177 }
178
179 $lock = $this->lock_cron();
180 $processed_events = array();
181
182 foreach ( $crons as $timestamp => $cronhooks ) {
183 if ( $timestamp > $gmt_time && ! isset( $args['hook'] ) ) {
184 break;
185 }
186
187 foreach ( $cronhooks as $hook => $hook_data ) {
188 if ( isset( $args['hook'] ) && ! in_array( $hook, $args['hook'], true ) ) {
189 continue;
190 }
191
192 foreach ( $hook_data as $hook_item ) {
193
194 $schedule = $hook_item['schedule'];
195 $arguments = $hook_item['args'];
196
197 if ( ! $schedule ) {
198 wp_reschedule_event( $timestamp, $schedule, $hook, $arguments );
199 }
200
201 wp_unschedule_event( $timestamp, $hook, $arguments );
202
203 do_action_ref_array( $hook, $arguments );
204 $processed_events[] = array( $hook => $arguments );
205
206 // If the hook ran too long and another cron process stole the lock,
207 // or if we things are taking longer then 20 seconds then quit.
208 if ( ( $this->get_cron_lock() !== $lock ) || ( $gmt_time + 20 > microtime( true ) ) ) {
209 $this->maybe_unlock_cron( $lock );
210 return array( 'success' => $processed_events );
211 }
212 }
213 }
214 }
215
216 $this->maybe_unlock_cron( $lock );
217 return array( 'success' => $processed_events );
218 }
219 }
220
221 /**
222 * Schedule endpoint class.
223 *
224 * POST /sites/%s/cron/schedule
225 */
226 class Jetpack_JSON_API_Cron_Schedule_Endpoint extends Jetpack_JSON_API_Cron_Endpoint { // phpcs:ignore Generic.Files.OneObjectStructurePerFile.MultipleFound, Generic.Classes.OpeningBraceSameLine.ContentAfterBrace
227
228 /**
229 * The result.
230 *
231 * @return array|WP_Error
232 */
233 protected function result() {
234 $args = $this->input();
235 if ( ! isset( $args['timestamp'] ) ) {
236 return new WP_Error( 'missing_argument', 'Please provide the timestamp argument', 400 );
237 }
238
239 if ( ! is_int( $args['timestamp'] ) || $args['timestamp'] < time() ) {
240 return new WP_Error( 'timestamp-invalid', 'Please provide timestamp that is an integer and set in the future', 400 );
241 }
242
243 if ( ! isset( $args['hook'] ) ) {
244 return new WP_Error( 'missing_argument', 'Please provide the hook argument', 400 );
245 }
246
247 $hook = $this->sanitize_hook( $args['hook'] );
248
249 $locked = $this->is_cron_locked( microtime( true ) );
250 if ( is_wp_error( $locked ) ) {
251 return $locked;
252 }
253
254 $arguments = $this->resolve_arguments();
255 $next_scheduled = $this->get_schedules( $hook, $arguments );
256
257 if ( isset( $args['recurrence'] ) ) {
258 $schedules = wp_get_schedules();
259 if ( ! isset( $schedules[ $args['recurrence'] ] ) ) {
260 return new WP_Error( 'invalid-recurrence', 'Please provide a valid recurrence argument', 400 );
261 }
262
263 if ( count( $next_scheduled ) > 0 ) {
264 return new WP_Error( 'event-already-scheduled', 'This event is ready scheduled', 400 );
265 }
266 $lock = $this->lock_cron();
267 wp_schedule_event( $args['timestamp'], $args['recurrence'], $hook, $arguments );
268 $this->maybe_unlock_cron( $lock );
269 return array( 'success' => true );
270 }
271
272 foreach ( $next_scheduled as $scheduled_time ) {
273 if ( abs( $scheduled_time - $args['timestamp'] ) <= 10 * MINUTE_IN_SECONDS ) {
274 return new WP_Error( 'event-already-scheduled', 'This event is ready scheduled', 400 );
275 }
276 }
277 $lock = $this->lock_cron();
278 $next = wp_schedule_single_event( $args['timestamp'], $hook, $arguments );
279 $this->maybe_unlock_cron( $lock );
280 return array( 'success' => $next );
281 }
282 }
283
284 /**
285 * The cron unschedule ednpoint class.
286 *
287 * POST /sites/%s/cron/unschedule
288 */
289 class Jetpack_JSON_API_Cron_Unschedule_Endpoint extends Jetpack_JSON_API_Cron_Endpoint { // phpcs:ignore Generic.Files.OneObjectStructurePerFile.MultipleFound, Generic.Classes.OpeningBraceSameLine.ContentAfterBrace
290
291 /**
292 * The result.
293 *
294 * @return array|WP_Error
295 */
296 protected function result() {
297 $args = $this->input();
298
299 if ( ! isset( $args['hook'] ) ) {
300 return new WP_Error( 'missing_argument', 'Please provide the hook argument', 400 );
301 }
302
303 $hook = $this->sanitize_hook( $args['hook'] );
304
305 $locked = $this->is_cron_locked( microtime( true ) );
306 if ( is_wp_error( $locked ) ) {
307 return $locked;
308 }
309
310 $crons = _get_cron_array();
311 if ( empty( $crons ) ) {
312 return new WP_Error( 'cron-not-present', 'Unable to unschedule an event, no events in the cron', 400 );
313 }
314
315 $arguments = $this->resolve_arguments();
316
317 if ( isset( $args['timestamp'] ) ) {
318 $next_schedulded = $this->get_schedules( $hook, $arguments );
319 if ( in_array( $args['timestamp'], $next_schedulded, true ) ) {
320 return new WP_Error( 'event-not-present', 'Unable to unschedule the event, the event doesn\'t exist', 400 );
321 }
322
323 $lock = $this->lock_cron();
324 wp_unschedule_event( $args['timestamp'], $hook, $arguments );
325 $this->maybe_unlock_cron( $lock );
326 return array( 'success' => true );
327 }
328 $lock = $this->lock_cron();
329 wp_clear_scheduled_hook( $hook, $arguments );
330 $this->maybe_unlock_cron( $lock );
331 return array( 'success' => true );
332 }
333 }
334