Completed
Push — master ( e03636...f679a4 )
by Juliette
11:48
created

ZT_Debug_Bar_Cron::display_event_cron_arguments()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 10
rs 9.4286
cc 3
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
		 * Lists all crons that are defined in WP Core.
84
		 *
85
		 * @var array
86
		 *
87
		 * @internal To find all, search WP trunk for `wp_schedule_(single_)?event`.
88
		 */
89
		private $core_cron_hooks = array(
90
			'do_pings',
91
			'importer_scheduled_cleanup',     // WP 3.1+.
92
			'publish_future_post',
93
			'update_network_counts',          // WP 3.1+.
94
			'upgrader_scheduled_cleanup',     // WP 3.3+.
95
			'wp_maybe_auto_update',           // WP 3.7+.
96
			'wp_scheduled_auto_draft_delete', // WP 3.4+.
97
			'wp_scheduled_delete',            // WP 2.9+.
98
			'wp_split_shared_term_batch',     // WP 4.3+.
99
			'wp_update_plugins',
100
			'wp_update_themes',
101
			'wp_version_check',
102
		);
103
104
105
106
		/**
107
		 * Give the panel a title and set the enqueues.
108
		 *
109
		 * @return void
110
		 */
111
		public function init() {
112
			load_plugin_textdomain( 'zt-debug-bar-cron', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
113
			$this->title( __( 'Cron', 'zt-debug-bar-cron' ) );
114
			add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts_styles' ) );
115
			add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts_styles' ) );
116
		}
117
118
119
		/**
120
		 * Enqueue styles.
121
		 *
122
		 * @return void
123
		 */
124
		public function enqueue_scripts_styles() {
125
			$suffix = ( ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min' );
126
127
			wp_enqueue_style(
128
				self::DBCRON_NAME,
129
				plugins_url( 'css/' . self::DBCRON_NAME . $suffix . '.css', __FILE__ ),
130
				array( 'debug-bar' ),
131
				self::DBCRON_STYLES_VERSION
132
			);
133
		}
134
135
136
		/**
137
		 * Show the menu item in Debug Bar.
138
		 *
139
		 * @return void
140
		 */
141
		public function prerender() {
142
			$this->set_visible( true );
143
		}
144
145
146
		/**
147
		 * Show the contents of the page.
148
		 *
149
		 * @return void
150
		 */
151
		public function render() {
152
			$this->get_crons();
153
154
			$this->_doing_cron = get_transient( 'doing_cron' ) ? __( 'Yes', 'zt-debug-bar-cron' ) : __( 'No', 'zt-debug-bar-cron' );
155
156
			// Get the time of the next event.
157
			$cron_times          = ( is_array( $this->_crons ) ? array_keys( $this->_crons ) : array() );
158
			$unix_time_next_cron = (int) $cron_times[0];
159
			$time_next_cron      = date( 'Y-m-d H:i:s', $unix_time_next_cron );
160
161
			$human_time_next_cron = human_time_diff( $unix_time_next_cron );
162
163
			// Add a class if past current time and doing cron is not running.
164
			$times_class = $this->is_time_in_past( $unix_time_next_cron ) ? ' past' : '';
165
166
			$this->load_debug_bar_pretty_output();
167
168
			echo // WPCS: XSS ok.
169
			'
170
			<div class="debug-bar-cron">
171
				<h2><span>', esc_html__( 'Total Events', 'zt-debug-bar-cron' ), ':</span>', $this->_total_crons, '</h2>
172
				<h2><span>', esc_html__( 'Core Events', 'zt-debug-bar-cron' ), ':</span>', $this->_total_core_crons, '</h2>
173
				<h2><span>', esc_html__( 'Custom Events', 'zt-debug-bar-cron' ), ':</span>', $this->_total_user_crons, '</h2>
174
				<h2><span>', esc_html__( 'Doing Cron', 'zt-debug-bar-cron' ), ':</span>', esc_html( $this->_doing_cron ), '</h2>
175
				<h2 class="times', esc_attr( $times_class ), '"><span>', esc_html__( 'Next Event', 'zt-debug-bar-cron' ), ':</span>
176
					', esc_html( $time_next_cron ), '<br />
177
					', $unix_time_next_cron, '<br />
178
					', esc_html( $this->display_past_time( $human_time_next_cron, $unix_time_next_cron ) ), '
179
				</h2>
180
				<h2><span>', esc_html__( 'Current Time', 'zt-debug-bar-cron' ), ':</span>', esc_html( date( 'H:i:s' ) ), '</h2>
181
182
				<div class="clear"></div>
183
184
				<h3>', esc_html__( 'Schedules', 'zt-debug-bar-cron' ), '</h3>';
185
186
			$this->display_schedules();
187
188
			echo '
189
				<h3>', esc_html__( 'Custom Events', 'zt-debug-bar-cron' ), '</h3>';
190
191
			$this->display_events( $this->_user_crons, __( 'No Custom Events scheduled.', 'zt-debug-bar-cron' ) );
192
193
			echo '
194
				<h3>', esc_html__( 'Core Events', 'zt-debug-bar-cron' ), '</h3>';
195
196
			$this->display_events( $this->_core_crons, __( 'No Core Events scheduled.', 'zt-debug-bar-cron' ) );
197
198
			echo '
199
			</div>';
200
201
			$this->reset_debug_bar_pretty_output();
202
		}
203
204
205
		/**
206
		 * Gets all of the cron jobs.
207
		 *
208
		 * @return array|null Array of crons.
209
		 */
210
		private function get_crons() {
211
			if ( is_array( $this->_crons ) ) {
212
				return $this->_crons;
213
			}
214
215
			$crons = _get_cron_array();
216
			if ( is_array( $crons ) && ! empty( $crons ) ) {
217
				$this->_crons = $crons;
218
				$this->sort_count_crons();
219
			}
220
221
			return $this->_crons;
222
		}
223
224
225
		/**
226
		 * Sort and count crons.
227
		 *
228
		 * This function sorts the cron jobs into core crons, and custom crons. It also tallies
229
		 * a total count for the crons as this number is otherwise tough to get.
230
		 */
231
		private function sort_count_crons() {
232
			foreach ( $this->_crons as $time => $time_cron_array ) {
233
				foreach ( $time_cron_array as $hook => $data ) {
234
					$this->_total_crons += count( $data );
235
236
					if ( in_array( $hook, $this->core_cron_hooks, true ) ) {
237
						$this->_core_crons[ $time ][ $hook ] = $data;
238
						$this->_total_core_crons            += count( $data );
239
					} else {
240
						$this->_user_crons[ $time ][ $hook ] = $data;
241
						$this->_total_user_crons            += count( $data );
242
					}
243
				}
244
			}
245
		}
246
247
248
		/**
249
		 * Displays the events in an easy to read table.
250
		 *
251
		 * @param array  $events        Array of events.
252
		 * @param string $no_events_msg Message to display if there are no events.
253
		 */
254
		private function display_events( $events, $no_events_msg ) {
255
			// Exit early if no events found.
256
			if ( ! is_array( $events ) || empty( $events ) ) {
257
				echo '
258
				<p>', esc_html( $no_events_msg ), '</p>';
259
				return;
260
			}
261
262
			echo '
263
				<table class="zt-debug-bar-cron-table zt-debug-bar-cron-event-table">
264
					<thead><tr>
265
						<th class="col1">', esc_html__( 'Next Execution', 'zt-debug-bar-cron' ), '</th>
266
						<th class="col2">', esc_html__( 'Hook', 'zt-debug-bar-cron' ), '</th>
267
						<th class="col3">', esc_html__( 'Interval Hook', 'zt-debug-bar-cron' ), '</th>
268
						<th class="col4">', esc_html__( 'Interval Value', 'zt-debug-bar-cron' ), '</th>
269
						<th class="col5">', esc_html__( 'Args', 'zt-debug-bar-cron' ), '</th>
270
					</tr></thead>
271
					<tbody>';
272
273
			foreach ( $events as $time => $time_cron_array ) {
274
				$time        = (int) $time;
275
				$event_count = $this->get_arg_set_count( $time_cron_array );
276
				$show_time   = true;
277
278
				foreach ( $time_cron_array as $hook => $data ) {
279
					$row_attributes = $this->get_event_row_attributes( $time, $hook );
280
					$arg_set_count  = count( $data );
281
					$show_hook      = true;
282
283
					foreach ( $data as $hash => $info ) {
284
						echo // WPCS: xss ok.
285
						'
286
						<tr', $row_attributes, '>';
287
288
						if ( true === $show_time ) {
289
							$this->display_event_time( $time, $event_count );
290
							$show_time = false;
291
						}
292
293
						if ( true === $show_hook ) {
294
							$this->display_event_hook( $hook, $arg_set_count );
295
							$show_hook = false;
296
						}
297
298
						// Report the schedule.
299
						echo '
300
							<td>';
301
						$this->display_event_schedule( $info );
302
						echo '</td>';
303
304
						// Report the interval.
305
						echo '
306
							<td class="intervals">';
307
						$this->display_event_intervals( $info );
308
						echo '</td>';
309
310
						// Report the args.
311
						echo '
312
							<td>';
313
						$this->display_event_cron_arguments( $info['args'] );
314
						echo '</td>
315
						</tr>';
316
					}
317
					unset( $hash, $info );
318
				}
319
				unset( $hook, $data, $row_attributes, $arg_set_count, $show_hook );
320
			}
321
			unset( $time, $time_cron_array, $hook_count, $show_time );
322
323
			echo '
324
					</tbody>
325
				</table>';
326
		}
327
328
329
		/**
330
		 * Count the number of argument sets for a cron time.
331
		 *
332
		 * @param array $hook_array Array of hooks with argument sets.
333
		 *
334
		 * @return int
335
		 */
336
		private function get_arg_set_count( $hook_array ) {
337
			$count = 0;
338
			foreach ( $hook_array as $set ) {
339
				$count += count( $set );
340
			}
341
			return $count;
342
		}
343
344
345
		/**
346
		 * Create a HTML attribute string for an event row.
347
		 *
348
		 * @param int    $time Unix timestamp.
349
		 * @param string $hook Action hook for the cron job.
350
		 *
351
		 * @return string
352
		 */
353
		private function get_event_row_attributes( $time, $hook ) {
354
			$attributes = '';
355
			$classes    = array();
356
357
			// Add a class if past current time.
358
			if ( $this->is_time_in_past( $time ) ) {
359
				$classes[] = 'past';
360
			}
361
362
			// Verify if any events are hooked in.
363
			if ( false === has_action( $hook ) ) {
364
				/* TRANSLATORS: This text will display as a tooltip. %1$s will be replaced by a line break. */
365
				$attributes .= ' title="' . sprintf( esc_attr__( 'No actions are hooked into this event at this time.%1$sThe most likely reason for this is that a plugin or theme was de-activated or uninstalled and didn\'t clean up after itself.%1$sHowever, a number of plugins also use the best practice of lean loading and only hook in conditionally, so check carefully if you intend to remove this event.', 'zt-debug-bar-cron' ), "\n" ) . '"';
366
				$classes[]   = 'empty-event';
367
			}
368
369
			if ( ! empty( $classes ) ) {
370
				$attributes .= ' class="' . implode( ' ', $classes ) . '"';
371
			}
372
373
			return $attributes;
374
		}
375
376
377
		/**
378
		 * Display the timing for the event as a date, timestamp and human readable time difference.
379
		 *
380
		 * @param int $time        Timestamp.
381
		 * @param int $event_count Number of events running at this time.
382
		 */
383
		private function display_event_time( $time, $event_count ) {
384
			$row_span = ( $event_count > 1 ) ? ' rowspan="' . $event_count . '"' : '';
385
386
			echo // WPCS: xss ok.
387
			'
388
			<td' . $row_span . '>
389
				', date( 'Y-m-d H:i:s', $time ), '<br />
390
				', $time, '<br />
391
				', esc_html( $this->display_past_time( human_time_diff( $time ), $time ) ), '
392
			</td>';
393
		}
394
395
396
		/**
397
		 * Display the name of the cron job event hook.
398
		 *
399
		 * @param string $hook          Hook name.
400
		 * @param int    $arg_set_count Number of events running at this time and on this hook.
401
		 */
402
		private function display_event_hook( $hook, $arg_set_count ) {
403
			$row_span = ( $arg_set_count > 1 ) ? ' rowspan="' . $arg_set_count . '"' : '';
404
405
			echo // WPCS: xss ok.
406
			'
407
			<td' . $row_span . '>', esc_html( $hook ), '</td>';
408
		}
409
410
411
		/**
412
		 * Displays the the event schedule name for recurring events or else 'single event'.
413
		 *
414
		 * @param array $info Event info array.
415
		 */
416
		private function display_event_schedule( $info ) {
417
			if ( ! empty( $info['schedule'] ) ) {
418
				echo esc_html( $info['schedule'] );
419
			} else {
420
				echo esc_html__( 'Single Event', 'zt-debug-bar-cron' );
421
			}
422
		}
423
424
425
		/**
426
		 * Displays the event interval in seconds, minutes and hours.
427
		 *
428
		 * @param array $info Event info array.
429
		 */
430
		private function display_event_intervals( $info ) {
431
			if ( ! empty( $info['interval'] ) ) {
432
				$interval = (int) $info['interval'];
433
				/* TRANSLATORS: %s is number of seconds. */
434
				printf( esc_html__( '%ss', 'zt-debug-bar-cron' ) . '<br />', $interval ); // WPCS: XSS ok.
435
				/* TRANSLATORS: %s is number of minutes. */
436
				printf( esc_html__( '%sm', 'zt-debug-bar-cron' ) . '<br />', $this->get_minutes( $interval ) ); // WPCS: XSS ok.
437
				/* TRANSLATORS: %s is number of hours. */
438
				printf( esc_html__( '%sh', 'zt-debug-bar-cron' ), $this->get_hours( $interval ) ); // WPCS: XSS ok.
439
				unset( $interval );
440
			} else {
441
				echo esc_html__( 'Single Event', 'zt-debug-bar-cron' );
442
			}
443
		}
444
445
446
		/**
447
		 * Displays the cron arguments in a readable format.
448
		 *
449
		 * @param mixed $args Cron argument(s).
450
		 *
451
		 * @return void
452
		 */
453
		private function display_event_cron_arguments( $args ) {
454
			// Arguments defaults to an empty array if no arguments are given.
455
			if ( is_array( $args ) && array() === $args ) {
456
				echo esc_html__( 'No Args', 'zt-debug-bar-cron' );
457
				return;
458
			}
459
460
			// Ok, we have an argument, let's pretty print it.
461
			$this->print_pretty_output( $args );
462
		}
463
464
465
		/**
466
		 * Displays all of the schedules defined.
467
		 *
468
		 * @return void
469
		 */
470
		private function display_schedules() {
471
			echo '
472
				<table class="zt-debug-bar-cron-table zt-debug-bar-cron-schedule-table">
473
					<thead><tr>
474
						<th class="col1">', esc_html__( 'Interval Hook', 'zt-debug-bar-cron' ), '</th>
475
						<th class="col2">', esc_html__( 'Interval (S)', 'zt-debug-bar-cron' ), '</th>
476
						<th class="col3">', esc_html__( 'Interval (M)', 'zt-debug-bar-cron' ), '</th>
477
						<th class="col4">', esc_html__( 'Interval (H)', 'zt-debug-bar-cron' ), '</th>
478
						<th class="col5">', esc_html__( 'Display Name', 'zt-debug-bar-cron' ), '</th>
479
					</tr></thead>
480
					<tbody>';
481
482
483
			$schedules = wp_get_schedules();
484
			ksort( $schedules );
485
			uasort( $schedules, array( $this, 'schedules_sorting' ) );
486
			foreach ( $schedules as $interval_hook => $data ) {
487
				$interval = (int) $data['interval'];
488
				echo // WPCS: XSS ok.
489
				'
490
						<tr>
491
							<td>', esc_html( $interval_hook ), '</td>
492
							<td>', $interval, '</td>
493
							<td>', $this->get_minutes( $interval ), '</td>
494
							<td>', $this->get_hours( $interval ), '</td>
495
							<td>', esc_html( $data['display'] ) . '</td>
496
						</tr>';
497
			}
498
499
			echo '
500
					</tbody>
501
				</table>';
502
		}
503
504
		/**
505
		 * Sorting method for cron scheldules. Order by schedules interval.
506
		 *
507
		 * @param array $a First element of comparison pair.
508
		 * @param array $b Second element of comparison pair.
509
		 *
510
		 * @return int Return 1 if $a argument 'interval' greater then $b argument 'interval', 0 if both intervals equivalent and -1 otherwise.
511
		 */
512
		private function schedules_sorting( $a, $b ) {
513
			if ( (int) $a['interval'] === (int) $b['interval'] ) {
514
				return 0;
515
			} else {
516
				return ( ( (int) $a['interval'] > (int) $b['interval'] ) ? 1 : -1 );
517
			}
518
		}
519
520
		/**
521
		 * Verify if a given timestamp is in the past or the future.
522
		 *
523
		 * @param int $time Unix timestamp.
524
		 *
525
		 * @return bool True if the time has passed, false otherwise.
526
		 */
527
		private function is_time_in_past( $time ) {
528
			return ( time() > $time && 'No' === $this->_doing_cron );
529
		}
530
531
532
		/**
533
		 * Transform a time in seconds to minutes rounded to 2 decimals.
534
		 *
535
		 * @param int $time Unix timestamp.
536
		 *
537
		 * @return int|float
538
		 */
539
		private function get_minutes( $time ) {
540
			return round( ( (int) $time / 60 ), 2 );
541
		}
542
543
544
		/**
545
		 * Transform a time in seconds to hours rounded to 2 decimals.
546
		 *
547
		 * @param int $time Unix timestamp.
548
		 *
549
		 * @return int|float
550
		 */
551
		private function get_hours( $time ) {
552
			return round( ( (int) $time / 3600 ), 2 );
553
		}
554
555
556
		/**
557
		 * Compares time with current time and adds ' ago' if current time is greater than event time.
558
		 *
559
		 * @param string $human_time Human readable time difference.
560
		 * @param int    $time       Unix time of event.
561
		 *
562
		 * @return string
563
		 */
564
		private function display_past_time( $human_time, $time ) {
565
			if ( time() > $time ) {
566
				/* TRANSLATORS: %s is a human readable time difference. */
567
				return sprintf( __( '%s ago', 'zt-debug-bar-cron' ), $human_time );
568
			} else {
569
				return $human_time;
570
			}
571
		}
572
573
574
		/**
575
		 * Load the pretty output class & set the recursion limit.
576
		 */
577
		private function load_debug_bar_pretty_output() {
578
			if ( ! class_exists( 'Debug_Bar_Pretty_Output' ) ) {
579
				require_once plugin_dir_path( __FILE__ ) . 'inc/debug-bar-pretty-output/class-debug-bar-pretty-output.php';
580
			}
581
582
			// Limit recursion depth if possible - method available since DBPO v1.4.
583
			if ( method_exists( 'Debug_Bar_Pretty_Output', 'limit_recursion' ) ) {
584
				Debug_Bar_Pretty_Output::limit_recursion( 2 );
585
			}
586
		}
587
588
589
		/**
590
		 * Print any type of variable with colour coding and a variable type indication.
591
		 *
592
		 * @param mixed $variable The variable to print.
593
		 */
594
		private function print_pretty_output( $variable ) {
595
			if ( defined( 'Debug_Bar_Pretty_Output::VERSION' ) ) {
596
				echo Debug_Bar_Pretty_Output::get_output( $variable, '', true ); // WPCS: XSS ok.
597
			} else {
598
				// An old version of the pretty output class was loaded.
599
				// Real possibility as there are several DB plugins using the pretty print class.
600
				Debug_Bar_Pretty_Output::output( $variable, '', true );
601
			}
602
		}
603
604
605
		/**
606
		 * Unset recursion depth limit for the pretty output class.
607
		 *
608
		 * @internal Method available since DBPO v1.4.
609
		 */
610
		private function reset_debug_bar_pretty_output() {
611
			if ( method_exists( 'Debug_Bar_Pretty_Output', 'unset_recursion_limit' ) ) {
612
				Debug_Bar_Pretty_Output::unset_recursion_limit();
613
			}
614
		}
615
	} // End of class ZT_Debug_Bar_Cron.
616
617
} // End of if class_exists wrapper.
618