Completed
Push — master ( ae3f91...c703ec )
by Juliette
02:12
created

ZT_Debug_Bar_Cron::get_crons()   C

Complexity

Conditions 7
Paths 6

Size

Total Lines 46
Code Lines 30

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 46
rs 6.7273
cc 7
eloc 30
nc 6
nop 0
1
<?php
2
/**
3
 * Debug Bar Cron - Debug Bar Panel.
4
 *
5
 * @package     WordPress\Plugins\Debug Bar Cron
6
 * @author      Zack Tollman, Helen Hou-Sandi, Juliette Reinders Folmer
7
 * @link        https://github.com/tollmanz/debug-bar-cron
8
 * @version     0.1.2
9
 * @license     http://creativecommons.org/licenses/GPL/2.0/ GNU General Public License, version 2 or higher
10
 */
11
12
// Avoid direct calls to this file.
13
if ( ! function_exists( 'add_action' ) ) {
14
	header( 'Status: 403 Forbidden' );
15
	header( 'HTTP/1.1 403 Forbidden' );
16
	exit();
17
}
18
19
/**
20
 * The class in this file extends the functionality provided by the parent plugin "Debug Bar".
21
 */
22
if ( ! class_exists( 'ZT_Debug_Bar_Cron' ) && class_exists( 'Debug_Bar_Panel' ) ) {
23
24
	/**
25
	 * Add a new Debug Bar Panel.
26
	 */
27
	class ZT_Debug_Bar_Cron extends Debug_Bar_Panel {
28
29
		const DBCRON_STYLES_VERSION = '1.0';
30
31
		const DBCRON_NAME = 'debug-bar-cron';
32
33
		/**
34
		 * Holds all of the cron events.
35
		 *
36
		 * @var array
37
		 */
38
		private $_crons;
39
40
		/**
41
		 * Holds only the cron events initiated by WP core.
42
		 *
43
		 * @var array
44
		 */
45
		private $_core_crons;
46
47
		/**
48
		 * Holds the cron events created by plugins or themes.
49
		 *
50
		 * @var array
51
		 */
52
		private $_user_crons;
53
54
		/**
55
		 * Total number of cron events.
56
		 *
57
		 * @var int
58
		 */
59
		private $_total_crons = 0;
60
61
		/**
62
		 * Total number of cron events created by plugins and themes.
63
		 *
64
		 * @var int
65
		 */
66
		private $_total_user_crons = 0;
67
68
		/**
69
		 * Total number of WP core cron events.
70
		 *
71
		 * @var int
72
		 */
73
		private $_total_core_crons = 0;
74
75
		/**
76
		 * Whether cron is being executed or not.
77
		 *
78
		 * @var string
79
		 */
80
		private $_doing_cron = 'No';
81
82
83
		/**
84
		 * Give the panel a title and set the enqueues.
85
		 *
86
		 * @return void
87
		 */
88
		public function init() {
89
			load_plugin_textdomain( 'zt-debug-bar-cron', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
90
			$this->title( __( 'Cron', 'zt-debug-bar-cron' ) );
91
			add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts_styles' ) );
92
			add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts_styles' ) );
93
		}
94
95
96
		/**
97
		 * Enqueue styles.
98
		 *
99
		 * @return void
100
		 */
101
		public function enqueue_scripts_styles() {
102
			$suffix = ( ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min' );
103
104
			wp_enqueue_style(
105
				self::DBCRON_NAME,
106
				plugins_url( 'css/' . self::DBCRON_NAME . $suffix . '.css', __FILE__ ),
107
				array( 'debug-bar' ),
108
				self::DBCRON_STYLES_VERSION
109
			);
110
		}
111
112
113
		/**
114
		 * Show the menu item in Debug Bar.
115
		 *
116
		 * @return void
117
		 */
118
		public function prerender() {
119
			$this->set_visible( true );
120
		}
121
122
123
		/**
124
		 * Show the contents of the page.
125
		 *
126
		 * @return void
127
		 */
128
		public function render() {
129
			$this->get_crons();
130
131
			$this->_doing_cron = get_transient( 'doing_cron' ) ? __( 'Yes', 'zt-debug-bar-cron' ) : __( 'No', 'zt-debug-bar-cron' );
132
133
			// Get the time of the next event.
134
			$cron_times          = ( is_array( $this->_crons ) ? array_keys( $this->_crons ) : array() );
135
			$unix_time_next_cron = (int) $cron_times[0];
136
			$time_next_cron      = date( 'Y-m-d H:i:s', $unix_time_next_cron );
137
138
			$human_time_next_cron = human_time_diff( $unix_time_next_cron );
139
140
			// Add a class if past current time and doing cron is not running.
141
			$times_class = $this->is_time_in_past( $unix_time_next_cron ) ? ' past' : '';
142
143
			$this->load_debug_bar_pretty_output();
144
145
			echo // WPCS: XSS ok.
146
			'
147
			<div class="debug-bar-cron">
148
				<h2><span>', esc_html__( 'Total Events', 'zt-debug-bar-cron' ), ':</span>', $this->_total_crons, '</h2>
149
				<h2><span>', esc_html__( 'Core Events', 'zt-debug-bar-cron' ), ':</span>', $this->_total_core_crons, '</h2>
150
				<h2><span>', esc_html__( 'Custom Events', 'zt-debug-bar-cron' ), ':</span>', $this->_total_user_crons, '</h2>
151
				<h2><span>', esc_html__( 'Doing Cron', 'zt-debug-bar-cron' ), ':</span>', esc_html( $this->_doing_cron ), '</h2>
152
				<h2 class="times', esc_attr( $times_class ), '"><span>', esc_html__( 'Next Event', 'zt-debug-bar-cron' ), ':</span>
153
					', esc_html( $time_next_cron ), '<br />
154
					', $unix_time_next_cron, '<br />
155
					', esc_html( $this->display_past_time( $human_time_next_cron, $unix_time_next_cron ) ), '
156
				</h2>
157
				<h2><span>', esc_html__( 'Current Time', 'zt-debug-bar-cron' ), ':</span>', esc_html( date( 'H:i:s' ) ), '</h2>
158
159
				<div class="clear"></div>
160
161
				<h3>', esc_html__( 'Schedules', 'zt-debug-bar-cron' ), '</h3>';
162
163
			$this->display_schedules();
164
165
			echo '
166
				<h3>', esc_html__( 'Custom Events', 'zt-debug-bar-cron' ), '</h3>';
167
168
			$this->display_events( $this->_user_crons, __( 'No Custom Events scheduled.', 'zt-debug-bar-cron' ) );
169
170
			echo '
171
				<h3>', esc_html__( 'Core Events', 'zt-debug-bar-cron' ), '</h3>';
172
173
			$this->display_events( $this->_core_crons, __( 'No Core Events scheduled.', 'zt-debug-bar-cron' ) );
174
175
			echo '
176
			</div>';
177
178
			$this->reset_debug_bar_pretty_output();
179
		}
180
181
182
		/**
183
		 * Gets all of the cron jobs.
184
		 *
185
		 * This function sorts the cron jobs into core crons, and custom crons. It also tallies
186
		 * a total count for the crons as this number is otherwise tough to get.
187
		 *
188
		 * @return array|null Array of crons.
189
		 */
190
		private function get_crons() {
191
			if ( is_array( $this->_crons ) ) {
192
				return $this->_crons;
193
			}
194
195
			$crons = _get_cron_array();
196
			if ( ! is_array( $crons ) || array() === $crons  ) {
197
				return $this->_crons;
198
			}
199
200
			$this->_crons = $crons;
201
202
			// Lists all crons that are defined in WP Core.
203
			// @internal To find all, search WP trunk for `wp_schedule_(single_)?event`.
204
			$core_cron_hooks = array(
205
				'do_pings',
206
				'importer_scheduled_cleanup',     // WP 3.1+.
207
				'publish_future_post',
208
				'update_network_counts',          // WP 3.1+.
209
				'upgrader_scheduled_cleanup',     // WP 3.3+.
210
				'wp_maybe_auto_update',           // WP 3.7+.
211
				'wp_scheduled_auto_draft_delete', // WP 3.4+.
212
				'wp_scheduled_delete',            // WP 2.9+.
213
				'wp_split_shared_term_batch',     // WP 4.3+.
214
				'wp_update_plugins',
215
				'wp_update_themes',
216
				'wp_version_check',
217
			);
218
219
			// Sort and count crons.
220
			foreach ( $this->_crons as $time => $time_cron_array ) {
221
				foreach ( $time_cron_array as $hook => $data ) {
222
					$this->_total_crons += count( $data );
223
224
					if ( in_array( $hook, $core_cron_hooks, true ) ) {
225
						$this->_core_crons[ $time ][ $hook ] = $data;
226
						$this->_total_core_crons            += count( $data );
227
					} else {
228
						$this->_user_crons[ $time ][ $hook ] = $data;
229
						$this->_total_user_crons            += count( $data );
230
					}
231
				}
232
			}
233
234
			return $this->_crons;
235
		}
236
237
238
		/**
239
		 * Displays the events in an easy to read table.
240
		 *
241
		 * @param array  $events        Array of events.
242
		 * @param string $no_events_msg Message to display if there are no events.
243
		 */
244
		private function display_events( $events, $no_events_msg ) {
245
			// Exit early if no events found.
246
			if ( ! is_array( $events ) || empty( $events ) ) {
247
				echo '
248
				<p>', esc_html( $no_events_msg ), '</p>';
249
				return;
250
			}
251
252
			echo '
253
				<table class="zt-debug-bar-cron-table zt-debug-bar-cron-event-table">
254
					<thead><tr>
255
						<th class="col1">', esc_html__( 'Next Execution', 'zt-debug-bar-cron' ), '</th>
256
						<th class="col2">', esc_html__( 'Hook', 'zt-debug-bar-cron' ), '</th>
257
						<th class="col3">', esc_html__( 'Interval Hook', 'zt-debug-bar-cron' ), '</th>
258
						<th class="col4">', esc_html__( 'Interval Value', 'zt-debug-bar-cron' ), '</th>
259
						<th class="col5">', esc_html__( 'Args', 'zt-debug-bar-cron' ), '</th>
260
					</tr></thead>
261
					<tbody>';
262
263
			foreach ( $events as $time => $time_cron_array ) {
264
				$time = (int) $time;
265
				foreach ( $time_cron_array as $hook => $data ) {
266
					foreach ( $data as $hash => $info ) {
267
						echo '
268
						<tr>
269
							<td';
270
271
						// Add a class if past current time.
272
						if ( $this->is_time_in_past( $time ) ) {
273
							echo ' class="past"';
274
						}
275
276
						echo // WPCS: XSS ok.
277
						'>
278
								', date( 'Y-m-d H:i:s', $time ), '<br />
279
								', $time, '<br />
280
								', esc_html( $this->display_past_time( human_time_diff( $time ), $time ) ), '
281
							</td>
282
							<td>', esc_html( $hook ), '</td>';
283
284
285
						// Report the schedule.
286
						echo '
287
							<td>';
288
						if ( $info['schedule'] ) {
289
							echo esc_html( $info['schedule'] );
290
						} else {
291
							echo esc_html__( 'Single Event', 'zt-debug-bar-cron' );
292
						}
293
						echo '</td>';
294
295
						// Report the interval.
296
						echo '
297
							<td>';
298
						if ( isset( $info['interval'] ) ) {
299
							$interval = (int) $info['interval'];
300
							/* TRANSLATORS: %s is number of seconds. */
301
							printf( esc_html__( '%ss', 'zt-debug-bar-cron' ) . '<br />', $interval ); // WPCS: XSS ok.
302
							/* TRANSLATORS: %s is number of minutes. */
303
							printf( esc_html__( '%sm', 'zt-debug-bar-cron' ) . '<br />', $this->get_minutes( $interval ) ); // WPCS: XSS ok.
304
							/* TRANSLATORS: %s is number of hours. */
305
							printf( esc_html__( '%sh', 'zt-debug-bar-cron' ), $this->get_hours( $interval ) ); // WPCS: XSS ok.
306
						} else {
307
							echo esc_html__( 'Single Event', 'zt-debug-bar-cron' );
308
						}
309
						echo '</td>';
310
311
						// Report the args.
312
						echo '
313
							<td>';
314
						$this->display_cron_arguments( $info['args'] );
315
						echo '</td>
316
						</tr>';
317
					}
318
				}
319
			}
320
321
			echo '
322
					</tbody>
323
				</table>';
324
		}
325
326
327
		/**
328
		 * Displays the cron arguments in a readable format.
329
		 *
330
		 * @param mixed $args Cron argument(s).
331
		 *
332
		 * @return void
333
		 */
334
		private function display_cron_arguments( $args ) {
335
			// Arguments defaults to an empty array if no arguments are given.
336
			if ( is_array( $args ) && array() === $args ) {
337
				echo esc_html__( 'No Args', 'zt-debug-bar-cron' );
338
				return;
339
			}
340
341
			// Ok, we have an argument, let's pretty print it.
342
			$this->print_pretty_output( $args );
343
		}
344
345
346
		/**
347
		 * Displays all of the schedules defined.
348
		 *
349
		 * @return void
350
		 */
351
		private function display_schedules() {
352
			echo '
353
				<table class="zt-debug-bar-cron-table zt-debug-bar-cron-schedule-table">
354
					<thead><tr>
355
						<th class="col1">', esc_html__( 'Interval Hook', 'zt-debug-bar-cron' ), '</th>
356
						<th class="col2">', esc_html__( 'Interval (S)', 'zt-debug-bar-cron' ), '</th>
357
						<th class="col3">', esc_html__( 'Interval (M)', 'zt-debug-bar-cron' ), '</th>
358
						<th class="col4">', esc_html__( 'Interval (H)', 'zt-debug-bar-cron' ), '</th>
359
						<th class="col5">', esc_html__( 'Display Name', 'zt-debug-bar-cron' ), '</th>
360
					</tr></thead>
361
					<tbody>';
362
363
364
			$schedules = wp_get_schedules();
365
			ksort( $schedules );
366
			uasort( $schedules, array( $this, 'schedules_sorting' ) );
367
			foreach ( $schedules as $interval_hook => $data ) {
368
				$interval = (int) $data['interval'];
369
				echo // WPCS: XSS ok.
370
				'
371
						<tr>
372
							<td>', esc_html( $interval_hook ), '</td>
373
							<td>', $interval, '</td>
374
							<td>', $this->get_minutes( $interval ), '</td>
375
							<td>', $this->get_hours( $interval ), '</td>
376
							<td>', esc_html( $data['display'] ) . '</td>
377
						</tr>';
378
			}
379
380
			echo '
381
					</tbody>
382
				</table>';
383
		}
384
385
		/**
386
		 * Sorting method for cron scheldules. Order by schedules interval.
387
		 *
388
		 * @param array $a First element of comparison pair.
389
		 * @param array $b Second element of comparison pair.
390
		 *
391
		 * @return int Return 1 if $a argument 'interval' greater then $b argument 'interval', 0 if both intervals equivalent and -1 otherwise.
392
		 */
393
		function schedules_sorting( $a, $b ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
394
			if ( (int) $a['interval'] === (int) $b['interval'] ) {
395
				return 0;
396
			} else {
397
				return ( ( (int) $a['interval'] > (int) $b['interval'] ) ? 1 : -1 );
398
			}
399
		}
400
401
		/**
402
		 * Verify if a given timestamp is in the past or the future.
403
		 *
404
		 * @param int $time Unix timestamp.
405
		 *
406
		 * @return bool True if the time has passed, false otherwise.
407
		 */
408
		private function is_time_in_past( $time ) {
409
			return ( time() > $time && 'No' === $this->_doing_cron );
410
		}
411
412
413
		/**
414
		 * Transform a time in seconds to minutes rounded to 2 decimals.
415
		 *
416
		 * @param int $time Unix timestamp.
417
		 *
418
		 * @return int|float
419
		 */
420
		private function get_minutes( $time ) {
421
			return round( ( (int) $time / 60 ), 2 );
422
		}
423
424
425
		/**
426
		 * Transform a time in seconds to hours rounded to 2 decimals.
427
		 *
428
		 * @param int $time Unix timestamp.
429
		 *
430
		 * @return int|float
431
		 */
432
		private function get_hours( $time ) {
433
			return round( ( (int) $time / 3600 ), 2 );
434
		}
435
436
437
		/**
438
		 * Compares time with current time and adds ' ago' if current time is greater than event time.
439
		 *
440
		 * @param string $human_time Human readable time difference.
441
		 * @param int    $time       Unix time of event.
442
		 *
443
		 * @return string
444
		 */
445
		private function display_past_time( $human_time, $time ) {
446
			if ( time() > $time ) {
447
				/* TRANSLATORS: %s is a human readable time difference. */
448
				return sprintf( __( '%s ago', 'zt-debug-bar-cron' ), $human_time );
449
			} else {
450
				return $human_time;
451
			}
452
		}
453
454
455
		/**
456
		 * Load the pretty output class & set the recursion limit.
457
		 */
458
		private function load_debug_bar_pretty_output() {
459
			if ( ! class_exists( 'Debug_Bar_Pretty_Output' ) ) {
460
				require_once plugin_dir_path( __FILE__ ) . 'inc/debug-bar-pretty-output/class-debug-bar-pretty-output.php';
461
			}
462
463
			// Limit recursion depth if possible - method available since DBPO v1.4.
464
			if ( method_exists( 'Debug_Bar_Pretty_Output', 'limit_recursion' ) ) {
465
				Debug_Bar_Pretty_Output::limit_recursion( 2 );
466
			}
467
		}
468
469
470
		/**
471
		 * Print any type of variable with colour coding and a variable type indication.
472
		 *
473
		 * @param mixed $variable The variable to print.
474
		 */
475
		private function print_pretty_output( $variable ) {
476
			if ( defined( 'Debug_Bar_Pretty_Output::VERSION' ) ) {
477
				echo Debug_Bar_Pretty_Output::get_output( $variable, '', true ); // WPCS: XSS ok.
478
			} else {
479
				// An old version of the pretty output class was loaded.
480
				// Real possibility as there are several DB plugins using the pretty print class.
481
				Debug_Bar_Pretty_Output::output( $variable, '', true );
482
			}
483
		}
484
485
486
		/**
487
		 * Unset recursion depth limit for the pretty output class.
488
		 *
489
		 * @internal Method available since DBPO v1.4.
490
		 */
491
		private function reset_debug_bar_pretty_output() {
492
			if ( method_exists( 'Debug_Bar_Pretty_Output', 'unset_recursion_limit' ) ) {
493
				Debug_Bar_Pretty_Output::unset_recursion_limit();
494
			}
495
		}
496
	} // End of class ZT_Debug_Bar_Cron.
497
498
} // End of if class_exists wrapper.
499