PluginProbe
phpinfo() WP – Site Health, PHP Compatibility & Server Audit / 7.2.5
phpinfo() WP – Site Health, PHP Compatibility & Server Audit v7.2.5
7.2.7 7.2.6 7.2.5 7.2.4 7.2.3 7.2.0 7.2.1 7.2.2 7.1.0 7.0.3 7.0.4 7.0.5 trunk 6.0 7.0.0 7.0.1 7.0.2
phpinfo-wp / includes / class-error-log.php

class-error-log.php in phpinfo() WP – Site Health, PHP Compatibility & Server Audit 7.2.5, at includes/class-error-log.php

180 lines 7.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 defined('ABSPATH') or die('Unauthorized Access');
3
4 class Phpinfo_WP_Error_Log {
5
6 private static function _pro(): bool { return Phpinfo_WP_License::is_valid(); }
7
8 public static function find_path(): ?string {
9 foreach (self::candidate_paths() as $c) {
10 if (@file_exists($c)) return $c;
11 }
12 return null;
13 }
14
15 // List of every path the plugin considers a possible PHP error log. Used
16 // both by find_path() and by the diagnostic view that explains why
17 // nothing was discovered. Order is the discovery priority.
18 public static function candidate_paths(): array {
19 $paths = [];
20
21 // 1. WP_DEBUG_LOG — true (default) or explicit path
22 if (defined('WP_DEBUG_LOG') && WP_DEBUG_LOG !== false) {
23 if (is_string(WP_DEBUG_LOG) && WP_DEBUG_LOG !== '1' && WP_DEBUG_LOG !== 'true') {
24 $paths[] = WP_DEBUG_LOG;
25 } else {
26 $paths[] = WP_CONTENT_DIR . '/debug.log';
27 }
28 }
29
30 // 2. php.ini error_log directive
31 $ini = ini_get('error_log');
32 if ($ini) $paths[] = $ini;
33
34 // 3. Common WordPress + shared-host fallbacks
35 $paths[] = WP_CONTENT_DIR . '/debug.log';
36 $paths[] = ABSPATH . 'error_log';
37 $paths[] = ABSPATH . 'php_errors.log';
38 $paths[] = ABSPATH . 'php-error.log';
39 $paths[] = dirname(ABSPATH) . '/error_log';
40 $paths[] = dirname(ABSPATH) . '/php_errors.log';
41 // Cloudways, SiteGround, Kinsta-style log directories above webroot
42 $paths[] = dirname(ABSPATH, 2) . '/logs/error.log';
43 $paths[] = dirname(ABSPATH, 2) . '/logs/php_errors.log';
44
45 return array_values(array_unique(array_filter($paths)));
46 }
47
48 // Reports the full diagnostic: WP_DEBUG state, each candidate path's
49 // status (defined/not-defined, found, missing, unreadable), and a
50 // human summary that tells the user exactly what to do.
51 public static function diagnose(): array {
52 $debug_const = defined('WP_DEBUG') && WP_DEBUG;
53 $debug_log_const = defined('WP_DEBUG_LOG') && WP_DEBUG_LOG !== false;
54 $debug_display_const = defined('WP_DEBUG_DISPLAY') ? (bool) WP_DEBUG_DISPLAY : true;
55
56 $candidates = self::candidate_paths();
57 $checks = [];
58 $found = null;
59
60 foreach ($candidates as $p) {
61 $entry = ['path' => $p, 'status' => 'missing', 'note' => ''];
62 if (@file_exists($p)) {
63 if (!@is_readable($p)) {
64 $entry['status'] = 'unreadable';
65 $entry['note'] = 'File exists but PHP cannot read it (check permissions).';
66 } else {
67 $entry['status'] = 'found';
68 $entry['size'] = (int) @filesize($p);
69 $entry['mtime'] = (int) @filemtime($p);
70 if ($found === null) $found = $p;
71 }
72 } else {
73 $entry['note'] = 'Not found.';
74 }
75 $checks[] = $entry;
76 }
77
78 // Build a one-line summary
79 if ($found) {
80 $summary = 'Log file found.';
81 $verdict = 'found';
82 } elseif (!$debug_log_const) {
83 $summary = 'WP_DEBUG_LOG is off, so WordPress is not writing a log. If your host writes PHP errors somewhere else, point WP_DEBUG_LOG at that path (or check your host control panel).';
84 $verdict = 'disabled';
85 } else {
86 $summary = "WP_DEBUG_LOG is on but no log file has been created — your site hasn't logged any PHP errors recently. That's usually a good thing. If you expected errors and don't see them, your host may be writing logs to a path we don't check (check your hosting control panel for an error_log location).";
87 $verdict = 'empty';
88 }
89
90 return [
91 'constants' => [
92 'WP_DEBUG' => $debug_const,
93 'WP_DEBUG_LOG' => $debug_log_const,
94 'WP_DEBUG_DISPLAY' => $debug_display_const,
95 ],
96 'wp_debug_log_value' => defined('WP_DEBUG_LOG') ? WP_DEBUG_LOG : null,
97 'ini_error_log' => ini_get('error_log') ?: null,
98 'checks' => $checks,
99 'found' => $found,
100 'summary' => $summary,
101 'verdict' => $verdict,
102 ];
103 }
104
105 // Returns last $lines lines, newest first
106 public static function tail(string $path, int $lines = 150): array {
107 if (!self::_pro()) return [];
108 if (!@file_exists($path) || !@is_readable($path)) return [];
109
110 $file = new SplFileObject($path, 'r');
111 $file->seek(PHP_INT_MAX);
112 $total = $file->key();
113
114 $start = max(0, $total - $lines);
115 $result = [];
116
117 $file->seek($start);
118 while (!$file->eof()) {
119 $line = rtrim((string) $file->current(), "\r\n");
120 if ($line !== '') $result[] = $line;
121 $file->next();
122 }
123
124 return array_reverse($result);
125 }
126
127 public static function size(string $path): int {
128 return @file_exists($path) ? (int) @filesize($path) : 0;
129 }
130
131 public static function clear(string $path): bool {
132 if (!self::_pro()) return false;
133 if (!@file_exists($path) || !@is_writable($path)) return false;
134 return (bool) @file_put_contents($path, '');
135 }
136
137 public static function format_bytes(int $bytes): string {
138 if ($bytes >= 1048576) return round($bytes / 1048576, 2) . ' MB';
139 if ($bytes >= 1024) return round($bytes / 1024, 1) . ' KB';
140 return $bytes . ' B';
141 }
142
143 // Counts PHP error log lines stamped with today's date.
144 // Free-safe: returns 0 if no log discovered. Reads only the tail (~512 KB) to stay cheap on huge logs.
145 public static function today_count(?string $path = null): int {
146 $path = $path ?? self::find_path();
147 if (!$path || !@is_readable($path)) return 0;
148
149 $size = (int) @filesize($path);
150 if ($size <= 0) return 0;
151
152 $chunk = 512 * 1024;
153 $offset = max(0, $size - $chunk);
154
155 $fh = @fopen($path, 'rb');
156 if (!$fh) return 0;
157 @fseek($fh, $offset);
158 $data = @fread($fh, $chunk);
159 @fclose($fh);
160 if ($data === false || $data === '') return 0;
161
162 // PHP error log lines start with: "[DD-Mon-YYYY HH:MM:SS TZ] ..."
163 // Use local date — error_log writes timestamps in PHP's configured timezone.
164 $today = date('d-M-Y');
165 $count = preg_match_all('/^\[' . preg_quote($today, '/') . ' /m', $data);
166 return (int) $count;
167 }
168
169 // Classifies a log line for color-coding
170 public static function classify(string $line): string {
171 $l = strtolower($line);
172 if (strpos($l, 'fatal error') !== false || strpos($l, 'uncaught') !== false) return 'log-fatal';
173 if (strpos($l, 'parse error') !== false) return 'log-fatal';
174 if (strpos($l, 'warning') !== false) return 'log-warning';
175 if (strpos($l, 'notice') !== false || strpos($l, 'deprecated') !== false) return 'log-notice';
176 if (strpos($l, 'wp_debug') !== false || strpos($l, '[debug]') !== false) return 'log-debug';
177 return 'log-default';
178 }
179 }
180