Completed
Push — add/carousel-lightbox-single-i... ( 204ac6...43c884 )
by
unknown
09:26
created

Jetpack_JSON_API_Cron_Endpoint::result()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
// GET /sites/%s/cron
4
class Jetpack_JSON_API_Cron_Endpoint extends Jetpack_JSON_API_Endpoint {
5
	protected $needed_capabilities = 'manage_options';
6
7
	protected function validate_call( $_blog_id, $capability, $check_manage_active = true ) {
8
		parent::validate_call( $_blog_id, $capability, false );
9
	}
10
11
	protected function result() {
12
		return array(
13
			'cron_array' => _get_cron_array(),
14
			'current_timestamp' => time()
15
		);
16
	}
17
18
	protected function sanitize_hook( $hook ) {
19
		return preg_replace( '/[^A-Za-z0-9-_]/', '', $hook );
20
	}
21
22
	protected function resolve_arguments() {
23
		$args = $this->input();
24
		return  isset( $args['arguments'] ) ? json_decode( $args['arguments'] ) : array();
25
	}
26
27
	protected function is_cron_locked( $gmt_time ) {
28
		// The cron lock: a unix timestamp from when the cron was spawned.
29
		$doing_cron_transient = $this->get_cron_lock();
30
		if ( $doing_cron_transient && ( $doing_cron_transient + WP_CRON_LOCK_TIMEOUT > $gmt_time )  ) {
31
			return new WP_Error( 'cron-is-locked', 'Current there is a cron already happening.', 403 );
32
		}
33
		return $doing_cron_transient;
34
	}
35
36
	protected function maybe_unlock_cron( $doing_wp_cron ) {
37
		if ( $this->get_cron_lock() == $doing_wp_cron ) {
38
			delete_transient( 'doing_cron' );
39
		}
40
	}
41
42
	protected function lock_cron() {
43
		$lock = sprintf( '%.22F', microtime( true ) );
44
		set_transient( 'doing_cron', $lock );
45
		return $lock;
46
	}
47
48
	protected function get_schedules( $hook, $args ) {
49
		$crons = _get_cron_array();
50
		$key = md5(serialize($args));
51
		if ( empty( $crons ) )
52
			return array();
53
		$found = array();
54
		foreach ( $crons as $timestamp => $cron ) {
55
			if ( isset( $cron[$hook][$key] ) )
56
				$found[] = $timestamp;
57
		}
58
59
		return $found;
60
	}
61
62
	/**
63
	 * This function is based on the one found in wp-cron.php with a similar name
64
	 * @return int
65
	 */
66
	protected function get_cron_lock() {
67
		global $wpdb;
68
69
		$value = 0;
70
		if ( wp_using_ext_object_cache() ) {
71
			/*
72
			 * Skip local cache and force re-fetch of doing_cron transient
73
			 * in case another process updated the cache.
74
			 */
75
			$value = wp_cache_get( 'doing_cron', 'transient', true );
76
		} else {
77
			$row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", '_transient_doing_cron' ) );
78
			if ( is_object( $row ) ) {
79
				$value = $row->option_value;
80
			}
81
		}
82
		return $value;
83
	}
84
}
85
86
// POST /sites/%s/cron
87
class Jetpack_JSON_API_Cron_Post_Endpoint extends Jetpack_JSON_API_Cron_Endpoint {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
88
89
	protected function result() {
90
		define( 'DOING_CRON', true );
91
		set_time_limit( 0 );
92
		$args = $this->input();
93
94
		if ( false === $crons = _get_cron_array() ) {
95
			return new WP_Error( 'no-cron-event', 'Currently there are no cron events', 400 );
96
		}
97
98
		$timestamps_to_run = array_keys( $crons );
99
		$gmt_time = microtime( true );
100
101
		if ( isset( $timestamps_to_run[0] ) && $timestamps_to_run[0] > $gmt_time ) {
102
			return new WP_Error( 'no-cron-event', 'Currently there are no cron events ready to be run', 400 );
103
		}
104
105
		$locked = $this->is_cron_locked( $gmt_time );
106
		if ( is_wp_error( $locked ) ) {
107
			return $locked;
108
		}
109
110
		$lock = $this->lock_cron();
111
		$processed_events = array();
112
113
		foreach ( $crons as $timestamp => $cronhooks ) {
114
			if ( $timestamp > $gmt_time && ! isset( $args[ 'hook' ] ) ) {
115
				break;
116
			}
117
118
			foreach ( $cronhooks as $hook => $hook_data ) {
119
				if ( isset( $args[ 'hook' ] ) && ! in_array( $hook, $args['hook'] ) ) {
120
					continue;
121
				}
122
123
				foreach ( $hook_data as $hash => $hook_item ) {
124
125
					$schedule = $hook_item['schedule'];
126
					$arguments = $hook_item['args'];
127
128
					if ( $schedule != false ) {
129
						wp_reschedule_event( $timestamp, $schedule, $hook, $arguments );
130
					}
131
132
					wp_unschedule_event( $timestamp, $hook, $arguments );
133
134
					do_action_ref_array( $hook, $arguments );
135
					$processed_events[] = array( $hook => $arguments );
136
137
					// If the hook ran too long and another cron process stole the lock,
138
					// or if we things are taking longer then 20 seconds then quit.
139
					if ( ( $this->get_cron_lock() != $lock ) || ( $gmt_time + 20 > microtime( true ) ) ) {
140
						$this->maybe_unlock_cron( $lock );
141
						return array( 'success' => $processed_events );
142
					}
143
144
				}
145
			}
146
		}
147
148
		$this->maybe_unlock_cron( $lock );
149
		return array( 'success' => $processed_events );
150
	}
151
}
152
153
// POST /sites/%s/cron/schedule
154
class Jetpack_JSON_API_Cron_Schedule_Endpoint extends Jetpack_JSON_API_Cron_Endpoint {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
155
156
	protected function result() {
157
		$args = $this->input();
158
		if ( ! isset( $args['timestamp'] ) ) {
159
			return new WP_Error( 'missing_argument', 'Please provide the timestamp argument', 400 );
160
		}
161
162
		if ( ! is_int( $args['timestamp'] ) || $args['timestamp'] < time() ) {
163
			return new WP_Error( 'timestamp-invalid', 'Please provide timestamp that is an integer and set in the future', 400 );
164
		}
165
166
		if ( ! isset( $args['hook'] ) ) {
167
			return new WP_Error( 'missing_argument', 'Please provide the hook argument', 400 );
168
		}
169
170
		$hook = $this->sanitize_hook( $args['hook'] );
171
172
		$locked = $this->is_cron_locked( microtime( true ) );
173
		if ( is_wp_error( $locked ) ) {
174
			return $locked;
175
		}
176
177
		$arguments = $this->resolve_arguments();
178
		$next_scheduled = $this->get_schedules( $hook, $arguments );
179
180
		if ( isset( $args['recurrence'] ) ) {
181
			$schedules = wp_get_schedules();
182
			if ( ! isset( $schedules[ $args['recurrence'] ] ) ) {
183
				return new WP_Error( 'invalid-recurrence', 'Please provide a valid recurrence argument', 400 );
184
			}
185
186
			if ( count( $next_scheduled ) > 0 ) {
187
				return new WP_Error( 'event-already-scheduled', 'This event is ready scheduled', 400 );
188
			}
189
			$lock = $this->lock_cron();
190
			wp_schedule_event( $args['timestamp'], $args['recurrence'], $hook, $arguments );
191
			$this->maybe_unlock_cron( $lock );
192
			return array( 'success' => true );
193
		}
194
195
		foreach( $next_scheduled as $scheduled_time ) {
196
			if ( abs( $scheduled_time - $args['timestamp'] ) <= 10 * MINUTE_IN_SECONDS ) {
197
				return new WP_Error( 'event-already-scheduled', 'This event is ready scheduled', 400 );
198
			}
199
		}
200
		$lock = $this->lock_cron();
201
		$next = wp_schedule_single_event( $args['timestamp'], $hook, $arguments );
202
		$this->maybe_unlock_cron( $lock );
203
		return array( 'success' => is_null( $next  ) ? true : false );
204
	}
205
}
206
207
// POST /sites/%s/cron/unschedule
208
class Jetpack_JSON_API_Cron_Unschedule_Endpoint extends Jetpack_JSON_API_Cron_Endpoint {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
209
210
	protected function result() {
211
		$args = $this->input();
212
213
		if ( !isset( $args['hook'] ) ) {
214
			return new WP_Error( 'missing_argument', 'Please provide the hook argument', 400 );
215
		}
216
217
		$hook = $this->sanitize_hook( $args['hook'] );
218
219
		$locked = $this->is_cron_locked( microtime( true ) );
220
		if ( is_wp_error( $locked ) ) {
221
			return $locked;
222
		}
223
224
		$crons = _get_cron_array();
225
		if ( empty( $crons ) ) {
226
			return new WP_Error( 'cron-not-present', 'Unable to unschedule an event, no events in the cron', 400 );
227
		}
228
229
		$arguments = $this->resolve_arguments();
230
231
		if ( isset( $args['timestamp'] ) ) {
232
			$next_schedulded = $this->get_schedules( $hook, $arguments );
233
			if ( in_array( $args['timestamp'], $next_schedulded ) ) {
234
				return new WP_Error( 'event-not-present', 'Unable to unschedule the event, the event doesn\'t exist', 400 );
235
			}
236
237
			$lock = $this->lock_cron();
238
			wp_unschedule_event( $args['timestamp'], $hook, $arguments );
239
			$this->maybe_unlock_cron( $lock );
240
			return array( 'success' => true );
241
		}
242
		$lock = $this->lock_cron();
243
		wp_clear_scheduled_hook( $hook, $arguments );
244
		$this->maybe_unlock_cron( $lock );
245
		return array( 'success' => true );
246
	}
247
}
248