PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.5.3
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.5.3
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / includes / class-wp-optimize-system-status-report.php

class-wp-optimize-system-status-report.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 4.5.3, at includes/class-wp-optimize-system-status-report.php

441 lines 12.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) die('No direct access allowed');
3
4 if (!class_exists('WP_Optimize_System_Status_Report')) :
5
6 class WP_Optimize_System_Status_Report {
7
8 /**
9 * @var WP_Optimize_Server_Information
10 */
11 private $server_info_util;
12
13 /**
14 * @var array
15 */
16 private $replaceable_md_tags = array();
17
18 /**
19 * Constructor
20 */
21 private function __construct() {
22 $this->server_info_util = new WP_Optimize_Server_Information();
23 }
24
25 /**
26 * Returns singleton instance
27 *
28 * @return WP_Optimize_System_Status_Report
29 */
30 public static function get_instance() {
31 static $_instance = null;
32 if (null === $_instance) {
33 $_instance = new self();
34 }
35 return $_instance;
36 }
37
38 /**
39 * Fill the report with actual data
40 *
41 * @return array
42 */
43 private function generate_wpo_report() {
44 $report_format = $this->get_report_format();
45 $report_data = array();
46 foreach ($report_format as $block) {
47 $piece = array(
48 'title' => $block['title'],
49 'items' => array()
50 );
51
52 foreach ($block['items'] as $part_index => $part) {
53 $piece['items'][$part_index]['title'] = $part['title'];
54 $piece['items'][$part_index]['value'] = $part['value'] ?? '';
55
56 if (isset($part['markdown_value'])) {
57 $piece['items'][$part_index]['markdown_value'] = $part['markdown_value'];
58 }
59 }
60
61 $report_data[] = $piece;
62 }
63
64 return $report_data;
65 }
66
67 /**
68 * Define the report format
69 *
70 * @return array
71 */
72 private function get_report_format() {
73 return array(
74 array(
75 'title' => esc_html__('WP-Optimize information', 'wp-optimize'),
76 'items' => array(
77 array(
78 'title' => esc_html__('Report generation time', 'wp-optimize'),
79 'value' => esc_html($this->generation_time())
80 ),
81 array(
82 'title' => esc_html__('Cache size', 'wp-optimize'),
83 'value' => $this->cache_size(),
84 ),
85 array(
86 'title' => esc_html__('Minify size', 'wp-optimize'),
87 'value' => $this->minify_size(),
88 ),
89 array(
90 'title' => esc_html__('Logs', 'wp-optimize'),
91 'value' => $this->logs(),
92 'markdown_value' => $this->plain_log_debug()
93 ),
94 array(
95 'title' => esc_html__('WebP redirection rules', 'wp-optimize'),
96 'value' => $this->webp_redirect_status(),
97 ),
98 array(
99 'title' => esc_html__('Plugin settings', 'wp-optimize'),
100 'value' => $this->settings_container(),
101 'markdown_value' => '{{[wpo-general-settings]}}'
102 ),
103 array(
104 'title' => esc_html__('Cache settings', 'wp-optimize'),
105 'value' => $this->cache_settings(),
106 'markdown_value' => $this->cache_settings(false)
107 ),
108 array(
109 'title' => esc_html__('Webroot .htaccess', 'wp-optimize'),
110 'value' => $this->webroot_htaccess(),
111 'markdown_value' => $this->webroot_htaccess(false),
112 ),
113 array(
114 'title' => esc_html__('Uploads .htaccess', 'wp-optimize'),
115 'value' => $this->uploads_htaccess(),
116 'markdown_value' => $this->uploads_htaccess(false),
117 ),
118 array(
119 'title' => esc_html__('Folder permissions', 'wp-optimize'),
120 'value' => $this->path_perms(),
121 'markdown_value' => $this->path_perms(false),
122 ),
123 )
124 ),
125 );
126 }
127
128 /**
129 * Some WordPress Core Health report fields are loaded via AJAX after page render (ex: directory sizes), these field names are used as tags in JS to replace inside the plaintext report.
130 * This function is a getter for the list that has been set while parsing WP core health report
131 *
132 * @return array
133 */
134 public function get_replaceable_md_tags() {
135 return $this->replaceable_md_tags;
136 }
137
138 /**
139 * Generate WPO report and merge with WP code report
140 *
141 * @return array
142 */
143 public function generate_report() {
144 return array_merge($this->generate_wpo_report(), $this->get_wp_health_report());
145 }
146
147 /**
148 * Get debug information from WordPress core
149 *
150 * @return array
151 */
152 private function get_wp_health_report() {
153 $report = array();
154 $core_data = $this->server_info_util->get_wp_debug_data();
155 foreach ($core_data as $data_idx => $data) {
156 $block = array(
157 'title' => $data['label'],
158 'items' => array()
159 );
160
161 if (!isset($data['fields'])) {
162 $report[] = $block;
163 continue;
164 }
165
166 foreach ($data['fields'] as $fieldname => $info) {
167 $path_size_field_id = 'wp-paths-sizes' === $data_idx ? $data_idx . '-' . $fieldname : null;
168
169 $row = array();
170
171 $ajax_field_class = null;
172 if (isset($info['debug']) && ('loading...' === $info['debug']) && !is_null($path_size_field_id)) {
173 $replaceable_tag = '{{[' . $path_size_field_id . ']}}';
174
175 // Collect the tags that will need to be replaced after page render via Ajax requests to WP api
176 $this->replaceable_md_tags[$fieldname] = $replaceable_tag;
177
178 $row['markdown_value'] = $replaceable_tag;
179 $ajax_field_class = (string) $data_idx;
180 }
181
182 $row['title'] = $this->fix_title_case($info['label']);
183 $row['value'] = $this->ajax_value_container($info['value'], $path_size_field_id, $ajax_field_class);
184
185 $block['items'][] = $row;
186 }
187
188 $report[] = $block;
189 }
190
191 return $report;
192 }
193
194 /**
195 * Hard fix title issue
196 *
197 * @param string $title Original title
198 * @return string
199 */
200 private function fix_title_case($title) {
201 return str_replace('Site Language', 'Site language', $title);
202 }
203
204 /**
205 * Give format to cache size information
206 *
207 * @return string
208 */
209 private function cache_size() {
210 $data = $this->server_info_util->get_cache_size();
211 $size = size_format($data['size']);
212 if (false === $size) {
213 return esc_html__('Not available', 'wp-optimize');
214 }
215 // translators: %d represents the number of files.
216 return esc_html($size) . ' ' . sprintf(esc_html__('(%d files)', 'wp-optimize'), $data['file_count']);
217 }
218
219 /**
220 * Grab minify size from information object
221 *
222 * @return string
223 */
224 private function minify_size() {
225 $data = $this->server_info_util->get_minify_size();
226 if (empty($data)) {
227 return esc_html__('Not available', 'wp-optimize');
228 }
229 return esc_html($data);
230 }
231
232 /**
233 * Give link for user to download logs in a zip file
234 *
235 * @return string
236 */
237 private function logs() {
238 $data = $this->server_info_util->get_logs_summary();
239 if (0 === count($data)) {
240 return esc_html__('No log files found', 'wp-optimize');
241 }
242 return '<button id="wpo-download-logs" class="button-primary">' . esc_html__('Download logs', 'wp-optimize') . '</button><span id="wpo-generate-zip-file-text"></span>' . $this->pre_container(implode("\n", $data));
243 }
244
245 /**
246 * Just show log data, no button
247 *
248 * @return string
249 */
250 private function plain_log_debug() {
251 $data = $this->server_info_util->get_logs_summary();
252
253 if (0 < count($data)) {
254 return "\n" . ' - ' . implode("\n - ", $data);
255 }
256 return esc_html__('No log files found', 'wp-optimize');
257 }
258
259 /**
260 * Empty placeholder for plugin settings, that will be filled using `WP_Optimize().build_settings()`
261 *
262 * @return string
263 */
264 private function settings_container() {
265 return $this->pre_container('', 'wpo-general-settings');
266 }
267
268 /**
269 * Cache settings into preformatted HTML container
270 *
271 * @param bool $preformat_container Enclose the value inside a <pre> tag
272 * @return string
273 */
274 private function cache_settings($preformat_container = true) {
275 $config = $this->server_info_util->get_cache_config();
276
277 if (isset($config['wp_salt_auth'])) {
278 $config['wp_salt_auth'] = '*****';
279 }
280 if (isset($config['wp_salt_logged_in'])) {
281 $config['wp_salt_logged_in'] = '*****';
282 }
283
284 $json = wp_json_encode($config, JSON_PRETTY_PRINT);
285 return (string) $preformat_container ? $this->pre_container($json) : $json;
286 }
287
288 /**
289 * Grab .htaccess contents in webroot
290 *
291 * @param bool $preformat_container Enclose the value inside a <pre> tag
292 * @return string
293 */
294 private function webroot_htaccess($preformat_container = true) {
295 $content = $this->server_info_util->get_webroot_htaccess();
296 if ('' !== $content) {
297 return $preformat_container ? $this->pre_container($content) : $content;
298 }
299 return esc_html__('Not found or not readable', 'wp-optimize');
300 }
301
302 /**
303 * Grab .htaccess contents in uploads folder
304 *
305 * @param bool $preformat_container Enclose the value inside a <pre> tag
306 * @return string
307 */
308 private function uploads_htaccess($preformat_container = true) {
309 $content = $this->server_info_util->get_upload_dir_htaccess();
310 if ('' !== $content) {
311 return $preformat_container ? $this->pre_container($content) : $content;
312 }
313 return esc_html__('Not found or not readable', 'wp-optimize');
314 }
315
316 /**
317 * Helper to generate a preformatted HTML tag with contents and optional id
318 *
319 * @param string $content The innerHTML for the tag
320 * @param string $id Optional DOM id attribute
321 * @return string
322 */
323 private function pre_container($content, $id = '') {
324 return '<div class="wpo-settings-debug"><pre' . ('' !== $id ? ' id="' . esc_attr($id) .'"' : '') . '>' . esc_html($content) . '</pre></div>';
325 }
326
327 /**
328 * Show information about directories permissions
329 *
330 * @param bool $preformat_container Enclose the value inside a <pre> tag
331 * @return string
332 */
333 private function path_perms($preformat_container = true) {
334 $json = wp_json_encode($this->server_info_util->get_uploads_dir_permissions(), JSON_PRETTY_PRINT);
335 return (string) $preformat_container ? $this->pre_container($json) : $json;
336 }
337
338 /**
339 * Information about WebP image redirection status
340 *
341 * @return string
342 */
343 private function webp_redirect_status() {
344 $result = $this->server_info_util->webp_redirection_status();
345
346 if (null === $result) {
347 return esc_html__('Redirection is not available', 'wp-optimize');
348 } elseif (false === $result) {
349 return esc_html__('Redirection is disabled', 'wp-optimize');
350 }
351 return esc_html__('Redirection is enabled', 'wp-optimize');
352 }
353
354 /**
355 * Prepare complex item (async loading) as a label HTML tag that will be replaced with JS code (ex. directories sizes from core-site-health API call)
356 *
357 * @param string|array $value If an array, it will be JSON encoded inside a preformatted HTML tag
358 * @param string|null $id If not null, it is used as the label id property. If null, there will be no label, just value
359 * @param string|null $dynamic_field_type If not null, a css class is used to be able to catch these fields when the call is done
360 * @return string
361 */
362 private function ajax_value_container($value, $id, $dynamic_field_type) {
363 if (is_array($value)) {
364 $value = '<pre>' . wp_json_encode($value, JSON_PRETTY_PRINT) . '</pre>';
365 } else {
366 $value = esc_html($value);
367 }
368
369 if (is_null($id)) {
370 return $value;
371 }
372
373 $css_class = null !== $dynamic_field_type ? 'wpo-ajax-field-' . esc_attr($dynamic_field_type) : '';
374 return '<label class="' . $css_class . '" id="' . esc_attr('wpo-value-' . $id) . '">' . esc_html($value) . '</label>';
375 }
376
377 /**
378 * Add timestamp to report information
379 *
380 * @return string
381 */
382 private function generation_time() {
383 $timezone = $this->wp_timezone_string();
384
385 return date_i18n('Y-m-d H:i:s', $this->current_datetime()->getTimestamp()) . ('' !== $timezone ? ' (' . $timezone . ')' : '');
386 }
387
388 /**
389 * Retrieves the site's timezone string in a backward-compatible way.
390 *
391 * If the `wp_timezone_string()` function is available (WordPress 5.3+), it will use it.
392 * Otherwise, it falls back to a custom implementation that uses `timezone_string` or `gmt_offset`.
393 *
394 * Example return values:
395 * - 'Europe/London'
396 * - 'UTC'
397 * - '+01:00'
398 * - '-06:30'
399 *
400 * @return string PHP timezone identifier or a ±HH:MM offset.
401 */
402 private function wp_timezone_string() {
403 if (function_exists('wp_timezone_string')) {
404 return wp_timezone_string();
405 }
406
407 $timezone_string = get_option('timezone_string');
408
409 if ($timezone_string) {
410 return $timezone_string;
411 }
412
413 $offset = (float) get_option( 'gmt_offset' );
414 $hours = (int) $offset;
415 $minutes = ($offset - $hours);
416
417 $sign = ($offset < 0) ? '-' : '+';
418 $abs_hour = abs($hours);
419 $abs_mins = abs($minutes * 60);
420 $tz_offset = sprintf('%s%02d:%02d', $sign, $abs_hour, $abs_mins);
421
422 return $tz_offset;
423 }
424
425 /**
426 * Retrieves the current site datetime object in a backwards-compatible way.
427 *
428 * @return DateTimeImmutable
429 */
430 private function current_datetime() {
431 if (function_exists('current_datetime')) {
432 return current_datetime();
433 }
434
435 $timezone = new DateTimeZone($this->wp_timezone_string());
436 return new DateTimeImmutable('now', $timezone);
437 }
438 }
439
440 endif;
441