PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backups, Restore, Migration & Clone / 4.10.0
WP STAGING – WordPress Backups, Restore, Migration & Clone v4.10.0
4.10.0 4.9.5 4.9.4 4.9.3 4.9.2 4.9.1 4.9.0 4.8.1 trunk 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.10.0 3.2.0 3.3.1 3.3.2 3.3.3 3.4.1 3.4.3 3.5.0 3.6.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 4.0.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.8.0
wp-staging / Framework / Filesystem / DebugLogReader.php
wp-staging / Framework / Filesystem Last commit date
Filters 2 weeks ago Scanning 5 years ago AbstractFileObject.php 5 days ago AbstractFilesystemScanner.php 2 weeks ago DebugLogReader.php 5 days ago DirectoryListing.php 5 days ago DirectorySize.php 1 month ago DiskWriteCheck.php 7 months ago FileObject.php 1 year ago Filesystem.php 8 months ago FilesystemExceptions.php 5 years ago FilesystemScanner.php 1 week ago FilesystemScannerDto.php 1 month ago FilterableDirectoryIterator.php 1 year ago LegacyFileRulesTrait.php 1 month ago LogCleanup.php 7 months ago LogFiles.php 1 year ago MissingFileException.php 3 years ago OPcache.php 7 months ago PartIdentifier.php 10 months ago PathChecker.php 2 years ago PathIdentifier.php 1 week ago Permissions.php 1 month ago WpUploadsFolderSymlinker.php 1 month ago
DebugLogReader.php
215 lines
1 <?php
2
3 namespace WPStaging\Framework\Filesystem;
4
5 use WPStaging\Framework\Security\Capabilities;
6 use WPStaging\Framework\Adapter\Directory;
7
8 class DebugLogReader extends LogFiles
9 {
10 /**
11 * @var Filesystem
12 */
13 protected $filesystem;
14
15 /**
16 * @param Filesystem $filesystem
17 * @param Directory $logsDirectory
18 */
19 public function __construct(Filesystem $filesystem, Directory $logsDirectory)
20 {
21 parent::__construct($logsDirectory);
22 $this->filesystem = $filesystem;
23 }
24
25 /**
26 * Deletes a log file if requested.
27 * Used by WPStaging\Framework\CommonServiceProvider::registerClasses()
28 */
29 public function listenDeleteLogRequest()
30 {
31 if (!isset($_GET['deleteLog']) || !isset($_GET['deleteLogNonce'])) {
32 return;
33 }
34
35 if (!current_user_can((new Capabilities())->manageWPSTG()) || !wp_verify_nonce($_GET['deleteLogNonce'], 'wpstgDeleteLogNonce')) {
36 return;
37 }
38
39 $deleted = false;
40
41 if ($_GET['deleteLog'] === 'wpstaging') {
42 $deleted = $this->deleteWpStagingDebugLogFile();
43 }
44
45 if ($_GET['deleteLog'] === 'php') {
46 $deleted = $this->deletePhpDebugLogFile();
47 }
48
49 // Redirect so a page refresh does not delete the log again.
50 $redirectUrl = add_query_arg([
51 'page' => 'wpstg-tools',
52 'tab' => 'system-info',
53 'logDeleteStatus' => $deleted ? 'success' : 'failed',
54 ], admin_url('admin.php')) . '#logs';
55
56 wp_safe_redirect($redirectUrl);
57 exit;
58 }
59
60 /**
61 * @return bool True if the log file was deleted, false otherwise.
62 */
63 public function deletePhpDebugLogFile(): bool
64 {
65 return $this->deleteLogFile((string)ini_get('error_log'));
66 }
67
68 /**
69 * @return bool True if the log file was deleted, false otherwise.
70 */
71 public function deleteWpStagingDebugLogFile(): bool
72 {
73 if (!defined('WPSTG_DEBUG_LOG_FILE')) {
74 return false;
75 }
76
77 return $this->deleteLogFile(WPSTG_DEBUG_LOG_FILE);
78 }
79
80 /**
81 * Deletion depends on the folder being writable, not the log file itself, so
82 * a log created by another user can still be removed.
83 *
84 * @param string $path
85 * @return bool True if the file was deleted, false otherwise.
86 */
87 private function deleteLogFile(string $path): bool
88 {
89 if ($path === '' || !file_exists($path)) {
90 return false;
91 }
92
93 if (!is_file($path)) {
94 return false;
95 }
96
97 return $this->filesystem->delete($path);
98 }
99
100 /**
101 * @param int $maxSizeEach Max size in bytes to fetch from each log.
102 * @param bool $withWpstgDebugLog Whether to include WP STAGING custom log entries.
103 * @param bool $withPhpDebugLog Whether to include PHP error_log entries.
104 *
105 * @return string A formatted text with the last log entries from the debug log files.
106 */
107 public function getLastLogEntries(int $maxSizeEach, bool $withWpstgDebugLog = true, bool $withPhpDebugLog = true): string
108 {
109 $content = '';
110
111 if ($withWpstgDebugLog) {
112 if (defined('WPSTG_DEBUG_LOG_FILE')) {
113 $wpstgDebugLogFile = WPSTG_DEBUG_LOG_FILE;
114
115 if ($this->filesystem->isReadableFile($wpstgDebugLogFile)) {
116 $wpstgDebugLogFileSize = filesize($wpstgDebugLogFile);
117
118 $content .= sprintf(
119 "--- WP STAGING Debug Logs\nFile: %s\nTotal file size: %s\nShowing last: %s\n\n=== START ===\n\n",
120 $wpstgDebugLogFile,
121 size_format($wpstgDebugLogFileSize),
122 size_format($maxSizeEach)
123 );
124
125 if ($wpstgDebugLogFileSize > $maxSizeEach) {
126 $content .= $this->getDebugLogLines($wpstgDebugLogFile, $maxSizeEach);
127 } else {
128 $content .= file_get_contents($wpstgDebugLogFile);
129 }
130 $content .= "=== END ===\n\n";
131 } else {
132 $content .= "\n=== File WPSTG_DEBUG_LOG_FILE is not readable or does not exist ===\n";
133 }
134 } else {
135 $content .= "\n=== WPSTG_DEBUG_LOG_FILE NOT DEFINED ===\n";
136 }
137 }
138
139 if ($withPhpDebugLog) {
140 /** @see \wp_debug_mode to understand why it uses ini_get() */
141 $phpDebugLogFile = ini_get('error_log');
142
143 if ($this->filesystem->isReadableFile($phpDebugLogFile)) {
144 $phpDebugLogFileSize = filesize($phpDebugLogFile);
145
146 $content .= sprintf(
147 "--- PHP debug.log \nFile: %s\nTotal file size: %s\nShowing last: %s\n\n=== START ===\n\n",
148 $phpDebugLogFile,
149 size_format($phpDebugLogFileSize),
150 size_format($maxSizeEach)
151 );
152
153 if ($phpDebugLogFileSize > $maxSizeEach) {
154 $content .= $this->getDebugLogLines($phpDebugLogFile, $maxSizeEach);
155 } else {
156 $content .= file_get_contents($phpDebugLogFile);
157 }
158
159 $content .= "=== END ===\n\n";
160 } else {
161 $content .= "\n=== PHP DEBUG LOG FILE IS NOT A FILE OR IS NOT READABLE ===\n";
162 }
163 }
164
165 return $content;
166 }
167
168 /**
169 * @param $debugLogPath
170 * @param $maxSize
171 * @return string
172 */
173 protected function getDebugLogLines($debugLogPath, $maxSize): string
174 {
175 if (!is_file($debugLogPath) || !is_readable($debugLogPath)) {
176 return '';
177 }
178
179 try {
180 $debugFile = new FileObject($debugLogPath, 'r');
181
182 $negativeOffset = $maxSize;
183
184 // Set the pointer to the end of the file, minus the negative offset for which to start looking for errors.
185 $debugFile->fseek(max($debugFile->getSize() - $negativeOffset, 0), SEEK_SET);
186
187 $debugLines = [];
188
189 do {
190 $line = trim($debugFile->readAndMoveNext());
191 $line = html_entity_decode($line);
192 $line = sanitize_text_field($line);
193 $debugLines[] = $line;
194 } while ($debugFile->valid());
195
196 return implode("\n", $debugLines);
197 } catch (\Exception $e) {
198 return '';
199 }
200 }
201
202 /**
203 * @return string
204 */
205 public function maybeFixHtmlEntityDecode(string $content): string
206 {
207 if (empty($content)) {
208 return $content;
209 }
210
211 $content = esc_html(wp_strip_all_tags($content));
212 return str_replace(['&quot;', '&#039;', '&amp;'], ['"', "'", "&"], $content);
213 }
214 }
215