PluginProbe
OPcache Manager / trunk
OPcache Manager vtrunk
trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.2.0 1.3.0 1.3.1 1.3.2 2.0.0 2.1.0 2.10.0 2.11.0 2.12.0 2.13.0 2.13.1 2.14.0 2.2.0 2.3.0 2.3.1 2.3.2 2.4.0 2.5.0 2.6.0 All 36 releases
opcache-manager / includes / features / class-analytics.php

class-analytics.php in OPcache Manager trunk, at includes/features/class-analytics.php

1,609 lines 70.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OPcache Manager analytics
4 *
5 * Handles all analytics operations.
6 *
7 * @package Features
8 * @author Pierre Lannoy <https://pierre.lannoy.fr/>.
9 * @since 1.0.0
10 */
11
12 namespace OPcacheManager\Plugin\Feature;
13
14 use OPcacheManager\Plugin\Feature\Schema;
15 use OPcacheManager\System\Cache;
16 use OPcacheManager\System\Date;
17 use OPcacheManager\System\Conversion;
18 use OPcacheManager\System\L10n;
19 use OPcacheManager\System\OPcache;
20 use OPcacheManager\System\Timezone;
21 use OPcacheManager\System\UUID;
22 use OPcacheManager\System\Logger;
23 use OPcacheManager\Plugin\Feature\Capture;
24 use Feather;
25
26
27 /**
28 * Define the analytics functionality.
29 *
30 * Handles all analytics operations.
31 *
32 * @package Features
33 * @author Pierre Lannoy <https://pierre.lannoy.fr/>.
34 * @since 1.0.0
35 */
36 class Analytics {
37
38 /**
39 * The start date.
40 *
41 * @since 1.0.0
42 * @var string $start The start date.
43 */
44 private $start = '';
45
46 /**
47 * The end date.
48 *
49 * @since 1.0.0
50 * @var string $end The end date.
51 */
52 private $end = '';
53
54 /**
55 * The period duration in days.
56 *
57 * @since 1.0.0
58 * @var integer $duration The period duration in days.
59 */
60 private $duration = 0;
61
62 /**
63 * The timezone.
64 *
65 * @since 1.0.0
66 * @var string $timezone The timezone.
67 */
68 private $timezone = 'UTC';
69
70 /**
71 * The main query filter.
72 *
73 * @since 1.0.0
74 * @var array $filter The main query filter.
75 */
76 private $filter = [];
77
78 /**
79 * The query filter fro the previous range.
80 *
81 * @since 1.0.0
82 * @var array $previous The query filter fro the previous range.
83 */
84 private $previous = [];
85
86 /**
87 * Is the start date today's date.
88 *
89 * @since 1.0.0
90 * @var boolean $today Is the start date today's date.
91 */
92 private $is_today = false;
93
94 /**
95 * Colors for graphs.
96 *
97 * @since 1.0.0
98 * @var array $colors The colors array.
99 */
100 private $colors = [ '#73879C', '#3398DB', '#9B59B6', '#b2c326', '#BDC3C6' ];
101
102 /**
103 * Initialize the class and set its properties.
104 *
105 * @param string $start The start date.
106 * @param string $end The end date.
107 * @param boolean $reload Is it a reload of an already displayed analytics.
108 * @since 1.0.0
109 */
110 public function __construct( $start, $end, $reload ) {
111 $this->timezone = Timezone::site_get();
112 $this->start = $start;
113 $this->end = $end;
114 $datetime = new \DateTime( 'now' );
115 $this->is_today = ( $this->start === $datetime->format( 'Y-m-d' ) || $this->end === $datetime->format( 'Y-m-d' ) );
116 $start = Date::get_mysql_utc_from_date( $this->start . ' 00:00:00', $this->timezone->getName() );
117 $end = Date::get_mysql_utc_from_date( $this->end . ' 23:59:59', $this->timezone->getName() );
118 $this->filter[] = "timestamp>='" . $start . "' and timestamp<='" . $end . "'";
119 $start = new \DateTime( $start, $this->timezone );
120 $end = new \DateTime( $end, $this->timezone );
121 $start->sub( new \DateInterval( 'PT1S' ) );
122 $end->sub( new \DateInterval( 'PT1S' ) );
123 $delta = $start->diff( $end, true );
124 if ( $delta ) {
125 $start->sub( $delta );
126 $end->sub( $delta );
127 }
128 $this->duration = $delta->days + 1;
129 $this->previous[] = "timestamp>='" . $start->format( 'Y-m-d H:i:s' ) . "' and timestamp<='" . $end->format( 'Y-m-d H:i:s' ) . "'";
130 }
131
132 /**
133 * Query statistics table.
134 *
135 * @param string $query The query type.
136 * @param mixed $queried The query params.
137 * @return array The result of the query, ready to encode.
138 * @since 1.0.0
139 */
140 public function query( $query, $queried ) {
141 switch ( $query ) {
142 case 'main-chart':
143 return $this->query_chart();
144 case 'kpi':
145 return $this->query_kpi( $queried );
146 case 'events':
147 return $this->query_events();
148 }
149 return [];
150 }
151
152 /**
153 * Query statistics table.
154 *
155 * @return array The result of the query, ready to encode.
156 * @since 1.0.0
157 */
158 private function query_events() {
159 $data = Schema::get_list( $this->filter, ! $this->is_today, '', [], false, 'ORDER BY timestamp ASC' );
160 $result = '<table class="opcm-table">';
161 $result .= '<tr>';
162 $result .= '<th>&nbsp;</th>';
163 $result .= '<th>' . esc_html__( 'Timeframe', 'opcache-manager' ) . '</th>';
164 $result .= '<th>' . esc_html__( 'Details', 'opcache-manager' ) . '</th>';
165 $result .= '</tr>';
166 $found = false;
167 foreach ( $data as $key => $row ) {
168 $op = $row['reset'];
169 $name = '';
170 $time = '';
171 $details = '';
172 $str = [];
173 switch ( $row['reset'] ) {
174 case 'oom':
175 $icon = '<img style="width:14px;vertical-align:text-bottom;" src="' . Feather\Icons::get_base64( 'cpu', 'none', '#73879C' ) . '" />';
176 $name = esc_html__( 'Reset due to free memory exhaustion.', 'opcache-manager' );
177 break;
178 case 'hash':
179 $icon = '<img style="width:14px;vertical-align:text-bottom;" src="' . Feather\Icons::get_base64( 'database', 'none', '#73879C' ) . '" />';
180 $name = esc_html__( 'Reset due to excessive keys saturation.', 'opcache-manager' );
181 break;
182 case 'manual':
183 $icon = '<img style="width:14px;vertical-align:text-bottom;" src="' . Feather\Icons::get_base64( 'settings', 'none', '#73879C' ) . '" />';
184 $name = esc_html__( 'Programmatic or manual reset.', 'opcache-manager' );
185 }
186 switch ( $row['status'] ) {
187 case 'reset_warmup':
188 $icon = '<img style="width:14px;vertical-align:text-bottom;" src="' . Feather\Icons::get_base64( 'clock', 'none', '#73879C' ) . '" />';
189 $name = esc_html__( 'Programmatic site invalidation and warm-up.', 'opcache-manager' );
190 $op = $row['status'];
191 break;
192 case 'warmup':
193 $icon = '<img style="width:14px;vertical-align:text-bottom;" src="' . Feather\Icons::get_base64( 'mouse-pointer', 'none', '#73879C' ) . '" />';
194 $name = esc_html__( 'Manual site warm-up.', 'opcache-manager' );
195 $op = $row['status'];
196 break;
197 case 'cache_full':
198 if ( array_key_exists( $key - 1, $data ) && 'cache_full' !== $data[ $key - 1 ]['status'] ) {
199 $icon = '<img style="width:14px;vertical-align:text-bottom;" src="' . Feather\Icons::get_base64( 'alert-triangle', 'none', '#73879C' ) . '" />';
200 $name = esc_html__( 'Cache is full.', 'opcache-manager' );
201 $op = $row['status'];
202 }
203 break;
204 }
205 switch ( $row['status'] ) {
206 case 'disabled':
207 if ( array_key_exists( $key - 1, $data ) && 'disabled' !== $data[ $key - 1 ]['status'] ) {
208 $icon = '<img style="width:14px;vertical-align:text-bottom;" src="' . Feather\Icons::get_base64( 'power', 'none', '#73879C' ) . '" />';
209 $name = esc_html__( 'OPcache disabled.', 'opcache-manager' );
210 $op = $row['status'];
211 }
212 break;
213 default:
214 if ( array_key_exists( $key - 1, $data ) && 'disabled' === $data[ $key - 1 ]['status'] ) {
215 $icon = '<img style="width:14px;vertical-align:text-bottom;" src="' . Feather\Icons::get_base64( 'power', 'none', '#73879C' ) . '" />';
216 $name = esc_html__( 'OPcache enabled.', 'opcache-manager' );
217 $op = 'enabled';
218 }
219 }
220 if ( array_key_exists( $key - 1, $data ) && 'recycle_in_progress' === $data[ $key - 1 ]['status'] ) {
221 $op = 'recycle_in_progress';
222 }
223 $conf = [];
224 if ( array_key_exists( $key - 1, $data ) ) {
225 foreach ( [ 'mem', 'key', 'buf' ] as $idx ) {
226 if ( $row[ $idx . '_total' ] !== $data[ $key - 1 ][ $idx . '_total' ] ) {
227 $conf[] = $idx;
228 }
229 }
230 }
231 if ( 0 < count( $conf ) && 'disabled' !== $op && 'enabled' !== $op && 'recycle_in_progress' !== $op ) {
232 foreach ( $conf as $idx ) {
233 $val = $row[ $idx . '_total' ] - $data[ $key - 1 ][ $idx . '_total' ];
234 switch ( $idx ) {
235 case 'mem':
236 if ( 0 < $val ) {
237 $str[] = sprintf( esc_html__( 'Total memory size increased by %s.', 'opcache-manager' ), Conversion::data_shorten( abs( $val ), 0, false, '&nbsp;' ) );
238 } elseif ( 0 > $val ) {
239 $str[] = sprintf( esc_html__( 'Total memory size decreased by %s.', 'opcache-manager' ), Conversion::data_shorten( abs( $val ), 0, false, '&nbsp;' ) );
240 }
241 $op = 'settings';
242 break;
243 case 'buf':
244 if ( 0 < $val ) {
245 $str[] = sprintf( esc_html__( 'Total buffer size increased by %s.', 'opcache-manager' ), Conversion::data_shorten( abs( $val ), 0, false, '&nbsp;' ) );
246 } elseif ( 0 > $val ) {
247 $str[] = sprintf( esc_html__( 'Total buffer size decreased by %s.', 'opcache-manager' ), Conversion::data_shorten( abs( $val ), 0, false, '&nbsp;' ) );
248 }
249 $op = 'settings';
250 break;
251 case 'key':
252 if ( 0 < $val ) {
253 $str[] = sprintf( esc_html__( 'Maximum keys slots increased by %s.', 'opcache-manager' ), Conversion::number_shorten( abs( $val ), 0, false, '' ) );
254 } elseif ( 0 > $val ) {
255 $str[] = sprintf( esc_html__( 'Maximum keys slots decreased by %s.', 'opcache-manager' ), Conversion::number_shorten( abs( $val ), 0, false, '' ) );
256 }
257 $op = 'settings';
258 break;
259 }
260 }
261 }
262 if ( 'none' === $op || '' === $name ) {
263 continue;
264 }
265 $found = true;
266 $timestamp = new \DateTime( $row['timestamp'] );
267 $timestamp->setTimezone( $this->timezone );
268 $time = $timestamp->format( 'H:i' );
269 $timestamp->sub( new \DateInterval( 'PT5M' ) );
270 $time = $timestamp->format( 'Y-m-d H:i' ) . ' ⇥ ' . $time;
271 if ( 0 < count( $str ) ) {
272 $sicon = '<img style="width:14px;vertical-align:text-bottom;" src="' . Feather\Icons::get_base64( 'tool', 'none', '#73879C' ) . '" />';
273 $sname = esc_html__( 'Settings changed.', 'opcache-manager' );
274 $sdetails = implode( ' ', $str );
275 $row_str = '<tr>';
276 $row_str .= '<td data-th="">' . $sicon . '&nbsp;&nbsp;<span class="opcm-table-text">' . $sname . '</span></td>';
277 $row_str .= '<td data-th="' . esc_html__( 'Timeframe', 'opcache-manager' ) . '">' . $time . '</td>';
278 $row_str .= '<td data-th="' . esc_html__( 'Details', 'opcache-manager' ) . '">' . $sdetails . '</td>';
279 $row_str .= '</tr>';
280 $result .= $row_str;
281 }
282 $str = [];
283 switch ( $op ) {
284 case 'oom':
285 case 'hash':
286 case 'manual':
287 case 'reset_warmup':
288 if ( array_key_exists( $key - 1, $data ) ) {
289 $val = ( $row['mem_total'] - $row['mem_used'] - $row['mem_wasted'] ) - ( $data[ $key - 1 ]['mem_total'] - $data[ $key - 1 ]['mem_used'] - $data[ $key - 1 ]['mem_wasted'] );
290 if ( 0 < $val ) {
291 $str[] = sprintf( esc_html__( 'Free memory size increased by %s.', 'opcache-manager' ), Conversion::data_shorten( abs( $val ), 0, false, '&nbsp;' ) );
292 } elseif ( 0 > $val ) {
293 $str[] = sprintf( esc_html__( 'Free memory size decreased by %s.', 'opcache-manager' ), Conversion::data_shorten( abs( $val ), 0, false, '&nbsp;' ) );
294 }
295 $val = ( $row['buf_total'] - $row['buf_used'] ) - ( $data[ $key - 1 ]['buf_total'] - $data[ $key - 1 ]['buf_used'] );
296 if ( 0 < $val ) {
297 $str[] = sprintf( esc_html__( 'Free buffer size increased by %s.', 'opcache-manager' ), Conversion::data_shorten( abs( $val ), 0, false, '&nbsp;' ) );
298 } elseif ( 0 > $val ) {
299 $str[] = sprintf( esc_html__( 'Free buffer size decreased by %s.', 'opcache-manager' ), Conversion::data_shorten( abs( $val ), 0, false, '&nbsp;' ) );
300 }
301 $val = ( $row['key_total'] - $row['key_used'] ) - ( $data[ $key - 1 ]['key_total'] - $data[ $key - 1 ]['key_used'] );
302 if ( 0 < $val ) {
303 $str[] = sprintf( esc_html__( 'Free keys slots increased by %s.', 'opcache-manager' ), Conversion::number_shorten( abs( $val ), 0, false, '' ) );
304 } elseif ( 0 > $val ) {
305 $str[] = sprintf( esc_html__( 'Free keys slots decreased by %s.', 'opcache-manager' ), Conversion::number_shorten( abs( $val ), 0, false, '' ) );
306 }
307 }
308 $details = implode( ' ', $str );
309 break;
310 case 'warmup':
311 case 'disabled':
312 case 'enabled':
313 break;
314 case 'cache_full':
315 $details = sprintf( esc_html__( 'Current wasted memory: %s.', 'opcache-manager' ), Conversion::data_shorten( $row['mem_wasted'], 0, false, '&nbsp;' ) );
316 break;
317 }
318 if ( '' === $details ) {
319 $details = '-';
320 }
321 $row_str = '<tr>';
322 $row_str .= '<td data-th="">' . $icon . '&nbsp;&nbsp;<span class="opcm-table-text">' . $name . '</span></td>';
323 $row_str .= '<td data-th="' . esc_html__( 'Timeframe', 'opcache-manager' ) . '">' . $time . '</td>';
324 $row_str .= '<td data-th="' . esc_html__( 'Details', 'opcache-manager' ) . '">' . $details . '</td>';
325 $row_str .= '</tr>';
326 $result .= $row_str;
327 }
328 if ( ! $found ) {
329 $row_str = '<tr>';
330 $row_str .= '<td data-th=""><em>' . esc_html__( 'No status events in the selected time range.', 'opcache-manager' ) . '</em></span></td>';
331 $row_str .= '<td data-th="' . esc_html__( 'Timeframe', 'opcache-manager' ) . '">&nbsp;</td>';
332 $row_str .= '<td data-th="' . esc_html__( 'Details', 'opcache-manager' ) . '">&nbsp;</td>';
333 $row_str .= '</tr>';
334 $result .= $row_str;
335 }
336 $result .= '</table>';
337 return [ 'opcm-events' => $result ];
338 }
339
340 /**
341 * Query statistics table.
342 *
343 * @return array The result of the query, ready to encode.
344 * @since 1.0.0
345 */
346 private function query_chart() {
347 $uuid = UUID::generate_unique_id( 5 );
348 $query = Schema::get_time_series( $this->filter, ! $this->is_today, '', [], false );
349 $data = [];
350 $series = [];
351 $items = [ 'status', 'mem_total', 'mem_used', 'mem_wasted', 'key_total', 'key_used', 'buf_total', 'buf_used', 'hit', 'miss', 'strings', 'scripts' ];
352 $maxhit = 0;
353 $maxstrings = 0;
354 $maxscripts = 0;
355 // Data normalization.
356 if ( 0 !== count( $query ) ) {
357 if ( 1 === $this->duration ) {
358 $start = new \DateTime( Date::get_mysql_utc_from_date( $this->start . ' 00:00:00', $this->timezone->getName() ), new \DateTimeZone( 'UTC' ) );
359 $real = new \DateTime( array_values( $query )[0]['timestamp'], new \DateTimeZone( 'UTC' ) );
360 $offset = $this->timezone->getOffset( $real );
361 $ts = $start->getTimestamp();
362 $record = [];
363 foreach ( $items as $item ) {
364 $record[ $item ] = 0;
365 }
366 while ( 300 + Capture::$delta < $real->getTimestamp() - $ts ) {
367 $ts = $ts + 300;
368 $data[ $ts + $offset ] = $record;
369 }
370 foreach ( $query as $timestamp => $row ) {
371 $datetime = new \DateTime( $timestamp, new \DateTimeZone( 'UTC' ) );
372 $offset = $this->timezone->getOffset( $datetime );
373 $ts = $datetime->getTimestamp() + $offset;
374 $data[ $ts ] = $row;
375 }
376 $end = new \DateTime( Date::get_mysql_utc_from_date( $this->end . ' 23:59:59', $this->timezone->getName() ), $this->timezone );
377 $end = $end->getTimestamp();
378 $datetime = new \DateTime( $timestamp, new \DateTimeZone( 'UTC' ) );
379 $offset = $this->timezone->getOffset( $datetime );
380 $timestamp = $datetime->getTimestamp() + 300;
381 while ( $timestamp <= $end + $offset ) {
382 $datetime = new \DateTime( date( 'Y-m-d H:i:s', $timestamp ), new \DateTimeZone( 'UTC' ) );
383 $offset = $this->timezone->getOffset( $datetime );
384 $ts = $datetime->getTimestamp() + $offset;
385 $record = [];
386 foreach ( $items as $item ) {
387 $record[ $item ] = 0;
388 }
389 $data[ $ts ] = $record;
390 $timestamp = $timestamp + 300;
391 }
392 $datetime = new \DateTime( $this->start . ' 00:00:00', $this->timezone );
393 $offset = $this->timezone->getOffset( $datetime );
394 $datetime = $datetime->getTimestamp() + $offset;
395 $before = [
396 'x' => 'new Date(' . (string) ( $datetime ) . '000)',
397 'y' => 'null',
398 ];
399 $datetime = new \DateTime( $this->end . ' 23:59:59', $this->timezone );
400 $offset = $this->timezone->getOffset( $datetime );
401 $datetime = $datetime->getTimestamp() + $offset;
402 $after = [
403 'x' => 'new Date(' . (string) ( $datetime ) . '000)',
404 'y' => 'null',
405 ];
406 } else {
407 $buffer = [];
408 foreach ( $query as $timestamp => $row ) {
409 $datetime = new \DateTime( $timestamp, new \DateTimeZone( 'UTC' ) );
410 $datetime->setTimezone( $this->timezone );
411 $buffer[ $datetime->format( 'Y-m-d' ) ][] = $row;
412 }
413 foreach ( $buffer as $timestamp => $rows ) {
414 $record = [];
415 foreach ( $items as $item ) {
416 $record[ $item ] = 0;
417 }
418 foreach ( $rows as $row ) {
419 foreach ( $items as $item ) {
420 if ( 'status' === $item ) {
421 $record[ $item ] = ( 'disabled' === $row[ $item ] ? 0 : 100 );
422 } else {
423 $record[ $item ] = $record[ $item ] + $row[ $item ];
424 }
425 }
426 }
427 $cpt = count( $rows );
428 if ( 0 < $cpt ) {
429 foreach ( $items as $item ) {
430 $record[ $item ] = (int) round( $record[ $item ] / $cpt, 0 );
431 }
432 }
433 $data[ strtotime( $timestamp ) ] = $record;
434 }
435 $before = [
436 'x' => 'new Date(' . (string) ( strtotime( $this->start ) - 86400 ) . '000)',
437 'y' => 'null',
438 ];
439 $after = [
440 'x' => 'new Date(' . (string) ( strtotime( $this->end ) + 86400 ) . '000)',
441 'y' => 'null',
442 ];
443 }
444 // Series computation.
445 foreach ( $data as $timestamp => $datum ) {
446 $ts = 'new Date(' . (string) $timestamp . '000)';
447 // Hit ratio.
448 $val = 'null';
449 if ( 0 !== (int) $datum['hit'] + (int) $datum['miss'] ) {
450 $val = round( 100 * $datum['hit'] / ( $datum['hit'] + $datum['miss'] ), 3 );
451 }
452 $series['ratio'][] = [
453 'x' => $ts,
454 'y' => $val,
455 ];
456 // Availablility.
457 $series['availability'][] = [
458 'x' => $ts,
459 'y' => ( 'disabled' === $datum['status'] ? 0 : 100 ),
460 ];
461 // Time series.
462 foreach ( [ 'hit', 'miss', 'strings', 'scripts' ] as $item ) {
463 $val = (int) $datum[ $item ];
464 $series[ $item ][] = [
465 'x' => $ts,
466 'y' => $val,
467 ];
468 switch ( $item ) {
469 case 'hit':
470 case 'miss':
471 if ( $maxhit < $val ) {
472 $maxhit = $val;
473 }
474 break;
475 case 'strings':
476 if ( $maxstrings < $val ) {
477 $maxstrings = $val;
478 }
479 break;
480 case 'scripts':
481 if ( $maxscripts < $val ) {
482 $maxscripts = $val;
483 }
484 break;
485 }
486 }
487 // Time series (free vs.used).
488 foreach ( [ 'buf', 'key', 'mem' ] as $item ) {
489 if ( 'key' === $item ) {
490 $factor = 1024;
491 } else {
492 $factor = 1024 * 1024;
493 }
494 if ( 'mem' === $item ) {
495 $series['memory'][0][] = [
496 'x' => $ts,
497 'y' => round( $datum['mem_used'] / $factor, 2 ),
498 ];
499 $series['memory'][1][] = [
500 'x' => $ts,
501 'y' => round( ( $datum['mem_total'] - $datum['mem_used'] - $datum['mem_wasted'] ) / $factor, 2 ),
502 ];
503 $series['memory'][2][] = [
504 'x' => $ts,
505 'y' => round( $datum['mem_wasted'] / $factor, 2 ),
506 ];
507 } else {
508 $series[ $item ][0][] = [
509 'x' => $ts,
510 'y' => round( $datum[ $item . '_used' ] / $factor, 2 ),
511 ];
512 $series[ $item ][1][] = [
513 'x' => $ts,
514 'y' => round( ( $datum[ $item . '_total' ] - $datum[ $item . '_used' ] ) / $factor, 2 ),
515 ];
516 }
517 }
518 }
519 // Hit ratio.
520 array_unshift( $series['ratio'], $before );
521 $series['ratio'][] = $after;
522 $json_ratio = wp_json_encode(
523 [
524 'series' => [
525 [
526 'name' => esc_html_x( 'Hit Ratio', 'Noun - Cache hit ratio.', 'opcache-manager' ),
527 'data' => $series['ratio'],
528 ],
529 ],
530 ]
531 );
532 $json_ratio = str_replace( '"x":"new', '"x":new', $json_ratio );
533 $json_ratio = str_replace( ')","y"', '),"y"', $json_ratio );
534 $json_ratio = str_replace( '"null"', 'null', $json_ratio );
535
536 // Availability.
537 array_unshift( $series['availability'], $before );
538 $series['availability'][] = $after;
539 $json_availability = wp_json_encode(
540 [
541 'series' => [
542 [
543 'name' => esc_html__( 'Availability', 'opcache-manager' ),
544 'data' => $series['availability'],
545 ],
546 ],
547 ]
548 );
549 $json_availability = str_replace( '"x":"new', '"x":new', $json_availability );
550 $json_availability = str_replace( ')","y"', '),"y"', $json_availability );
551 $json_availability = str_replace( '"null"', 'null', $json_availability );
552
553 // Hit & miss distribution.
554 array_unshift( $series['hit'], $before );
555 $series['hit'][] = $after;
556 array_unshift( $series['miss'], $before );
557 $series['miss'][] = $after;
558 $json_hit = wp_json_encode(
559 [
560 'series' => [
561 [
562 'name' => esc_html__( 'Hit Count', 'opcache-manager' ),
563 'data' => $series['hit'],
564 ],
565 [
566 'name' => esc_html__( 'Miss Count', 'opcache-manager' ),
567 'data' => $series['miss'],
568 ],
569 ],
570 ]
571 );
572 $json_hit = str_replace( '"x":"new', '"x":new', $json_hit );
573 $json_hit = str_replace( ')","y"', '),"y"', $json_hit );
574 $json_hit = str_replace( '"null"', 'null', $json_hit );
575
576 // Scripts variation.
577 array_unshift( $series['scripts'], $before );
578 $series['scripts'][] = $after;
579 $json_scripts = wp_json_encode(
580 [
581 'series' => [
582 [
583 'name' => esc_html__( 'Files Count', 'opcache-manager' ),
584 'data' => $series['scripts'],
585 ],
586 ],
587 ]
588 );
589 $json_scripts = str_replace( '"x":"new', '"x":new', $json_scripts );
590 $json_scripts = str_replace( ')","y"', '),"y"', $json_scripts );
591 $json_scripts = str_replace( '"null"', 'null', $json_scripts );
592
593 // Strings variation.
594 array_unshift( $series['strings'], $before );
595 $series['strings'][] = $after;
596 $json_strings = wp_json_encode(
597 [
598 'series' => [
599 [
600 'name' => esc_html__( 'Strings Count', 'opcache-manager' ),
601 'data' => $series['strings'],
602 ],
603 ],
604 ]
605 );
606 $json_strings = str_replace( '"x":"new', '"x":new', $json_strings );
607 $json_strings = str_replace( ')","y"', '),"y"', $json_strings );
608 $json_strings = str_replace( '"null"', 'null', $json_strings );
609
610 // Memory.
611 array_unshift( $series['memory'][0], $before );
612 $series['memory'][0][] = $after;
613 array_unshift( $series['memory'][1], $before );
614 $series['memory'][1][] = $after;
615 array_unshift( $series['memory'][2], $before );
616 $series['memory'][2][] = $after;
617 $json_memory = wp_json_encode(
618 [
619 'series' => [
620 [
621 'name' => esc_html__( 'Used Memory', 'opcache-manager' ),
622 'data' => $series['memory'][0],
623 ],
624 [
625 'name' => esc_html__( 'Free Memory', 'opcache-manager' ),
626 'data' => $series['memory'][1],
627 ],
628 [
629 'name' => esc_html__( 'Wasted Memory', 'opcache-manager' ),
630 'data' => $series['memory'][2],
631 ],
632 ],
633 ]
634 );
635 $json_memory = str_replace( '"x":"new', '"x":new', $json_memory );
636 $json_memory = str_replace( ')","y"', '),"y"', $json_memory );
637 $json_memory = str_replace( '"null"', 'null', $json_memory );
638
639 // Key.
640 array_unshift( $series['key'][0], $before );
641 $series['key'][0][] = $after;
642 array_unshift( $series['key'][1], $before );
643 $series['mkeyem'][1][] = $after;
644 $json_key = wp_json_encode(
645 [
646 'series' => [
647 [
648 'name' => esc_html__( 'Used Key Slots', 'opcache-manager' ),
649 'data' => $series['key'][0],
650 ],
651 [
652 'name' => esc_html__( 'Free Key Slots', 'opcache-manager' ),
653 'data' => $series['key'][1],
654 ],
655 ],
656 ]
657 );
658 $json_key = str_replace( '"x":"new', '"x":new', $json_key );
659 $json_key = str_replace( ')","y"', '),"y"', $json_key );
660 $json_key = str_replace( '"null"', 'null', $json_key );
661
662 // Buf.
663 array_unshift( $series['buf'][0], $before );
664 $series['buf'][0][] = $after;
665 array_unshift( $series['buf'][1], $before );
666 $series['buf'][1][] = $after;
667 $json_buf = wp_json_encode(
668 [
669 'series' => [
670 [
671 'name' => esc_html__( 'Used Buffer', 'opcache-manager' ),
672 'data' => $series['buf'][0],
673 ],
674 [
675 'name' => esc_html__( 'Free Buffer', 'opcache-manager' ),
676 'data' => $series['buf'][1],
677 ],
678 ],
679 ]
680 );
681 $json_buf = str_replace( '"x":"new', '"x":new', $json_buf );
682 $json_buf = str_replace( ')","y"', '),"y"', $json_buf );
683 $json_buf = str_replace( '"null"', 'null', $json_buf );
684
685 // Rendering.
686 $ticks = (int) ( 1 + ( $this->duration / 15 ) );
687 if ( 1 < $this->duration ) {
688 $style = 'opcm-multichart-xlarge-item';
689 if ( 20 < $this->duration ) {
690 $style = 'opcm-multichart-large-item';
691 }
692 if ( 40 < $this->duration ) {
693 $style = 'opcm-multichart-medium-item';
694 }
695 if ( 60 < $this->duration ) {
696 $style = 'opcm-multichart-small-item';
697 }
698 if ( 80 < $this->duration ) {
699 $style = 'opcm-multichart-xsmall-item';
700 }
701 } else {
702 $style = 'opcm-multichart-xxsmall-item';
703 }
704 $result = '<div class="opcm-multichart-handler">';
705 $result .= '<div class="opcm-multichart-item active" id="opcm-chart-ratio">';
706 $result .= '</div>';
707 $result .= '<script>';
708 $result .= 'jQuery(function ($) {';
709 $result .= ' var ratio_data' . $uuid . ' = ' . $json_ratio . ';';
710 $result .= ' var ratio_tooltip' . $uuid . ' = Chartist.plugins.tooltip({percentage: false, appendToBody: true});';
711 $result .= ' var ratio_option' . $uuid . ' = {';
712 $result .= ' height: 300,';
713 $result .= ' fullWidth: true,';
714 $result .= ' showArea: true,';
715 $result .= ' showLine: true,';
716 $result .= ' showPoint: false,';
717 $result .= ' plugins: [ratio_tooltip' . $uuid . '],';
718 if ( 1 < $this->duration ) {
719 $result .= ' axisX: {showGrid: true, scaleMinSpace: 10, type: Chartist.FixedScaleAxis, divisor:' . ( $this->duration + 1 ) . ', labelInterpolationFnc: function skipLabels(value, index, labels) {return 0 === index % ' . $ticks . ' ? moment(value).format("DD") : null;}},';
720 } else {
721 $result .= ' axisX: {showGrid: true,labelOffset: {x: -8,y: 0},scaleMinSpace: 100, type: Chartist.FixedScaleAxis, divisor:8, labelInterpolationFnc: function (value) {var shift=0;if(moment(value).isDST()){shift=3600000};return moment(value-shift).format("HH:00");}},';
722 }
723 $result .= ' axisY: {type: Chartist.AutoScaleAxis, labelInterpolationFnc: function (value) {return value.toString() + " %";}},';
724 $result .= ' };';
725 $result .= ' new Chartist.Line("#opcm-chart-ratio", ratio_data' . $uuid . ', ratio_option' . $uuid . ');';
726 $result .= '});';
727 $result .= '</script>';
728 $result .= '<div class="opcm-multichart-item" id="opcm-chart-uptime">';
729 $result .= '</div>';
730 $result .= '<script>';
731 $result .= 'jQuery(function ($) {';
732 $result .= ' var uptime_data' . $uuid . ' = ' . $json_availability . ';';
733 $result .= ' var uptime_tooltip' . $uuid . ' = Chartist.plugins.tooltip({percentage: false, appendToBody: true});';
734 $result .= ' var uptime_option' . $uuid . ' = {';
735 $result .= ' height: 300,';
736 $result .= ' fullWidth: true,';
737 $result .= ' showArea: true,';
738 $result .= ' showLine: true,';
739 $result .= ' showPoint: false,';
740 $result .= ' plugins: [uptime_tooltip' . $uuid . '],';
741 if ( 1 < $this->duration ) {
742 $result .= ' axisX: {showGrid: true, scaleMinSpace: 10, type: Chartist.FixedScaleAxis, divisor:' . ( $this->duration + 1 ) . ', labelInterpolationFnc: function skipLabels(value, index, labels) {return 0 === index % ' . $ticks . ' ? moment(value).format("DD") : null;}},';
743 } else {
744 $result .= ' axisX: {showGrid: true,labelOffset: {x: -8,y: 0},scaleMinSpace: 100, type: Chartist.FixedScaleAxis, divisor:8, labelInterpolationFnc: function (value) {var shift=0;if(moment(value).isDST()){shift=3600000};return moment(value-shift).format("HH:00");}},';
745 }
746 $result .= ' axisY: {type: Chartist.AutoScaleAxis, labelInterpolationFnc: function (value) {return value.toString() + " %";}},';
747 $result .= ' };';
748 $result .= ' new Chartist.Line("#opcm-chart-uptime", uptime_data' . $uuid . ', uptime_option' . $uuid . ');';
749 $result .= '});';
750 $result .= '</script>';
751 $result .= '<div class="opcm-multichart-item" id="opcm-chart-hit">';
752 $result .= '</div>';
753 $result .= '<script>';
754 $result .= 'jQuery(function ($) {';
755 $result .= ' var hit_data' . $uuid . ' = ' . $json_hit . ';';
756 $result .= ' var hit_tooltip' . $uuid . ' = Chartist.plugins.tooltip({percentage: false, appendToBody: true});';
757 $result .= ' var hit_option' . $uuid . ' = {';
758 $result .= ' height: 300,';
759 $result .= ' fullWidth: true,';
760 $result .= ' showArea: true,';
761 $result .= ' showLine: true,';
762 $result .= ' showPoint: false,';
763 $result .= ' plugins: [hit_tooltip' . $uuid . '],';
764 if ( 1 < $this->duration ) {
765 $result .= ' axisX: {showGrid: true, scaleMinSpace: 10, type: Chartist.FixedScaleAxis, divisor:' . ( $this->duration + 1 ) . ', labelInterpolationFnc: function skipLabels(value, index, labels) {return 0 === index % ' . $ticks . ' ? moment(value).format("DD") : null;}},';
766 } else {
767 $result .= ' axisX: {showGrid: true,labelOffset: {x: -8,y: 0},scaleMinSpace: 100, type: Chartist.FixedScaleAxis, divisor:8, labelInterpolationFnc: function (value) {var shift=0;if(moment(value).isDST()){shift=3600000};return moment(value-shift).format("HH:00");}},';
768 }
769 if ( $maxhit < 1000 ) {
770 $result .= ' axisY: {type: Chartist.AutoScaleAxis, labelInterpolationFnc: function (value) {return value.toString();}},';
771 } elseif ( $maxhit < 1000000 ) {
772 $result .= ' axisY: {type: Chartist.AutoScaleAxis, labelInterpolationFnc: function (value) {value = value / 1000; return value.toString() + " K";}},';
773 } else {
774 $result .= ' axisY: {type: Chartist.AutoScaleAxis, labelInterpolationFnc: function (value) {value = value / 1000000; return value.toString() + " M";}},';
775 }
776 $result .= ' };';
777 $result .= ' new Chartist.Line("#opcm-chart-hit", hit_data' . $uuid . ', hit_option' . $uuid . ');';
778 $result .= '});';
779 $result .= '</script>';
780 $result .= '<div class="opcm-multichart-item" id="opcm-chart-string">';
781 $result .= '</div>';
782 $result .= '<script>';
783 $result .= 'jQuery(function ($) {';
784 $result .= ' var string_data' . $uuid . ' = ' . $json_strings . ';';
785 $result .= ' var string_tooltip' . $uuid . ' = Chartist.plugins.tooltip({percentage: false, appendToBody: true});';
786 $result .= ' var string_option' . $uuid . ' = {';
787 $result .= ' height: 300,';
788 $result .= ' fullWidth: true,';
789 $result .= ' showArea: true,';
790 $result .= ' showLine: true,';
791 $result .= ' showPoint: false,';
792 $result .= ' plugins: [string_tooltip' . $uuid . '],';
793 if ( 1 < $this->duration ) {
794 $result .= ' axisX: {showGrid: true, scaleMinSpace: 10, type: Chartist.FixedScaleAxis, divisor:' . ( $this->duration + 1 ) . ', labelInterpolationFnc: function skipLabels(value, index, labels) {return 0 === index % ' . $ticks . ' ? moment(value).format("DD") : null;}},';
795 } else {
796 $result .= ' axisX: {showGrid: true,labelOffset: {x: -8,y: 0},scaleMinSpace: 100, type: Chartist.FixedScaleAxis, divisor:8, labelInterpolationFnc: function (value) {var shift=0;if(moment(value).isDST()){shift=3600000};return moment(value-shift).format("HH:00");}},';
797 }
798 if ( $maxstrings < 1000 ) {
799 $result .= ' axisY: {type: Chartist.AutoScaleAxis, labelInterpolationFnc: function (value) {return value.toString();}},';
800 } elseif ( $maxstrings < 1000000 ) {
801 $result .= ' axisY: {type: Chartist.AutoScaleAxis, labelInterpolationFnc: function (value) {value = value / 1000; return value.toString() + " K";}},';
802 } else {
803 $result .= ' axisY: {type: Chartist.AutoScaleAxis, labelInterpolationFnc: function (value) {value = value / 1000000; return value.toString() + " M";}},';
804 }
805 $result .= ' };';
806 $result .= ' new Chartist.Line("#opcm-chart-string", string_data' . $uuid . ', string_option' . $uuid . ');';
807 $result .= '});';
808 $result .= '</script>';
809 $result .= '<div class="opcm-multichart-item" id="opcm-chart-file">';
810 $result .= '</div>';
811 $result .= '<script>';
812 $result .= 'jQuery(function ($) {';
813 $result .= ' var file_data' . $uuid . ' = ' . $json_scripts . ';';
814 $result .= ' var file_tooltip' . $uuid . ' = Chartist.plugins.tooltip({percentage: false, appendToBody: true});';
815 $result .= ' var file_option' . $uuid . ' = {';
816 $result .= ' height: 300,';
817 $result .= ' fullWidth: true,';
818 $result .= ' showArea: true,';
819 $result .= ' showLine: true,';
820 $result .= ' showPoint: false,';
821 $result .= ' plugins: [file_tooltip' . $uuid . '],';
822 if ( 1 < $this->duration ) {
823 $result .= ' axisX: {showGrid: true, scaleMinSpace: 10, type: Chartist.FixedScaleAxis, divisor:' . ( $this->duration + 1 ) . ', labelInterpolationFnc: function skipLabels(value, index, labels) {return 0 === index % ' . $ticks . ' ? moment(value).format("DD") : null;}},';
824 } else {
825 $result .= ' axisX: {showGrid: true,labelOffset: {x: -8,y: 0},scaleMinSpace: 100, type: Chartist.FixedScaleAxis, divisor:8, labelInterpolationFnc: function (value) {var shift=0;if(moment(value).isDST()){shift=3600000};return moment(value-shift).format("HH:00");}},';
826 }
827 if ( $maxscripts < 1000 ) {
828 $result .= ' axisY: {type: Chartist.AutoScaleAxis, labelInterpolationFnc: function (value) {return value.toString();}},';
829 } elseif ( $maxscripts < 1000000 ) {
830 $result .= ' axisY: {type: Chartist.AutoScaleAxis, labelInterpolationFnc: function (value) {value = value / 1000; return value.toString() + " K";}},';
831 } else {
832 $result .= ' axisY: {type: Chartist.AutoScaleAxis, labelInterpolationFnc: function (value) {value = value / 1000000; return value.toString() + " M";}},';
833 }
834 $result .= ' };';
835 $result .= ' new Chartist.Line("#opcm-chart-file", file_data' . $uuid . ', file_option' . $uuid . ');';
836 $result .= '});';
837 $result .= '</script>';
838 $result .= '<div class="' . $style . '" id="opcm-chart-memory">';
839 $result .= '</div>';
840 $result .= '<script>';
841 $result .= 'jQuery(function ($) {';
842 $result .= ' var memory_data' . $uuid . ' = ' . $json_memory . ';';
843 $result .= ' var memory_tooltip' . $uuid . ' = Chartist.plugins.tooltip({justvalue: true, appendToBody: true});';
844 $result .= ' var memory_option' . $uuid . ' = {';
845 $result .= ' height: 300,';
846 $result .= ' stackBars: true,';
847 $result .= ' stackMode: "accumulate",';
848 $result .= ' seriesBarDistance: 1,';
849 $result .= ' plugins: [memory_tooltip' . $uuid . '],';
850 if ( 1 < $this->duration ) {
851 $result .= ' axisX: {showGrid: false, scaleMinSpace: 10, type: Chartist.FixedScaleAxis, divisor:' . ( $this->duration + 1 ) . ', labelInterpolationFnc: function skipLabels(value, index, labels) {return 0 === index % ' . $ticks . ' ? moment(value).format("DD") : null;}},';
852 } else {
853 $result .= ' axisX: {showGrid: false,labelOffset: {x: -8,y: 0},type: Chartist.FixedScaleAxis, divisor:8, labelInterpolationFnc: function (value) {var shift=0;if(moment(value).isDST()){shift=3600000};return moment(value-shift).format("HH:00");}},';
854 }
855 $result .= ' axisY: {showGrid: true, labelInterpolationFnc: function (value) {return value.toString() + " ' . esc_html_x( 'MB', 'Abbreviation - Stands for "megabytes".', 'opcache-manager' ) . '";}},';
856 $result .= ' };';
857 $result .= ' new Chartist.Bar("#opcm-chart-memory", memory_data' . $uuid . ', memory_option' . $uuid . ');';
858 $result .= '});';
859 $result .= '</script>';
860 $result .= '<div class="' . $style . '" id="opcm-chart-buffer">';
861 $result .= '</div>';
862 $result .= '<script>';
863 $result .= 'jQuery(function ($) {';
864 $result .= ' var buffer_data' . $uuid . ' = ' . $json_buf . ';';
865 $result .= ' var buffer_tooltip' . $uuid . ' = Chartist.plugins.tooltip({justvalue: true, appendToBody: true});';
866 $result .= ' var buffer_option' . $uuid . ' = {';
867 $result .= ' height: 300,';
868 $result .= ' stackBars: true,';
869 $result .= ' stackMode: "accumulate",';
870 $result .= ' seriesBarDistance: 1,';
871 $result .= ' plugins: [buffer_tooltip' . $uuid . '],';
872 if ( 1 < $this->duration ) {
873 $result .= ' axisX: {showGrid: false, scaleMinSpace: 10, type: Chartist.FixedScaleAxis, divisor:' . ( $this->duration + 1 ) . ', labelInterpolationFnc: function skipLabels(value, index, labels) {return 0 === index % ' . $ticks . ' ? moment(value).format("DD") : null;}},';
874 } else {
875 $result .= ' axisX: {showGrid: false,labelOffset: {x: -8,y: 0},type: Chartist.FixedScaleAxis, divisor:8, labelInterpolationFnc: function (value) {var shift=0;if(moment(value).isDST()){shift=3600000};return moment(value-shift).format("HH:00");}},';
876 }
877 $result .= ' axisY: {showGrid: true, labelInterpolationFnc: function (value) {return value.toString() + " ' . esc_html_x( 'MB', 'Abbreviation - Stands for "megabytes".', 'opcache-manager' ) . '";}},';
878 $result .= ' };';
879 $result .= ' new Chartist.Bar("#opcm-chart-buffer", buffer_data' . $uuid . ', buffer_option' . $uuid . ');';
880 $result .= '});';
881 $result .= '</script>';
882 $result .= '<div class="' . $style . '" id="opcm-chart-key">';
883 $result .= '</div>';
884 $result .= '<script>';
885 $result .= 'jQuery(function ($) {';
886 $result .= ' var key_data' . $uuid . ' = ' . $json_key . ';';
887 $result .= ' var key_tooltip' . $uuid . ' = Chartist.plugins.tooltip({justvalue: true, appendToBody: true});';
888 $result .= ' var key_option' . $uuid . ' = {';
889 $result .= ' height: 300,';
890 $result .= ' stackBars: true,';
891 $result .= ' stackMode: "accumulate",';
892 $result .= ' seriesBarDistance: 1,';
893 $result .= ' plugins: [key_tooltip' . $uuid . '],';
894 if ( 1 < $this->duration ) {
895 $result .= ' axisX: {showGrid: false, scaleMinSpace: 10, type: Chartist.FixedScaleAxis, divisor:' . ( $this->duration + 1 ) . ', labelInterpolationFnc: function skipLabels(value, index, labels) {return 0 === index % ' . $ticks . ' ? moment(value).format("DD") : null;}},';
896 } else {
897 $result .= ' axisX: {showGrid: false,labelOffset: {x: -8,y: 0},type: Chartist.FixedScaleAxis, divisor:8, labelInterpolationFnc: function (value) {var shift=0;if(moment(value).isDST()){shift=3600000};return moment(value-shift).format("HH:00");}},';
898 }
899 $result .= ' axisY: {showGrid: true, labelInterpolationFnc: function (value) {return value.toString() + " ' . esc_html_x( 'K', 'Abbreviation - Stands for "thousand".', 'opcache-manager' ) . '";}},';
900 $result .= ' };';
901 $result .= ' new Chartist.Bar("#opcm-chart-key", key_data' . $uuid . ', key_option' . $uuid . ');';
902 $result .= '});';
903 $result .= '</script>';
904 $result .= '<div class="opcm-multichart-item" id="opcm-chart-data">';
905 $result .= '</div>';
906 } else {
907 $result = '<div class="opcm-multichart-handler">';
908 $result .= '<div class="opcm-multichart-item active" id="opcm-chart-ratio">';
909 $result .= $this->get_graph_placeholder_nodata( 274 );
910 $result .= '</div>';
911 $result .= '<div class="opcm-multichart-item" id="opcm-chart-uptime">';
912 $result .= $this->get_graph_placeholder_nodata( 274 );
913 $result .= '</div>';
914 $result .= '<div class="opcm-multichart-item" id="opcm-chart-hit">';
915 $result .= $this->get_graph_placeholder_nodata( 274 );
916 $result .= '</div>';
917 $result .= '<div class="opcm-multichart-item" id="opcm-chart-string">';
918 $result .= $this->get_graph_placeholder_nodata( 274 );
919 $result .= '</div>';
920 $result .= '<div class="opcm-multichart-item" id="opcm-chart-file">';
921 $result .= $this->get_graph_placeholder_nodata( 274 );
922 $result .= '</div>';
923 $result .= '<div class="opcm-multichart-item" id="opcm-chart-memory">';
924 $result .= $this->get_graph_placeholder_nodata( 274 );
925 $result .= '</div>';
926 $result .= '<div class="opcm-multichart-item" id="opcm-chart-buffer">';
927 $result .= $this->get_graph_placeholder_nodata( 274 );
928 $result .= '</div>';
929 $result .= '<div class="opcm-multichart-item" id="opcm-chart-key">';
930 $result .= $this->get_graph_placeholder_nodata( 274 );
931 $result .= '</div>';
932 $result .= '<div class="opcm-multichart-item" id="opcm-chart-data">';
933 $result .= '</div>';
934 }
935 return [ 'opcm-main-chart' => $result ];
936 }
937
938 /**
939 * Query all kpis in statistics table.
940 *
941 * @param array $args Optional. The needed args.
942 * @return array The KPIs ready to send.
943 * @since 1.0.0
944 */
945 public static function get_status_kpi_collection( $args = [] ) {
946 $result['meta'] = [
947 'plugin' => OPCM_PRODUCT_NAME . ' ' . OPCM_VERSION,
948 'opcache' => OPcache::name(),
949 'period' => date( 'Y-m-d' ),
950 ];
951 $result['data'] = [];
952 $kpi = new static( date( 'Y-m-d' ), date( 'Y-m-d' ), false );
953 foreach ( [ 'ratio', 'memory', 'key', 'buffer', 'uptime', 'script' ] as $query ) {
954 $data = $kpi->query_kpi( $query, false );
955 switch ( $query ) {
956 case 'ratio':
957 $val = Conversion::number_shorten( $data['kpi-bottom-ratio'], 1, true );
958 $result['data']['hit'] = [
959 'name' => esc_html_x( 'Hits', 'Noun - Cache hit.', 'opcache-manager' ),
960 'short' => esc_html_x( 'Hits', 'Noun - Short (max 4 char) - Cache hit.', 'opcache-manager' ),
961 'description' => esc_html__( 'Successful calls to the cache.', 'opcache-manager' ),
962 'dimension' => 'none',
963 'ratio' => [
964 'raw' => round( $data['kpi-main-ratio'] / 100, 6 ),
965 'percent' => round( $data['kpi-main-ratio'] ?? 0, 2 ),
966 'permille' => round( $data['kpi-main-ratio'] * 10, 2 ),
967 ],
968 'variation' => [
969 'raw' => round( $data['kpi-index-ratio'] / 100, 6 ),
970 'percent' => round( $data['kpi-index-ratio'] ?? 0, 2 ),
971 'permille' => round( $data['kpi-index-ratio'] * 10, 2 ),
972 ],
973 'value' => [
974 'raw' => $data['kpi-bottom-ratio'],
975 'human' => $val['value'] . $val['abbreviation'],
976 ],
977 ];
978 break;
979 case 'memory':
980 $val = Conversion::data_shorten( $data['kpi-bottom-memory'], 0, true );
981 $result['data']['memory'] = [
982 'name' => esc_html_x( 'Total memory', 'Noun - Total memory available for allocation.', 'opcache-manager' ),
983 'short' => esc_html_x( 'Mem.', 'Noun - Short (max 4 char) - Total memory available for allocation.', 'opcache-manager' ),
984 'description' => esc_html__( 'Total memory available for OPcache.', 'opcache-manager' ),
985 'dimension' => 'memory',
986 'ratio' => [
987 'raw' => round( 1.0 - $data['kpi-main-memory'] / 100, 6 ),
988 'percent' => round( 100.0 - $data['kpi-main-memory'], 2 ),
989 'permille' => round( 1000.0 - $data['kpi-main-memory'] * 10, 2 ),
990 ],
991 'variation' => [
992 'raw' => round( $data['kpi-index-memory'] / 100, 6 ),
993 'percent' => round( $data['kpi-index-memory'] ?? 0, 2 ),
994 'permille' => round( $data['kpi-index-memory'] * 10, 2 ),
995 ],
996 'value' => [
997 'raw' => $data['kpi-bottom-memory'],
998 'human' => $val['value'] . $val['abbreviation'],
999 ],
1000 ];
1001 break;
1002 case 'script':
1003 $val = Conversion::number_shorten( $data['kpi-main-script'], 1, true );
1004 $result['data']['script'] = [
1005 'name' => esc_html_x( 'Scripts', 'Noun - Cached scripts.', 'opcache-manager' ),
1006 'short' => esc_html_x( 'Scr.', 'Noun - Short (max 4 char) - Cached scripts.', 'opcache-manager' ),
1007 'description' => esc_html__( 'Scripts currently present in cache.', 'opcache-manager' ),
1008 'dimension' => 'none',
1009 'ratio' => null,
1010 'variation' => [
1011 'raw' => - round( $data['kpi-index-script'] / 100, 6 ),
1012 'percent' => - round( $data['kpi-index-script'] ?? 0, 2 ),
1013 'permille' => - round( $data['kpi-index-script'] * 10, 2 ),
1014 ],
1015 'value' => [
1016 'raw' => $data['kpi-main-script'],
1017 'human' => $val['value'] . $val['abbreviation'],
1018 ],
1019 ];
1020 break;
1021 case 'key':
1022 $val = Conversion::number_shorten( $data['kpi-bottom-key'], 0, true );
1023 $result['data']['key'] = [
1024 'name' => esc_html_x( 'Keys', 'Noun - Allocated keys.', 'opcache-manager' ),
1025 'short' => esc_html_x( 'Keys', 'Noun - Short (max 4 char) - Allocated keys.', 'opcache-manager' ),
1026 'description' => esc_html__( 'Keys allocated by OPcache.', 'opcache-manager' ),
1027 'dimension' => 'none',
1028 'ratio' => [
1029 'raw' => round( $data['kpi-main-key'] / 100, 6 ),
1030 'percent' => round( $data['kpi-main-key'] ?? 0, 2 ),
1031 'permille' => round( $data['kpi-main-key'] * 10, 2 ),
1032 ],
1033 'variation' => [
1034 'raw' => round( $data['kpi-index-key'] / 100, 6 ),
1035 'percent' => round( $data['kpi-index-key'] ?? 0, 2 ),
1036 'permille' => round( $data['kpi-index-key'] * 10, 2 ),
1037 ],
1038 'value' => [
1039 'raw' => $data['kpi-bottom-key'],
1040 'human' => $val['value'] . $val['abbreviation'],
1041 ],
1042 ];
1043 break;
1044 case 'buffer':
1045 $val = Conversion::data_shorten( $data['kpi-bottom-buffer'], 0, true );
1046 $result['data']['buffer'] = [
1047 'name' => esc_html_x( 'Buffer', 'Noun - Buffer.', 'opcache-manager' ),
1048 'short' => esc_html_x( 'Buf.', 'Noun - Short (max 4 char) - Buffer.', 'opcache-manager' ),
1049 'description' => esc_html__( 'Buffer size.', 'opcache-manager' ),
1050 'dimension' => 'memory',
1051 'ratio' => [
1052 'raw' => round( $data['kpi-main-buffer'] / 100, 6 ),
1053 'percent' => round( $data['kpi-main-buffer'] ?? 0, 2 ),
1054 'permille' => round( $data['kpi-main-buffer'] * 10, 2 ),
1055 ],
1056 'variation' => [
1057 'raw' => round( $data['kpi-index-buffer'] / 100, 6 ),
1058 'percent' => round( $data['kpi-index-buffer'] ?? 0, 2 ),
1059 'permille' => round( $data['kpi-index-buffer'] * 10, 2 ),
1060 ],
1061 'value' => [
1062 'raw' => $data['kpi-bottom-buffer'],
1063 'human' => $val['value'] . $val['abbreviation'],
1064 ],
1065 ];
1066 break;
1067 case 'uptime':
1068 $result['data']['uptime'] = [
1069 'name' => esc_html_x( 'Availability', 'Noun - Extrapolated availability time over 24 hours.', 'opcache-manager' ),
1070 'short' => esc_html_x( 'Avl.', 'Noun - Short (max 4 char) - Extrapolated availability time over 24 hours.', 'opcache-manager' ),
1071 'description' => esc_html__( 'Extrapolated availability time over 24 hours.', 'opcache-manager' ),
1072 'dimension' => 'time',
1073 'ratio' => [
1074 'raw' => round( $data['kpi-main-uptime'] / 100, 6 ),
1075 'percent' => round( $data['kpi-main-uptime'] ?? 0, 2 ),
1076 'permille' => round( $data['kpi-main-uptime'] * 10, 2 ),
1077 ],
1078 'variation' => [
1079 'raw' => round( $data['kpi-index-uptime'] / 100, 6 ),
1080 'percent' => round( $data['kpi-index-uptime'] ?? 0, 2 ),
1081 'permille' => round( $data['kpi-index-uptime'] * 10, 2 ),
1082 ],
1083 'value' => [
1084 'raw' => $data['kpi-bottom-uptime'],
1085 'human' => implode( ', ', Date::get_age_array_from_seconds( $data['kpi-bottom-uptime'], true, true ) ),
1086 ],
1087 ];
1088 break;
1089 }
1090 }
1091 $result['assets'] = [];
1092 return $result;
1093 }
1094
1095 /**
1096 * Query statistics table.
1097 *
1098 * @param mixed $queried The query params.
1099 * @param boolean $chart Optional, return the chart if true, only the data if false;
1100 * @return array The result of the query, ready to encode.
1101 * @since 1.0.0
1102 */
1103 public function query_kpi( $queried, $chart = true ) {
1104 $result = [];
1105 if ( 'ratio' === $queried || 'memory' === $queried || 'key' === $queried || 'buffer' === $queried || 'uptime' === $queried ) {
1106 $data = Schema::get_std_kpi( $this->filter, ! $this->is_today );
1107 $pdata = Schema::get_std_kpi( $this->previous );
1108 $base_value = 0.0;
1109 $pbase_value = 0.0;
1110 $data_value = 0.0;
1111 $pdata_value = 0.0;
1112 $current = 0.0;
1113 $previous = 0.0;
1114 if ( 'uptime' === $queried ) {
1115 $disabled_data = Schema::get_std_kpi( $this->filter, ! $this->is_today, 'status', [ 'disabled' ] );
1116 $disabled_pdata = Schema::get_std_kpi( $this->previous, true, 'status', [ 'disabled' ] );
1117 if ( is_array( $data ) && array_key_exists( 'records', $data ) && is_array( $disabled_data ) && array_key_exists( 'records', $disabled_data ) ) {
1118 if ( empty( $data['records'] ) ) {
1119 $data['records'] = 0;
1120 }
1121 if ( ! is_array( $disabled_data ) || ! array_key_exists( 'records', $disabled_data ) ) {
1122 $disabled_data['records'] = 0;
1123 }
1124 $base_value = (float) $data['records'] + $disabled_data['records'];
1125 $data_value = (float) $data['records'];
1126 }
1127 if ( is_array( $pdata ) && array_key_exists( 'records', $pdata ) && is_array( $disabled_pdata ) && array_key_exists( 'records', $disabled_pdata ) ) {
1128 if ( empty( $pdata['records'] ) ) {
1129 $pdata['records'] = 0;
1130 }
1131 if ( ! is_array( $disabled_pdata ) || ! array_key_exists( 'records', $disabled_pdata ) ) {
1132 $disabled_pdata['records'] = 0;
1133 }
1134 $pbase_value = (float) $pdata['records'] + $disabled_pdata['records'];
1135 $pdata_value = (float) $pdata['records'];
1136 }
1137 }
1138 if ( 'ratio' === $queried ) {
1139 if ( is_array( $data ) && array_key_exists( 'avg_hit', $data ) && ! empty( $data['avg_hit'] ) && array_key_exists( 'avg_miss', $data ) && ! empty( $data['avg_miss'] ) ) {
1140 $base_value = (float) $data['avg_hit'] + (float) $data['avg_miss'];
1141 $data_value = (float) $data['avg_hit'];
1142 }
1143 if ( is_array( $pdata ) && array_key_exists( 'avg_hit', $pdata ) && ! empty( $pdata['avg_hit'] ) && array_key_exists( 'avg_miss', $pdata ) && ! empty( $pdata['avg_miss'] ) ) {
1144 $pbase_value = (float) $pdata['avg_hit'] + (float) $pdata['avg_miss'];
1145 $pdata_value = (float) $pdata['avg_hit'];
1146 }
1147 }
1148 if ( 'key' === $queried ) {
1149 if ( is_array( $data ) && array_key_exists( 'avg_key_used', $data ) && ! empty( $data['avg_key_used'] ) && array_key_exists( 'avg_key_total', $data ) && ! empty( $data['avg_key_total'] ) ) {
1150 $base_value = (float) $data['avg_key_total'];
1151 $data_value = (float) $data['avg_key_used'];
1152 }
1153 if ( is_array( $pdata ) && array_key_exists( 'avg_key_used', $pdata ) && ! empty( $pdata['avg_key_used'] ) && array_key_exists( 'avg_key_total', $pdata ) && ! empty( $pdata['avg_key_total'] ) ) {
1154 $pbase_value = (float) $pdata['avg_key_total'];
1155 $pdata_value = (float) $pdata['avg_key_used'];
1156 }
1157 }
1158 if ( 'buffer' === $queried ) {
1159 if ( is_array( $data ) && array_key_exists( 'avg_buf_used', $data ) && ! empty( $data['avg_buf_used'] ) && array_key_exists( 'avg_buf_total', $data ) && ! empty( $data['avg_buf_total'] ) ) {
1160 $base_value = (float) $data['avg_buf_total'];
1161 $data_value = (float) $data['avg_buf_used'];
1162 }
1163 if ( is_array( $pdata ) && array_key_exists( 'avg_buf_used', $pdata ) && ! empty( $pdata['avg_buf_used'] ) && array_key_exists( 'avg_buf_total', $pdata ) && ! empty( $pdata['avg_buf_total'] ) ) {
1164 $pbase_value = (float) $pdata['avg_buf_total'];
1165 $pdata_value = (float) $pdata['avg_buf_used'];
1166 }
1167 }
1168 if ( 'memory' === $queried ) {
1169 if ( is_array( $data ) && array_key_exists( 'avg_mem_total', $data ) && ! empty( $data['avg_mem_total'] ) && array_key_exists( 'avg_mem_used', $data ) && ! empty( $data['avg_mem_used'] ) && array_key_exists( 'avg_mem_wasted', $data ) && ! empty( $data['avg_mem_wasted'] ) ) {
1170 $base_value = (float) $data['avg_mem_total'];
1171 $data_value = (float) $data['avg_mem_total'] - (float) $data['avg_mem_used'] - (float) $data['avg_mem_wasted'];
1172 }
1173 if ( is_array( $pdata ) && array_key_exists( 'avg_mem_total', $pdata ) && ! empty( $pdata['avg_mem_total'] ) && array_key_exists( 'avg_mem_used', $pdata ) && ! empty( $pdata['avg_mem_used'] ) && array_key_exists( 'avg_mem_wasted', $pdata ) && ! empty( $pdata['avg_mem_wasted'] ) ) {
1174 $pbase_value = (float) $pdata['avg_mem_total'];
1175 $pdata_value = (float) $pdata['avg_mem_total'] - (float) $pdata['avg_mem_used'] - (float) $pdata['avg_mem_wasted'];
1176 }
1177 }
1178 if ( 0.0 !== $base_value && 0.0 !== $data_value ) {
1179 $current = 100 * $data_value / $base_value;
1180 $result[ 'kpi-main-' . $queried ] = round( $current, $chart ? 1 : 4 );
1181 } else {
1182 if ( 0.0 !== $data_value ) {
1183 $result[ 'kpi-main-' . $queried ] = 100;
1184 } elseif ( 0.0 !== $base_value ) {
1185 $result[ 'kpi-main-' . $queried ] = 0;
1186 } else {
1187 $result[ 'kpi-main-' . $queried ] = null;
1188 }
1189 }
1190 if ( 0.0 !== $pbase_value && 0.0 !== $pdata_value ) {
1191 $previous = 100 * $pdata_value / $pbase_value;
1192 } else {
1193 if ( 0.0 !== $pdata_value ) {
1194 $previous = 100.0;
1195 }
1196 }
1197 if ( 0.0 !== $current && 0.0 !== $previous ) {
1198 $result[ 'kpi-index-' . $queried ] = round( 100 * ( $current - $previous ) / $previous, 4 );
1199 } else {
1200 $result[ 'kpi-index-' . $queried ] = null;
1201 }
1202 if ( ! $chart ) {
1203 $result[ 'kpi-bottom-' . $queried ] = null;
1204 switch ( $queried ) {
1205 case 'ratio':
1206 if ( is_array( $data ) && array_key_exists( 'sum_hit', $data ) ) {
1207 $result[ 'kpi-bottom-' . $queried ] = (int) $data['sum_hit'];
1208 }
1209 break;
1210 case 'memory':
1211 case 'buffer':
1212 $result[ 'kpi-bottom-' . $queried ] = (int) round( $base_value, 0 );
1213 /*break;
1214
1215 if ( is_array( $data ) && array_key_exists( 'avg_frag_count', $data ) ) {
1216 $result[ 'kpi-bottom-' . $queried ] = (int) round( $data['avg_frag_count'], 0 );
1217 }*/
1218 break;
1219 case 'key':
1220 $result[ 'kpi-bottom-' . $queried ] = (int) round( $data_value, 0 );
1221 break;
1222 case 'uptime':
1223 if ( 0.0 !== $base_value ) {
1224 $result[ 'kpi-bottom-' . $queried ] = (int) round( $this->duration * DAY_IN_SECONDS * ( $data_value / $base_value ) );
1225 }
1226 break;
1227 }
1228 return $result;
1229 }
1230 if ( isset( $result[ 'kpi-main-' . $queried ] ) ) {
1231 $result[ 'kpi-main-' . $queried ] = $result[ 'kpi-main-' . $queried ] . '&nbsp;%';
1232 } else {
1233 $result[ 'kpi-main-' . $queried ] = '-';
1234 }
1235 if ( 0.0 !== $current && 0.0 !== $previous ) {
1236 $percent = round( 100 * ( $current - $previous ) / $previous, 1 );
1237 if ( 0.1 > abs( $percent ) ) {
1238 $percent = 0;
1239 }
1240 $result[ 'kpi-index-' . $queried ] = '<span style="color:' . ( 0 <= $percent ? '#18BB9C' : '#E74C3C' ) . ';">' . ( 0 < $percent ? '+' : '' ) . $percent . '&nbsp;%</span>';
1241 } elseif ( 0.0 === $previous && 0.0 !== $current ) {
1242 $result[ 'kpi-index-' . $queried ] = '<span style="color:#18BB9C;">+∞</span>';
1243 } elseif ( 0.0 !== $previous && 100 !== $previous && 0.0 === $current ) {
1244 $result[ 'kpi-index-' . $queried ] = '<span style="color:#E74C3C;">-∞</span>';
1245 }
1246 switch ( $queried ) {
1247 case 'ratio':
1248 if ( is_array( $data ) && array_key_exists( 'sum_hit', $data ) ) {
1249 $result[ 'kpi-bottom-' . $queried ] = '<span class="opcm-kpi-large-bottom-text">' . sprintf( esc_html__( '%s hits', 'opcache-manager' ), Conversion::number_shorten( $data['sum_hit'], 2, false, '&nbsp;' ) ) . '</span>';
1250 }
1251 break;
1252 case 'memory':
1253 $result[ 'kpi-bottom-' . $queried ] = '<span class="opcm-kpi-large-bottom-text">' . sprintf( esc_html__( 'total memory: %s', 'opcache-manager' ), Conversion::data_shorten( $base_value, 0, false, '&nbsp;' ) ) . '</span>';
1254 break;
1255 case 'buffer':
1256 $result[ 'kpi-bottom-' . $queried ] = '<span class="opcm-kpi-large-bottom-text">' . sprintf( esc_html__( 'buffer size: %s', 'opcache-manager' ), Conversion::data_shorten( $base_value, 0, false, '&nbsp;' ) ) . '</span>';
1257 break;
1258 case 'key':
1259 $result[ 'kpi-bottom-' . $queried ] = '<span class="opcm-kpi-large-bottom-text">' . sprintf( esc_html__( '%s keys (avg.)', 'opcache-manager' ), (int) round( $data_value, 0 ) ) . '</span>';
1260 break;
1261 case 'uptime':
1262 if ( 0.0 !== $base_value ) {
1263 $duration = implode( ', ', Date::get_age_array_from_seconds( $this->duration * DAY_IN_SECONDS * ( $data_value / $base_value ), true, true ) );
1264 if ( '' === $duration ) {
1265 $duration = esc_html__( 'no availability', 'opcache-manager' );
1266 } else {
1267 $duration = sprintf( esc_html__( 'available %s', 'opcache-manager' ), $duration );
1268 }
1269 $result[ 'kpi-bottom-' . $queried ] = '<span class="opcm-kpi-large-bottom-text">' . $duration . '</span>';
1270 }
1271 break;
1272 }
1273 }
1274 if ( 'script' === $queried ) {
1275 $data = Schema::get_std_kpi( $this->filter, ! $this->is_today );
1276 $pdata = Schema::get_std_kpi( $this->previous );
1277 $current = 0.0;
1278 $previous = 0.0;
1279 if ( is_array( $data ) && array_key_exists( 'avg_scripts', $data ) && ! empty( $data['avg_scripts'] ) ) {
1280 $current = (float) $data['avg_scripts'];
1281 }
1282 if ( is_array( $pdata ) && array_key_exists( 'avg_scripts', $pdata ) && ! empty( $pdata['avg_scripts'] ) ) {
1283 $previous = (float) $pdata['avg_scripts'];
1284 }
1285 $result[ 'kpi-main-' . $queried ] = (int) round( $current, 0 );
1286 if ( ! $chart ) {
1287 if ( 0.0 !== $current && 0.0 !== $previous ) {
1288 $result[ 'kpi-index-' . $queried ] = round( 100 * ( $current - $previous ) / $previous, 4 );
1289 } else {
1290 $result[ 'kpi-index-' . $queried ] = null;
1291 }
1292 $result[ 'kpi-bottom-' . $queried ] = null;
1293 return $result;
1294 }
1295 if ( 0.0 !== $current && 0.0 !== $previous ) {
1296 $percent = round( 100 * ( $current - $previous ) / $previous, 1 );
1297 if ( 0.1 > abs( $percent ) ) {
1298 $percent = 0;
1299 }
1300 $result[ 'kpi-index-' . $queried ] = '<span style="color:' . ( 0 <= $percent ? '#18BB9C' : '#E74C3C' ) . ';">' . ( 0 < $percent ? '+' : '' ) . $percent . '&nbsp;%</span>';
1301 } elseif ( 0.0 === $previous && 0.0 !== $current ) {
1302 $result[ 'kpi-index-' . $queried ] = '<span style="color:#18BB9C;">+∞</span>';
1303 } elseif ( 0.0 !== $previous && 100 !== $previous && 0.0 === $current ) {
1304 $result[ 'kpi-index-' . $queried ] = '<span style="color:#E74C3C;">-∞</span>';
1305 }
1306 if ( is_array( $data ) && array_key_exists( 'min_scripts', $data ) && array_key_exists( 'max_scripts', $data ) ) {
1307 if ( empty( $data['min_scripts'] ) ) {
1308 $data['min_scripts'] = 0;
1309 }
1310 if ( empty( $data['max_scripts'] ) ) {
1311 $data['max_scripts'] = 0;
1312 }
1313 $result[ 'kpi-bottom-' . $queried ] = '<span class="opcm-kpi-large-bottom-text">' . (int) round( $data['min_scripts'], 0 ) . '&nbsp;<img style="width:12px;vertical-align:middle;" src="' . Feather\Icons::get_base64( 'arrow-right', 'none', '#73879C' ) . '" />&nbsp;' . (int) round( $data['max_scripts'], 0 ) . '&nbsp;</span>';
1314 }
1315 }
1316 return $result;
1317 }
1318
1319 /**
1320 * Get the title bar.
1321 *
1322 * @return string The bar ready to print.
1323 * @since 1.0.0
1324 */
1325 public function get_title_bar() {
1326 $result = '<div class="opcm-box opcm-box-full-line">';
1327 $result .= '<span class="opcm-title">' . esc_html__( 'OPcache Analytics', 'opcache-manager' ) . '</span>';
1328 $result .= '<span class="opcm-subtitle">' . OPcache::name() . '</span>';
1329 $result .= '<span class="opcm-datepicker">' . $this->get_date_box() . '</span>';
1330 $result .= '</div>';
1331 return $result;
1332 }
1333
1334 /**
1335 * Get the KPI bar.
1336 *
1337 * @return string The bar ready to print.
1338 * @since 1.0.0
1339 */
1340 public function get_kpi_bar() {
1341 $result = '<div class="opcm-box opcm-box-full-line">';
1342 $result .= '<div class="opcm-kpi-bar">';
1343 $result .= '<div class="opcm-kpi-large">' . $this->get_large_kpi( 'ratio' ) . '</div>';
1344 $result .= '<div class="opcm-kpi-large">' . $this->get_large_kpi( 'memory' ) . '</div>';
1345 $result .= '<div class="opcm-kpi-large">' . $this->get_large_kpi( 'script' ) . '</div>';
1346 $result .= '<div class="opcm-kpi-large">' . $this->get_large_kpi( 'key' ) . '</div>';
1347 $result .= '<div class="opcm-kpi-large">' . $this->get_large_kpi( 'buffer' ) . '</div>';
1348 $result .= '<div class="opcm-kpi-large">' . $this->get_large_kpi( 'uptime' ) . '</div>';
1349 $result .= '</div>';
1350 $result .= '</div>';
1351 return $result;
1352 }
1353
1354 /**
1355 * Get the main chart.
1356 *
1357 * @return string The main chart ready to print.
1358 * @since 1.0.0
1359 */
1360 public function get_main_chart() {
1361 $help_ratio = esc_html__( 'Hit ratio variation.', 'opcache-manager' );
1362 $help_hit = esc_html__( 'Hit and miss distribution.', 'opcache-manager' );
1363 $help_memory = esc_html__( 'Memory distribution.', 'opcache-manager' );
1364 $help_file = esc_html__( 'Files variation.', 'opcache-manager' );
1365 $help_key = esc_html__( 'Keys distribution.', 'opcache-manager' );
1366 $help_string = esc_html__( 'Strings variation.', 'opcache-manager' );
1367 $help_buffer = esc_html__( 'Buffer distribution.', 'opcache-manager' );
1368 $help_uptime = esc_html__( 'Availability variation.', 'opcache-manager' );
1369 $detail = '<span class="opcm-chart-button not-ready left" id="opcm-chart-button-ratio" data-position="left" data-tooltip="' . $help_ratio . '"><img style="width:12px;vertical-align:baseline;" src="' . Feather\Icons::get_base64( 'award', 'none', '#73879C' ) . '" /></span>';
1370 $detail .= '&nbsp;&nbsp;&nbsp;<span class="opcm-chart-button not-ready left" id="opcm-chart-button-hit" data-position="left" data-tooltip="' . $help_hit . '"><img style="width:12px;vertical-align:baseline;" src="' . Feather\Icons::get_base64( 'hash', 'none', '#73879C' ) . '" /></span>';
1371 $detail .= '&nbsp;&nbsp;&nbsp;<span class="opcm-chart-button not-ready left" id="opcm-chart-button-memory" data-position="left" data-tooltip="' . $help_memory . '"><img style="width:12px;vertical-align:baseline;" src="' . Feather\Icons::get_base64( 'cpu', 'none', '#73879C' ) . '" /></span>';
1372 $detail .= '&nbsp;&nbsp;&nbsp;<span class="opcm-chart-button not-ready left" id="opcm-chart-button-file" data-position="left" data-tooltip="' . $help_file . '"><img style="width:12px;vertical-align:baseline;" src="' . Feather\Icons::get_base64( 'file-text', 'none', '#73879C' ) . '" /></span>';
1373 $detail .= '&nbsp;&nbsp;&nbsp;<span class="opcm-chart-button not-ready left" id="opcm-chart-button-key" data-position="left" data-tooltip="' . $help_key . '"><img style="width:12px;vertical-align:baseline;" src="' . Feather\Icons::get_base64( 'key', 'none', '#73879C' ) . '" /></span>';
1374 $detail .= '&nbsp;&nbsp;&nbsp;<span class="opcm-chart-button not-ready left" id="opcm-chart-button-string" data-position="left" data-tooltip="' . $help_string . '"><img style="width:12px;vertical-align:baseline;" src="' . Feather\Icons::get_base64( 'tag', 'none', '#73879C' ) . '" /></span>';
1375 $detail .= '&nbsp;&nbsp;&nbsp;<span class="opcm-chart-button not-ready left" id="opcm-chart-button-buffer" data-position="left" data-tooltip="' . $help_buffer . '"><img style="width:12px;vertical-align:baseline;" src="' . Feather\Icons::get_base64( 'database', 'none', '#73879C' ) . '" /></span>';
1376 $detail .= '&nbsp;&nbsp;&nbsp;<span class="opcm-chart-button not-ready left" id="opcm-chart-button-uptime" data-position="left" data-tooltip="' . $help_uptime . '"><img style="width:12px;vertical-align:baseline;" src="' . Feather\Icons::get_base64( 'activity', 'none', '#73879C' ) . '" /></span>';
1377 $result = '<div class="opcm-row">';
1378 $result .= '<div class="opcm-box opcm-box-full-line">';
1379 $result .= '<div class="opcm-module-title-bar"><span class="opcm-module-title">' . esc_html__( 'Metrics Variations', 'opcache-manager' ) . '<span class="opcm-module-more">' . $detail . '</span></span></div>';
1380 $result .= '<div class="opcm-module-content" id="opcm-main-chart">' . $this->get_graph_placeholder( 274 ) . '</div>';
1381 $result .= '</div>';
1382 $result .= '</div>';
1383 $result .= $this->get_refresh_script(
1384 [
1385 'query' => 'main-chart',
1386 'queried' => 0,
1387 ]
1388 );
1389 return $result;
1390 }
1391
1392 /**
1393 * Get the domains list.
1394 *
1395 * @return string The table ready to print.
1396 * @since 1.0.0
1397 */
1398 public function get_events_list() {
1399 $result = '<div class="opcm-box opcm-box-full-line">';
1400 $result .= '<div class="opcm-module-title-bar"><span class="opcm-module-title">' . esc_html__( 'Status Events', 'opcache-manager' ) . '</span></div>';
1401 $result .= '<div class="opcm-module-content" id="opcm-events">' . $this->get_graph_placeholder( 200 ) . '</div>';
1402 $result .= '</div>';
1403 $result .= $this->get_refresh_script(
1404 [
1405 'query' => 'events',
1406 'queried' => 0,
1407 ]
1408 );
1409 return $result;
1410 }
1411
1412 /**
1413 * Get a large kpi box.
1414 *
1415 * @param string $kpi The kpi to render.
1416 * @return string The box ready to print.
1417 * @since 1.0.0
1418 */
1419 private function get_large_kpi( $kpi ) {
1420 switch ( $kpi ) {
1421 case 'ratio':
1422 $icon = Feather\Icons::get_base64( 'award', 'none', '#73879C' );
1423 $title = esc_html_x( 'Hit Ratio', 'Noun - Cache hit ratio.', 'opcache-manager' );
1424 $help = esc_html__( 'The ratio between hit and total calls.', 'opcache-manager' );
1425 break;
1426 case 'memory':
1427 $icon = Feather\Icons::get_base64( 'cpu', 'none', '#73879C' );
1428 $title = esc_html_x( 'Free Memory', 'Noun - Memory free of allocation.', 'opcache-manager' );
1429 $help = esc_html__( 'Ratio of free available memory.', 'opcache-manager' );
1430 break;
1431 case 'script':
1432 $icon = Feather\Icons::get_base64( 'file-text', 'none', '#73879C' );
1433 $title = esc_html_x( 'Cached Files', 'Noun - Number of already cached files.', 'opcache-manager' );
1434 $help = esc_html__( 'Number of compiled and cached files.', 'opcache-manager' );
1435 break;
1436 case 'key':
1437 $icon = Feather\Icons::get_base64( 'key', 'none', '#73879C' );
1438 $title = esc_html_x( 'Keys Saturation', 'Noun - Ratio of the allocated keys to the total available keys slots.', 'opcache-manager' );
1439 $help = esc_html__( 'Ratio of the allocated keys to the total available keys slots.', 'opcache-manager' );
1440 break;
1441 case 'buffer':
1442 $icon = Feather\Icons::get_base64( 'database', 'none', '#73879C' );
1443 $title = esc_html_x( 'Buffer Saturation', 'Noun - Ratio of the used buffer to the total buffer size.', 'opcache-manager' );
1444 $help = esc_html__( 'Ratio of the used buffer to the total buffer size.', 'opcache-manager' );
1445 break;
1446 case 'uptime':
1447 $icon = Feather\Icons::get_base64( 'activity', 'none', '#73879C' );
1448 $title = esc_html_x( 'Availability', 'Noun - Ratio of time when OPcache is not disabled.', 'opcache-manager' );
1449 $help = esc_html__( 'Time ratio with an operational OPcache.', 'opcache-manager' );
1450 break;
1451 }
1452 $top = '<img style="width:12px;vertical-align:baseline;" src="' . $icon . '" />&nbsp;&nbsp;<span style="cursor:help;" class="opcm-kpi-large-top-text bottom" data-position="bottom" data-tooltip="' . $help . '">' . $title . '</span>';
1453 $indicator = '&nbsp;';
1454 $bottom = '<span class="opcm-kpi-large-bottom-text">&nbsp;</span>';
1455 $result = '<div class="opcm-kpi-large-top">' . $top . '</div>';
1456 $result .= '<div class="opcm-kpi-large-middle"><div class="opcm-kpi-large-middle-left" id="kpi-main-' . $kpi . '">' . $this->get_value_placeholder() . '</div><div class="opcm-kpi-large-middle-right" id="kpi-index-' . $kpi . '">' . $indicator . '</div></div>';
1457 $result .= '<div class="opcm-kpi-large-bottom" id="kpi-bottom-' . $kpi . '">' . $bottom . '</div>';
1458 $result .= $this->get_refresh_script(
1459 [
1460 'query' => 'kpi',
1461 'queried' => $kpi,
1462 ]
1463 );
1464 return $result;
1465 }
1466
1467 /**
1468 * Get a placeholder for graph.
1469 *
1470 * @param integer $height The height of the placeholder.
1471 * @return string The placeholder, ready to print.
1472 * @since 1.0.0
1473 */
1474 private function get_graph_placeholder( $height ) {
1475 return '<p style="text-align:center;line-height:' . $height . 'px;"><img style="width:40px;vertical-align:middle;" src="' . OPCM_ADMIN_URL . 'medias/bars.svg" /></p>';
1476 }
1477
1478 /**
1479 * Get a placeholder for graph with no data.
1480 *
1481 * @param integer $height The height of the placeholder.
1482 * @return string The placeholder, ready to print.
1483 * @since 1.0.0
1484 */
1485 private function get_graph_placeholder_nodata( $height ) {
1486 return '<p style="color:#73879C;text-align:center;line-height:' . $height . 'px;">' . esc_html__( 'No Data', 'opcache-manager' ) . '</p>';
1487 }
1488
1489 /**
1490 * Get a placeholder for value.
1491 *
1492 * @return string The placeholder, ready to print.
1493 * @since 1.0.0
1494 */
1495 private function get_value_placeholder() {
1496 return '<img style="width:26px;vertical-align:middle;" src="' . OPCM_ADMIN_URL . 'medias/three-dots.svg" />';
1497 }
1498
1499 /**
1500 * Get refresh script.
1501 *
1502 * @param array $args Optional. The args for the ajax call.
1503 * @return string The script, ready to print.
1504 * @since 1.0.0
1505 */
1506 private function get_refresh_script( $args = [] ) {
1507 $result = '<script>';
1508 $result .= 'jQuery(document).ready( function($) {';
1509 $result .= ' var data = {';
1510 $result .= ' action:"opcm_get_stats",';
1511 $result .= ' nonce:"' . wp_create_nonce( 'ajax_opcm' ) . '",';
1512 foreach ( $args as $key => $val ) {
1513 $s = ' ' . $key . ':';
1514 if ( is_string( $val ) ) {
1515 $s .= '"' . $val . '"';
1516 } elseif ( is_numeric( $val ) ) {
1517 $s .= $val;
1518 } elseif ( is_bool( $val ) ) {
1519 $s .= $val ? 'true' : 'false';
1520 }
1521 $result .= $s . ',';
1522 }
1523 $result .= ' start:"' . $this->start . '",';
1524 $result .= ' end:"' . $this->end . '",';
1525 $result .= ' };';
1526 $result .= ' $.post(ajaxurl, data, function(response) {';
1527 $result .= ' var val = JSON.parse(response);';
1528 $result .= ' $.each(val, function(index, value) {$("#" + index).html(value);});';
1529 if ( array_key_exists( 'query', $args ) && 'main-chart' === $args['query'] ) {
1530 $result .= '$(".opcm-chart-button").removeClass("not-ready");';
1531 $result .= '$("#opcm-chart-button-ratio").addClass("active");';
1532 }
1533 $result .= ' });';
1534 $result .= '});';
1535 $result .= '</script>';
1536 return $result;
1537 }
1538
1539 /**
1540 * Get the url.
1541 *
1542 * @param array $exclude Optional. The args to exclude.
1543 * @param array $replace Optional. The args to replace or add.
1544 * @return string The url.
1545 * @since 1.0.0
1546 */
1547 private function get_url( $exclude = [], $replace = [] ) {
1548 $params = [];
1549 $params['start'] = $this->start;
1550 $params['end'] = $this->end;
1551 foreach ( $exclude as $arg ) {
1552 unset( $params[ $arg ] );
1553 }
1554 foreach ( $replace as $key => $arg ) {
1555 $params[ $key ] = $arg;
1556 }
1557 $url = admin_url( 'admin.php?page=opcm-viewer' );
1558 foreach ( $params as $key => $arg ) {
1559 if ( '' !== $arg ) {
1560 $url .= '&' . $key . '=' . $arg;
1561 }
1562 }
1563 return $url;
1564 }
1565
1566 /**
1567 * Get a date picker box.
1568 *
1569 * @return string The box ready to print.
1570 * @since 1.0.0
1571 */
1572 private function get_date_box() {
1573 $result = '<img style="width:13px;vertical-align:middle;" src="' . Feather\Icons::get_base64( 'calendar', 'none', '#5A738E' ) . '" />&nbsp;&nbsp;<span class="opcm-datepicker-value"></span>';
1574 $result .= '<script>';
1575 $result .= 'jQuery(function ($) {';
1576 $result .= ' moment.locale("' . L10n::get_display_locale() . '");';
1577 $result .= ' var start = moment("' . $this->start . '");';
1578 $result .= ' var end = moment("' . $this->end . '");';
1579 $result .= ' function changeDate(start, end) {';
1580 $result .= ' $("span.opcm-datepicker-value").html(start.format("LL") + " - " + end.format("LL"));';
1581 $result .= ' }';
1582 $result .= ' $(".opcm-datepicker").daterangepicker({';
1583 $result .= ' opens: "left",';
1584 $result .= ' startDate: start,';
1585 $result .= ' endDate: end,';
1586 $result .= ' minDate: moment("' . Schema::get_oldest_date() . '"),';
1587 $result .= ' maxDate: moment(),';
1588 $result .= ' showCustomRangeLabel: true,';
1589 $result .= ' alwaysShowCalendars: true,';
1590 $result .= ' locale: {customRangeLabel: "' . esc_html__( 'Custom Range', 'opcache-manager' ) . '",cancelLabel: "' . esc_html__( 'Cancel', 'opcache-manager' ) . '", applyLabel: "' . esc_html__( 'Apply', 'opcache-manager' ) . '"},';
1591 $result .= ' ranges: {';
1592 $result .= ' "' . esc_html__( 'Today', 'opcache-manager' ) . '": [moment(), moment()],';
1593 $result .= ' "' . esc_html__( 'Yesterday', 'opcache-manager' ) . '": [moment().subtract(1, "days"), moment().subtract(1, "days")],';
1594 $result .= ' "' . esc_html__( 'This Month', 'opcache-manager' ) . '": [moment().startOf("month"), moment().endOf("month")],';
1595 $result .= ' "' . esc_html__( 'Last Month', 'opcache-manager' ) . '": [moment().subtract(1, "month").startOf("month"), moment().subtract(1, "month").endOf("month")],';
1596 $result .= ' }';
1597 $result .= ' }, changeDate);';
1598 $result .= ' changeDate(start, end);';
1599 $result .= ' $(".opcm-datepicker").on("apply.daterangepicker", function(ev, picker) {';
1600 $result .= ' var url = "' . $this->get_url( [ 'start', 'end' ] ) . '" + "&start=" + picker.startDate.format("YYYY-MM-DD") + "&end=" + picker.endDate.format("YYYY-MM-DD");';
1601 $result .= ' $(location).attr("href", url);';
1602 $result .= ' });';
1603 $result .= '});';
1604 $result .= '</script>';
1605 return $result;
1606 }
1607
1608 }
1609