Completed
Push — master ( fbe1b5...a75a1a )
by Juliette
02:11
created

ZT_Debug_Bar_Cron::print_pretty_output()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 9
rs 9.6667
cc 2
eloc 5
nc 2
nop 1
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-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 />', ( $interval / 60 ) ); // WPCS: XSS ok.
304
							/* TRANSLATORS: %s is number of hours. */
305
							printf( esc_html__( '%sh', 'zt-debug-bar-cron' ), ( $interval / ( 60 * 60 ) ) ); // 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-event-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
			foreach ( $schedules as $interval_hook => $data ) {
366
				$interval = (int) $data['interval'];
367
				echo  // WPCS: XSS ok.
368
				'
369
						<tr>
370
							<td>', esc_html( $interval_hook ), '</td>
371
							<td>', $interval, '</td>
372
							<td>', ( $interval / 60 ), '</td>
373
							<td>', ( $interval / ( 60 * 60 ) ), '</td>
374
							<td>', esc_html( $data['display'] ) . '</td>
375
						</tr>';
376
			}
377
378
			echo '
379
					</tbody>
380
				</table>';
381
		}
382
383
384
		/**
385
		 * Verify if a given timestamp is in the past or the future.
386
		 *
387
		 * @param int $time Timestamp.
388
		 *
389
		 * @return bool True if the time has passed, false otherwise.
390
		 */
391
		private function is_time_in_past( $time ) {
392
			return ( time() > $time && 'No' === $this->_doing_cron );
393
		}
394
395
396
		/**
397
		 * Compares time with current time and adds ' ago' if current time is greater than event time.
398
		 *
399
		 * @param string $human_time Human readable time difference.
400
		 * @param int    $time       Unix time of event.
401
		 *
402
		 * @return string
403
		 */
404
		private function display_past_time( $human_time, $time ) {
405
			if ( time() > $time ) {
406
				/* TRANSLATORS: %s is a human readable time difference. */
407
				return sprintf( __( '%s ago', 'zt-debug-bar-cron' ), $human_time );
408
			} else {
409
				return $human_time;
410
			}
411
		}
412
413
414
		/**
415
		 * Load the pretty output class & set the recursion limit.
416
		 */
417
		private function load_debug_bar_pretty_output() {
418
			if ( ! class_exists( 'Debug_Bar_Pretty_Output' ) ) {
419
				require_once plugin_dir_path( __FILE__ ) . 'inc/debug-bar-pretty-output/class-debug-bar-pretty-output.php';
420
			}
421
422
			// Limit recursion depth if possible - method available since DBPO v1.4.
423
			if ( method_exists( 'Debug_Bar_Pretty_Output', 'limit_recursion' ) ) {
424
				Debug_Bar_Pretty_Output::limit_recursion( 2 );
425
			}
426
		}
427
428
429
		/**
430
		 * Print any type of variable with colour coding and a variable type indication.
431
		 *
432
		 * @param mixed $variable The variable to print.
433
		 */
434
		private function print_pretty_output( $variable ) {
435
			if ( defined( 'Debug_Bar_Pretty_Output::VERSION' ) ) {
436
				echo Debug_Bar_Pretty_Output::get_output( $variable, '', true ); // WPCS: XSS ok.
437
			} else {
438
				// An old version of the pretty output class was loaded.
439
				// Real possibility as there are several DB plugins using the pretty print class.
440
				Debug_Bar_Pretty_Output::output( $variable, '', true );
441
			}
442
		}
443
444
445
		/**
446
		 * Unset recursion depth limit for the pretty output class.
447
		 *
448
		 * @internal Method available since DBPO v1.4.
449
		 */
450
		private function reset_debug_bar_pretty_output() {
451
			if ( method_exists( 'Debug_Bar_Pretty_Output', 'unset_recursion_limit' ) ) {
452
				Debug_Bar_Pretty_Output::unset_recursion_limit();
453
			}
454
		}
455
	} // End of class ZT_Debug_Bar_Cron.
456
457
} // End of if class_exists wrapper.
458