PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / trunk
Search Atlas SEO – OTTO AI SEO Automation for WordPress vtrunk
2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 2.6.4 2.6.3 2.5.23 All 138 releases
metasync / includes / class-metasync-debug-manager.php

class-metasync-debug-manager.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress trunk, at includes/class-metasync-debug-manager.php

1,043 lines 54.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) {
3 exit;
4 }
5
6 /**
7 * Debug / Error-Logging manager.
8 *
9 * Extracted from Metasync_Admin to keep the admin class focused on UI concerns.
10 * Handles debug-mode rendering, error-log display, wp-config updates, and
11 * related form-post handlers.
12 *
13 * @package Metasync
14 * @subpackage Metasync/includes
15 */
16 class Metasync_Debug_Manager
17 {
18 /** @var self|null */
19 private static $instance = null;
20
21 /**
22 * Get singleton instance.
23 *
24 * @return self
25 */
26 public static function instance()
27 {
28 if (self::$instance === null) {
29 self::$instance = new self();
30 }
31 return self::$instance;
32 }
33
34 private function __construct() {}
35
36 /**
37 * Get the admin page slug.
38 */
39 private function get_page_slug()
40 {
41 return Metasync_Admin::$page_slug;
42 }
43
44 /**
45 * Read an execution setting (mirrors Metasync_Admin::get_execution_setting).
46 */
47 private function get_execution_setting($key, $default = null)
48 {
49 $settings = get_option('metasync_execution_settings', array());
50 $defaults = array(
51 'max_execution_time' => 30,
52 'max_memory_limit' => 256,
53 'log_batch_size' => 1000,
54 'action_scheduler_batches' => 1,
55 'otto_rate_limit' => 10,
56 'queue_cleanup_days' => 31
57 );
58
59 if (empty($settings)) {
60 return isset($defaults[$key]) ? $defaults[$key] : $default;
61 }
62
63 return isset($settings[$key]) ? $settings[$key] : (isset($defaults[$key]) ? $defaults[$key] : $default);
64 }
65
66 // ------------------------------------------------------------------
67 // Error-log page (standalone page)
68 // ------------------------------------------------------------------
69
70 /**
71 * Display the standalone Error Log admin page.
72 *
73 * @param Metasync_Admin $admin The admin instance (needed for header/nav rendering).
74 */
75 public function metasync_display_error_log($admin) {
76 $log_file = WP_CONTENT_DIR . '/metasync_data/plugin_errors.log';
77 if (!Metasync::current_user_has_plugin_access()) {
78 return;
79 }
80
81 $can_manage_debug_settings = current_user_can('manage_options');
82 if (isset($_POST['wp_debug_nonce']) && !$can_manage_debug_settings) {
83 return;
84 }
85
86 if ($can_manage_debug_settings &&
87 isset($_POST['wp_debug_log_enabled']) &&
88 isset($_POST['wp_debug_enabled']) &&
89 isset($_POST['wp_debug_display_enabled']) &&
90 isset($_POST['wp_debug_nonce']) &&
91 wp_verify_nonce($_POST['wp_debug_nonce'], 'metasync_wp_debug_settings')) {
92
93 $wp_debug = in_array($_POST['wp_debug_enabled'], ['true', 'false']) ? $_POST['wp_debug_enabled'] : 'false';
94 $wp_debug_log = in_array($_POST['wp_debug_log_enabled'], ['true', 'false']) ? $_POST['wp_debug_log_enabled'] : 'false';
95 $wp_debug_display = in_array($_POST['wp_debug_display_enabled'], ['true', 'false']) ? $_POST['wp_debug_display_enabled'] : 'false';
96
97 update_option('wp_debug_enabled', $wp_debug);
98 update_option('wp_debug_log_enabled', $wp_debug_log);
99 update_option('wp_debug_display_enabled', $wp_debug_display);
100
101 $data = new ConfigControllerMetaSync();
102 $saved = $data->store();
103
104 if ($saved) {
105 add_settings_error(
106 'metasync_messages',
107 'metasync_message',
108 'WordPress debug settings updated successfully.',
109 'updated'
110 );
111 } else {
112 add_settings_error(
113 'metasync_messages',
114 'metasync_message',
115 'Debug settings were saved, but wp-config.php could not be updated: '
116 . ($data->getConfigError() !== '' ? $data->getConfigError() : 'the file is not writable.'),
117 'error'
118 );
119 }
120 } elseif ($can_manage_debug_settings && isset($_POST['wp_debug_nonce']) && !wp_verify_nonce($_POST['wp_debug_nonce'], 'metasync_wp_debug_settings')) {
121 add_settings_error(
122 'metasync_messages',
123 'metasync_message',
124 'Security verification failed. Please try again.',
125 'error'
126 );
127 }
128
129
130 $log_enabled = get_option('metasync_log_enabled', 'yes');
131 $wp_debug_enabled = get_option('wp_debug_enabled', 'false');
132 $wp_debug_log_enabled = get_option('wp_debug_log_enabled', 'false');
133 $wp_debug_display_enabled = get_option('wp_debug_display_enabled', 'false');
134 ?>
135
136 <?php $admin->render_layout_open('Error Logs', 'error_log', 'View and manage WordPress error logs and debug settings.'); ?>
137
138 <!-- Log File Management -->
139 <div class="dashboard-card">
140 <h2>Error Log Management</h2>
141 <p style="color: var(--dashboard-text-secondary); margin-bottom: 20px;">Clear WordPress error logs to free up space and remove old entries.</p>
142
143 <form method="post" style="margin-top: 15px;">
144 <input type="hidden" name="clear_log" value="yes" />
145 <?php wp_nonce_field('metasync_clear_log_nonce', 'clear_log_nonce'); ?>
146 <?php submit_button('Clear Error Logs', 'secondary', 'clear-log', false, array('class' => 'button button-secondary')); ?>
147 </form>
148 </div>
149
150 <!-- WordPress Debug Settings -->
151 <form method="post">
152 <?php wp_nonce_field('metasync_wp_debug_settings', 'wp_debug_nonce'); ?>
153 <div class="dashboard-card">
154 <h2>WordPress Debug Configuration</h2>
155 <p style="color: var(--dashboard-text-secondary); margin-bottom: 20px;">Configure WordPress debug settings to control error logging and display.</p>
156 <?php settings_errors('metasync_messages'); ?>
157
158 <table class="form-table">
159 <tr valign="top">
160 <th scope="row">WP_DEBUG</th>
161 <td>
162 <select name="wp_debug_enabled">
163 <option value="false" <?php selected('false', $wp_debug_enabled); ?>>Disabled</option>
164 <option value="true" <?php selected('true', $wp_debug_enabled); ?>>Enabled</option>
165 </select>
166 <p class="description">Enable or disable WordPress debugging mode.</p>
167 </td>
168 </tr>
169 <tr valign="top">
170 <th scope="row">WP_DEBUG_LOG</th>
171 <td>
172 <select name="wp_debug_log_enabled">
173 <option value="false" <?php selected('false', $wp_debug_log_enabled); ?>>Disabled</option>
174 <option value="true" <?php selected('true', $wp_debug_log_enabled); ?>>Enabled</option>
175 </select>
176 <p class="description">Save debug messages to a log file.</p>
177 </td>
178 </tr>
179 <tr valign="top">
180 <th scope="row">WP_DEBUG_DISPLAY</th>
181 <td>
182 <select name="wp_debug_display_enabled">
183 <option value="false" <?php selected('false', $wp_debug_display_enabled); ?>>Disabled</option>
184 <option value="true" <?php selected('true', $wp_debug_display_enabled); ?>>Enabled</option>
185 </select>
186 <p class="description">Display debug messages on the website (not recommended for production).</p>
187 </td>
188 </tr>
189 </table>
190 </div>
191
192 <div class="dashboard-card">
193 <h2>Save Changes</h2>
194 <p style="color: var(--dashboard-text-secondary); margin-bottom: 20px;">Apply your WordPress logging configuration changes.</p>
195 <?php submit_button('Save WordPress Logging Settings', 'primary', 'submit', false, array('class' => 'button button-primary')); ?>
196 </div>
197 </form>
198
199 <!-- Error Log Display -->
200 <div class="dashboard-card">
201 <h2>Error Log Contents</h2>
202 <p style="color: var(--dashboard-text-secondary); margin-bottom: 20px;">View the current error log entries for troubleshooting and monitoring.</p>
203
204 <?php
205 if (file_exists($log_file)) {
206 $log_content = file_get_contents($log_file);
207 if (!empty($log_content)) {
208 echo '<div class="dashboard-code-block" style="width: 100%; box-sizing: border-box;">';
209 echo '<pre style="background: var(--dashboard-card-bg); border: 1px solid var(--dashboard-border); border-radius: 8px; padding: 20px; overflow: auto; max-height: 400px; font-family: \'SF Mono\', Monaco, \'Cascadia Code\', \'Roboto Mono\', Consolas, monospace; font-size: 13px; line-height: 1.6; color: var(--dashboard-text-primary); margin: 0; box-shadow: var(--dashboard-shadow-sm); width: 100%; box-sizing: border-box; white-space: pre-wrap; word-wrap: break-word;">';
210 echo esc_html($log_content);
211 echo '</pre>';
212 echo '</div>';
213 } else {
214 echo '<div class="dashboard-empty-state">';
215 echo '<p style="color: var(--dashboard-text-secondary); font-style: italic; text-align: center; padding: 40px 20px;">�
216 Log file is empty - no errors recorded.</p>';
217 echo '</div>';
218 }
219 } else {
220 echo '<div class="dashboard-empty-state">';
221 echo '<p style="color: var(--dashboard-text-secondary); font-style: italic; text-align: center; padding: 40px 20px;">📝 No log file found. Error logging may not be enabled.</p>';
222 echo '</div>';
223 }
224 ?>
225 </div>
226 <?php $admin->render_layout_close(); ?>
227 <?php
228 }
229
230 // ------------------------------------------------------------------
231 // wp-config.php manipulation
232 // ------------------------------------------------------------------
233
234 /**
235 * Update wp-config.php debug constants.
236 */
237 public function metasync_update_wp_config() {
238 $wp_config_path = ABSPATH . 'wp-config.php';
239 if (file_exists($wp_config_path) && is_writable($wp_config_path)) {
240 $config_file = file_get_contents($wp_config_path);
241
242 $wp_debug_enabled = get_option('wp_debug_enabled', 'false') === 'true' ? 'true' : 'false';
243 if (preg_match("/define\s*\(\s*['\"]WP_DEBUG['\"]\s*,\s*.*?\s*\)\s*;/", $config_file)) {
244 $config_file = preg_replace("/define\s*\(\s*['\"]WP_DEBUG['\"]\s*,\s*.*?\s*\)\s*;/", "define('WP_DEBUG', $wp_debug_enabled);", $config_file);
245 } else {
246 $config_file = str_replace("/* That's all, stop editing! Happy publishing. */", "define('WP_DEBUG', $wp_debug_enabled);\n\n/* That's all, stop editing! Happy publishing. */", $config_file);
247 }
248
249 $wp_debug_log_enabled = get_option('wp_debug_log_enabled', 'false') === 'true' ? 'true' : 'false';
250 if (preg_match("/define\s*\(\s*['\"]WP_DEBUG_LOG['\"]\s*,\s*.*?\s*\)\s*;/", $config_file)) {
251 $config_file = preg_replace("/define\s*\(\s*['\"]WP_DEBUG_LOG['\"]\s*,\s*.*?\s*\)\s*;/", "define('WP_DEBUG_LOG', $wp_debug_log_enabled);", $config_file);
252 } else {
253 $config_file = str_replace("/* That's all, stop editing! Happy publishing. */", "define('WP_DEBUG_LOG', $wp_debug_log_enabled);\n\n/* That's all, stop editing! Happy publishing. */", $config_file);
254 }
255
256 $wp_debug_display_enabled = get_option('wp_debug_display_enabled', 'false') === 'true' ? 'true' : 'false';
257 if (preg_match("/define\s*\(\s*['\"]WP_DEBUG_DISPLAY['\"]\s*,\s*.*?\s*\)\s*;/", $config_file)) {
258 $config_file = preg_replace("/define\s*\(\s*['\"]WP_DEBUG_DISPLAY['\"]\s*,\s*.*?\s*\)\s*;/","define('WP_DEBUG_DISPLAY', $wp_debug_display_enabled);", $config_file);
259 } else {
260 $config_file = str_replace("/* That's all, stop editing! Happy publishing. */", "define('WP_DEBUG_DISPLAY', $wp_debug_display_enabled);\n\n/* That's all, stop editing! Happy publishing. */", $config_file);
261 }
262
263 file_put_contents($wp_config_path, $config_file);
264 } else {
265 wp_die('The wp-config.php file is not writable. Please check the file permissions.');
266 }
267 }
268
269 // ------------------------------------------------------------------
270 // Options hook
271 // ------------------------------------------------------------------
272
273 /**
274 * Sync plugin file headers when metasync_options is updated.
275 *
276 * @param mixed $old_value The old option value.
277 * @param mixed $new_value The new option value.
278 */
279 public function on_options_updated_sync_file_headers($old_value, $new_value)
280 {
281 $old_general = is_array($old_value) ? ($old_value['general'] ?? []) : [];
282 $new_general = is_array($new_value) ? ($new_value['general'] ?? []) : [];
283
284 $whitelabel_keys = [
285 'white_label_plugin_name',
286 'white_label_plugin_description',
287 'white_label_plugin_author',
288 'white_label_plugin_author_uri',
289 'white_label_plugin_uri',
290 ];
291
292 $changed = false;
293 foreach ($whitelabel_keys as $key) {
294 if (($old_general[$key] ?? '') !== ($new_general[$key] ?? '')) {
295 $changed = true;
296 break;
297 }
298 }
299
300 if ($changed) {
301 if (!current_user_can('manage_options')) {
302 return;
303 }
304
305 if (!class_exists('Metasync_Activator')) {
306 require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-metasync-activator.php';
307 }
308 Metasync_Activator::sync_plugin_file_headers();
309 }
310 }
311
312 // ------------------------------------------------------------------
313 // Rendering (Advanced-tab sections)
314 // ------------------------------------------------------------------
315
316 /**
317 * Render debug mode section for inclusion in Advanced settings.
318 */
319 public function render_debug_mode_section()
320 {
321 if (!class_exists('Metasync_Debug_Mode_Manager')) {
322 ?>
323 <div style="background: rgba(255, 193, 7, 0.1); border: 1px solid rgba(255, 193, 7, 0.3); border-radius: 8px; padding: 20px; margin-bottom: 20px;">
324 <p style="color: var(--dashboard-text-primary); margin: 0;">
325 ⚠️ Debug Mode Manager is not available. Please ensure the plugin is properly installed.
326 </p>
327 </div>
328 <?php
329 return;
330 }
331
332 $debug_manager = Metasync_Debug_Mode_Manager::get_instance();
333 $status = $debug_manager->get_status();
334 ?>
335
336 <!-- Debug Mode Status Overview -->
337 <div style="margin-bottom: 30px; padding-top: 20px;">
338 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">Current Status</h4>
339 <div style="background: rgba(255, 255, 255, 0.05); border: 1px solid var(--dashboard-border); padding: 20px; border-radius: 8px;">
340 <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
341 <!-- Status Badge -->
342 <div style="padding: 10px; border-left: 4px solid <?php echo $status['enabled'] ? '#ffc107' : '#4caf50'; ?>; background: rgba(<?php echo $status['enabled'] ? '255, 193, 7' : '76, 175, 80'; ?>, 0.1); border-radius: 4px;">
343 <div style="font-size: 12px; color: var(--dashboard-text-secondary); margin-bottom: 5px;">Status</div>
344 <div style="font-weight: 600; color: var(--dashboard-text-primary); font-size: 16px;">
345 <?php echo $status['enabled'] ? '⚠️ Active' : '✓ Inactive'; ?>
346 </div>
347 </div>
348
349 <?php if ($status['enabled']): ?>
350 <!-- Mode Type -->
351 <div style="padding: 10px; border-left: 4px solid #2196f3; background: rgba(33, 150, 243, 0.1); border-radius: 4px;">
352 <div style="font-size: 12px; color: var(--dashboard-text-secondary); margin-bottom: 5px;">Mode</div>
353 <div style="font-weight: 600; color: var(--dashboard-text-primary); font-size: 14px;">
354 <?php echo $status['indefinite'] ? 'Indefinite' : '24-Hour Auto-Disable'; ?>
355 </div>
356 </div>
357
358 <?php if (!$status['indefinite']): ?>
359 <!-- Time Remaining -->
360 <div style="padding: 10px; border-left: 4px solid #9c27b0; background: rgba(156, 39, 176, 0.1); border-radius: 4px;">
361 <div style="font-size: 12px; color: var(--dashboard-text-secondary); margin-bottom: 5px;">Time Remaining</div>
362 <div style="font-weight: 600; color: var(--dashboard-text-primary); font-size: 14px;" class="debug-time-remaining">
363 <?php echo esc_html($status['time_remaining_formatted']); ?>
364 </div>
365 </div>
366 <?php endif; ?>
367
368 <!-- Log File Size -->
369 <div style="padding: 10px; border-left: 4px solid #ff5722; background: rgba(255, 87, 34, 0.1); border-radius: 4px;">
370 <div style="font-size: 12px; color: var(--dashboard-text-secondary); margin-bottom: 5px;">Log File Size</div>
371 <div style="font-weight: 600; color: var(--dashboard-text-primary); font-size: 14px;">
372 <?php echo esc_html($status['log_file_size_formatted']); ?>
373 </div>
374 <div style="font-size: 11px; color: var(--dashboard-text-secondary); margin-top: 2px;">
375 <?php echo number_format($status['percentage_used'], 1); ?>% of <?php echo esc_html($status['max_log_size_formatted']); ?>
376 </div>
377 </div>
378 <?php endif; ?>
379 </div>
380
381 <?php if ($status['enabled']): ?>
382 <!-- Progress Bar -->
383 <div style="margin-top: 15px;">
384 <div style="background: rgba(255, 255, 255, 0.1); height: 8px; border-radius: 4px; overflow: hidden;">
385 <div style="height: 100%; width: <?php echo esc_attr($status['percentage_used']); ?>%; background: linear-gradient(90deg, #4caf50 0%, #ffc107 70%, #f44336 100%); transition: width 0.3s ease;"></div>
386 </div>
387 </div>
388 <?php endif; ?>
389 </div>
390 </div>
391
392 <!-- Debug Mode Controls -->
393 <div style="margin-bottom: 30px;">
394 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">Controls</h4>
395
396 <form method="post" action="<?php echo admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced'); ?>">
397 <input type="hidden" name="metasync_debug_mode_action_advanced" value="1" />
398 <?php wp_nonce_field('metasync_debug_mode_action_advanced', 'metasync_debug_mode_nonce_advanced'); ?>
399
400 <?php if (!$status['enabled']): ?>
401 <!-- Enable Debug Mode -->
402 <div style="background: rgba(255, 255, 255, 0.05); border: 1px solid var(--dashboard-border); padding: 20px; border-radius: 8px; margin-bottom: 15px;">
403 <p style="color: var(--dashboard-text-secondary); margin-top: 0; margin-bottom: 15px;">
404 Activate debug mode to troubleshoot issues. Debug mode will automatically disable after 24 hours unless you enable indefinite mode.
405 </p>
406
407 <label style="display: flex; align-items: center; margin: 15px 0; cursor: pointer;">
408 <input type="checkbox" name="indefinite" value="1" id="indefinite-mode-advanced" style="margin-right: 8px;" />
409 <span style="font-weight: 500; color: var(--dashboard-text-primary);">Keep debug mode enabled indefinitely</span>
410 </label>
411
412 <div id="indefinite-warning-advanced" style="display: none; background: rgba(255, 193, 7, 0.1); border-left: 4px solid #ffc107; padding: 12px; border-radius: 4px; margin: 15px 0;">
413 <strong style="color: var(--dashboard-text-primary);">⚠️ Warning:</strong>
414 <span style="color: var(--dashboard-text-secondary);"> Indefinite debug mode may cause log files to grow without limits. This should only be used for extended troubleshooting sessions.</span>
415 </div>
416
417 <input type="hidden" name="action_type" value="enable" />
418 <button type="submit" class="metasync-btn-primary" style="background: var(--dashboard-gradient-primary); color: #ffffff; border: none; padding: 10px 20px; border-radius: 8px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); display: inline-block; width: auto; min-width: 200px;" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 4px 8px rgba(0, 0, 0, 0.15)';" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 4px rgba(0, 0, 0, 0.1)';">
419 🐛 Enable Debug Mode
420 </button>
421 </div>
422
423 <?php else: ?>
424 <!-- Manage Active Debug Mode -->
425 <div style="display: flex; gap: 10px; flex-wrap: wrap;">
426 <?php if (!$status['indefinite']): ?>
427 <div>
428 <input type="hidden" name="action_type" value="extend" />
429 <button type="submit" class="metasync-btn-secondary" style="background: rgba(255, 255, 255, 0.1); color: var(--dashboard-text-primary); border: 1px solid var(--dashboard-border); padding: 10px 20px; border-radius: 8px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; display: inline-block; width: auto;" onmouseover="this.style.background='rgba(255, 255, 255, 0.15)';" onmouseout="this.style.background='rgba(255, 255, 255, 0.1)';">
430 ⏱️ Extend for 24 Hours
431 </button>
432 </div>
433 <?php endif; ?>
434
435 <div>
436 <input type="hidden" name="action_type" value="disable" />
437 <button type="submit" class="metasync-btn-danger" onclick="return confirm('Are you sure you want to disable debug mode?');" style="background: linear-gradient(135deg, #f44336, #d32f2f); color: #ffffff; border: none; padding: 10px 20px; border-radius: 8px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); display: inline-block; width: auto;" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 4px 8px rgba(0, 0, 0, 0.15)';" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 4px rgba(0, 0, 0, 0.1)';">
438 ⏹️ Disable Debug Mode Now
439 </button>
440 </div>
441 </div>
442 <?php endif; ?>
443 </form>
444 </div>
445
446 <!-- Configuration Details -->
447 <div style="margin-bottom: 30px;">
448 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">Configuration Details</h4>
449 <div style="background: rgba(255, 255, 255, 0.05); border: 1px solid var(--dashboard-border); border-radius: 8px; padding: 20px;">
450 <table style="width: 100%; border-collapse: collapse;">
451 <tbody>
452 <tr style="border-bottom: 1px solid var(--dashboard-border);">
453 <td style="padding: 10px 0; font-weight: 600; color: var(--dashboard-text-primary);">Maximum Log Size</td>
454 <td style="padding: 10px 0; color: var(--dashboard-text-secondary);"><?php echo esc_html($status['max_log_size_formatted']); ?></td>
455 </tr>
456 <tr style="border-bottom: 1px solid var(--dashboard-border);">
457 <td style="padding: 10px 0; font-weight: 600; color: var(--dashboard-text-primary);">Auto-Disable Duration</td>
458 <td style="padding: 10px 0; color: var(--dashboard-text-secondary);">24 hours</td>
459 </tr>
460 <tr style="border-bottom: 1px solid var(--dashboard-border);">
461 <td style="padding: 10px 0; font-weight: 600; color: var(--dashboard-text-primary);">Log Rotation</td>
462 <td style="padding: 10px 0; color: var(--dashboard-text-secondary);">Automatic when size limit reached</td>
463 </tr>
464 <tr style="border-bottom: 1px solid var(--dashboard-border);">
465 <td style="padding: 10px 0; font-weight: 600; color: var(--dashboard-text-primary);">Rotated Files Kept</td>
466 <td style="padding: 10px 0; color: var(--dashboard-text-secondary);">1 (current + 1 old)</td>
467 </tr>
468 <tr style="border-bottom: 1px solid var(--dashboard-border);">
469 <td style="padding: 10px 0; font-weight: 600; color: var(--dashboard-text-primary);">Check Frequency</td>
470 <td style="padding: 10px 0; color: var(--dashboard-text-secondary);">Hourly (via WP Cron)</td>
471 </tr>
472 <tr>
473 <td style="padding: 10px 0; font-weight: 600; color: var(--dashboard-text-primary);">Log File Path</td>
474 <td style="padding: 10px 0; color: var(--dashboard-text-secondary); font-family: monospace; font-size: 12px; word-break: break-all;">
475 <?php echo esc_html($status['log_file_path']); ?>
476 </td>
477 </tr>
478 </tbody>
479 </table>
480 </div>
481 </div>
482
483 <script>
484 jQuery(document).ready(function($) {
485 $('#indefinite-mode-advanced').on('change', function() {
486 if ($(this).is(':checked')) {
487 $('#indefinite-warning-advanced').slideDown();
488 } else {
489 $('#indefinite-warning-advanced').slideUp();
490 }
491 });
492
493 <?php if ($status['enabled'] && !$status['indefinite']): ?>
494 var initialTimeRemaining = <?php echo $status['time_remaining']; ?>;
495 var hasReloaded = false;
496
497 function updateDebugTimeRemaining() {
498 if (initialTimeRemaining <= 0 || hasReloaded) {
499 return;
500 }
501
502 $.ajax({
503 url: '<?php echo rest_url('metasync/v1/debug-mode/status'); ?>',
504 method: 'GET',
505 beforeSend: function(xhr) {
506 xhr.setRequestHeader('X-WP-Nonce', '<?php echo wp_create_nonce('wp_rest'); ?>');
507 },
508 success: function(response) {
509 console.log('MetaSync Debug Mode Status:', response);
510
511 if (response && typeof response.time_remaining !== 'undefined') {
512 if (response.time_remaining_formatted) {
513 $('.debug-time-remaining').text(response.time_remaining_formatted);
514 }
515
516 if (response.time_remaining <= 0 && initialTimeRemaining > 0 && !hasReloaded) {
517 hasReloaded = true;
518 console.log('Debug mode expired, reloading page...');
519 setTimeout(function() {
520 window.location.reload();
521 }, 2000);
522 }
523 }
524 },
525 error: function(xhr, status, error) {
526 console.error('MetaSync Debug Mode: Failed to update time remaining', error);
527 }
528 });
529 }
530
531 if (initialTimeRemaining > 0) {
532 setTimeout(updateDebugTimeRemaining, 2000);
533 setInterval(updateDebugTimeRemaining, 60000);
534 } else {
535 console.log('Debug mode already expired, skipping AJAX updates');
536 }
537 <?php endif; ?>
538 });
539 </script>
540 <?php
541 }
542
543 /**
544 * Render error log content for inclusion in Advanced settings.
545 */
546 public function render_error_log_content()
547 {
548 ?>
549 <!-- Error Summary Section -->
550 <div style="margin-bottom: 30px; padding-top: 20px;">
551 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">Error Summary</h4>
552 <p style="margin-bottom: 15px; color: var(--dashboard-text-secondary);">View categorized error statistics with counts and last occurrence times.</p>
553
554 <?php
555 if (class_exists('Metasync_Error_Logger')) {
556 $error_summary = Metasync_Error_Logger::get_visible_error_summary();
557
558 if (!empty($error_summary) && is_array($error_summary)) {
559 uasort($error_summary, function($a, $b) {
560 return strtotime($b['last_seen']) - strtotime($a['last_seen']);
561 });
562 ?>
563 <div style="overflow-x: auto; margin-bottom: 20px;">
564 <table class="wp-list-table widefat fixed striped" style="background: var(--dashboard-card-bg); border: 1px solid var(--dashboard-border);">
565 <thead>
566 <tr style="background: var(--dashboard-card-bg);">
567 <th style="padding: 12px; text-align: left; border-bottom: 2px solid var(--dashboard-border); color: var(--dashboard-text-primary); font-weight: 600;">Error Category</th>
568 <th style="padding: 12px; text-align: center; border-bottom: 2px solid var(--dashboard-border); color: var(--dashboard-text-primary); font-weight: 600;">Error Code</th>
569 <th style="padding: 12px; text-align: center; border-bottom: 2px solid var(--dashboard-border); color: var(--dashboard-text-primary); font-weight: 600;">Count</th>
570 <th style="padding: 12px; text-align: left; border-bottom: 2px solid var(--dashboard-border); color: var(--dashboard-text-primary); font-weight: 600;">Last Occurred</th>
571 <th style="padding: 12px; text-align: left; border-bottom: 2px solid var(--dashboard-border); color: var(--dashboard-text-primary); font-weight: 600;">Message</th>
572 </tr>
573 </thead>
574 <tbody>
575 <?php foreach ($error_summary as $key => $error): ?>
576 <tr>
577 <td style="padding: 10px 12px; color: var(--dashboard-text-primary);">
578 <strong><?php echo esc_html(Metasync_Error_Logger::get_display_label($error['category'])); ?></strong>
579 </td>
580 <td style="padding: 10px 12px; text-align: center; color: var(--dashboard-text-secondary); font-family: monospace;">
581 <code style="background: rgba(255, 255, 255, 0.1); padding: 2px 6px; border-radius: 3px;"><?php echo esc_html($error['code']); ?></code>
582 </td>
583 <td style="padding: 10px 12px; text-align: center;">
584 <span style="display: inline-block; background: var(--dashboard-accent); color: #ffffff; padding: 4px 10px; border-radius: 12px; font-weight: 600; font-size: 13px;">
585 <?php echo esc_html(number_format($error['count'])); ?>
586 </span>
587 </td>
588 <td style="padding: 10px 12px; color: var(--dashboard-text-secondary); font-size: 13px;">
589 <?php
590 $last_seen = strtotime($error['last_seen']);
591 $time_diff = human_time_diff($last_seen, current_time('timestamp'));
592 echo esc_html($error['last_seen']) . ' <span style="color: var(--dashboard-text-secondary);">(' . $time_diff . ' ago)</span>';
593 ?>
594 </td>
595 <td style="padding: 10px 12px; color: var(--dashboard-text-primary); max-width: 400px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="<?php echo esc_attr($error['message']); ?>">
596 <?php echo esc_html($error['message']); ?>
597 </td>
598 </tr>
599 <?php endforeach; ?>
600 </tbody>
601 </table>
602 </div>
603
604 <form method="post" action="<?php echo admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced'); ?>" style="margin-bottom: 20px;">
605 <input type="hidden" name="clear_error_summary" value="yes" />
606 <?php wp_nonce_field('metasync_clear_error_summary_nonce', 'clear_error_summary_nonce'); ?>
607 <button type="submit" class="button button-secondary" style="background: #dc3232; color: #ffffff; border: none; padding: 8px 16px; border-radius: 4px; font-weight: 500; cursor: pointer;">
608 <span class="dashicons dashicons-trash" style="margin-top:3px;font-size:15px;width:15px;height:15px;"></span> Clear Error Summary
609 </button>
610 </form>
611 <?php
612 } else {
613 ?>
614 <div class="dashboard-empty-state" style="padding: 30px; text-align: center; background: var(--dashboard-card-bg); border: 1px solid var(--dashboard-border); border-radius: 8px;">
615 <p style="color: var(--dashboard-text-secondary); font-style: italic; margin: 0;">
616
617 No errors recorded yet. Error summary will appear here once errors are logged.
618 </p>
619 </div>
620 <?php
621 }
622 } else {
623 ?>
624 <div class="dashboard-empty-state" style="padding: 30px; text-align: center; background: rgba(255, 193, 7, 0.1); border: 1px solid rgba(255, 193, 7, 0.3); border-radius: 8px;">
625 <p style="color: var(--dashboard-text-primary); margin: 0;">
626 ⚠️ Error Logger class not available. Please ensure the plugin is properly loaded.
627 </p>
628 </div>
629 <?php
630 }
631 ?>
632 </div>
633
634 <!-- Error Log Management -->
635 <div style="margin-bottom: 30px;">
636 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">Clear Error Logs</h4>
637 <p style="margin-bottom: 15px; color: var(--dashboard-text-secondary);">Clear WordPress error logs to free up space and remove old entries.</p>
638
639 <form method="post" action="<?php echo admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced'); ?>" style="margin-bottom: 20px;">
640 <input type="hidden" name="clear_log" value="yes" />
641 <?php wp_nonce_field('metasync_clear_log_nonce', 'clear_log_nonce'); ?>
642 <button type="submit" class="metasync-btn-primary" style="background: var(--dashboard-gradient-primary); color: #ffffff; border: none; padding: 10px 20px; border-radius: 8px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); display: inline-block; width: auto; min-width: 240px; max-width: fit-content;" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 4px 8px rgba(0, 0, 0, 0.15)';" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 4px rgba(0, 0, 0, 0.1)';">
643 🧹 Clear Error Logs
644 </button>
645 </form>
646 </div>
647
648 <!-- WordPress Debug Settings -->
649 <div style="margin-bottom: 30px;">
650 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">WordPress Debug Configuration</h4>
651 <p style="margin-bottom: 15px; color: var(--dashboard-text-secondary);">Configure WordPress debug settings to control error logging and display.</p>
652
653 <form method="post">
654
655 <?php
656 $wp_debug = defined('WP_DEBUG') && WP_DEBUG;
657 $debug_log = defined('WP_DEBUG_LOG') && WP_DEBUG_LOG;
658 $debug_display = defined('WP_DEBUG_DISPLAY') && WP_DEBUG_DISPLAY;
659 ?>
660
661 <p><strong>Current WordPress Debug Status:</strong></p>
662 <ul>
663 <li>WP_DEBUG: <?php echo $wp_debug ? '�
664 Enabled' : ' Disabled'; ?></li>
665 <li>WP_DEBUG_LOG: <?php echo $debug_log ? '�
666 Enabled' : '❌ Disabled'; ?></li>
667 <li>WP_DEBUG_DISPLAY: <?php echo $debug_display ? '�
668 Enabled' : ' Disabled'; ?></li>
669 </ul>
670
671 <?php if (!$wp_debug): ?>
672 <p style="color: var(--dashboard-accent);">💡 To enable error logging, add these lines to your wp-config.php file:</p>
673 <div class="metasync-copy-wrap"><pre class="metasync-copy-snippet" style="background: rgba(255, 255, 255, 0.05); border: 1px solid var(--dashboard-border); padding: 10px; border-radius: 4px; color: var(--dashboard-text-primary);">define('WP_DEBUG', true);
674 define('WP_DEBUG_LOG', true);
675 define('WP_DEBUG_DISPLAY', false);</pre><span class="metasync-copy-badge">Copy</span></div>
676 <?php /* click-to-copy for the wp-config snippet above */ ?>
677 <style>
678 .metasync-copy-wrap { position: relative; }
679 .metasync-copy-snippet { cursor: pointer; outline: 1px solid transparent; transition: outline-color .15s ease; }
680 .metasync-copy-wrap:hover .metasync-copy-snippet { outline-color: var(--dashboard-border, #374151); }
681 .metasync-copy-badge { position: absolute; top: 8px; right: 10px; font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 4px; pointer-events: none; opacity: 0; transition: opacity .15s ease; background: var(--dashboard-border, #374151); color: var(--dashboard-text-primary, #fff); }
682 .metasync-copy-wrap:hover .metasync-copy-badge { opacity: .85; }
683 .metasync-copy-badge.metasync-copy-done { opacity: 1; background: var(--dashboard-gradient-primary, linear-gradient(135deg, #667eea 0%, #764ba2 100%)); color: #fff; }
684 </style>
685 <script>
686 (function(){
687 if (window.__metasyncCopyInit) return; window.__metasyncCopyInit = true;
688 function fallback(text){ var ta=document.createElement('textarea'); ta.value=text; ta.style.position='fixed'; ta.style.opacity='0'; document.body.appendChild(ta); ta.focus(); ta.select(); try{document.execCommand('copy');}catch(e){} document.body.removeChild(ta); }
689 function attach(){
690 document.querySelectorAll('.metasync-copy-snippet').forEach(function(pre){
691 if (pre.dataset.copyBound) return; pre.dataset.copyBound='1';
692 pre.setAttribute('title','Click to copy');
693 pre.addEventListener('click', function(){
694 var text = pre.textContent.trim();
695 var wrap = pre.closest('.metasync-copy-wrap') || pre.parentNode;
696 var badge = wrap.querySelector('.metasync-copy-badge');
697 var done = function(){ if(!badge) return; badge.textContent='Copied!'; badge.classList.add('metasync-copy-done'); clearTimeout(badge._t); badge._t=setTimeout(function(){ badge.textContent='Copy'; badge.classList.remove('metasync-copy-done'); },1500); };
698 if (navigator.clipboard && navigator.clipboard.writeText){ navigator.clipboard.writeText(text).then(done).catch(function(){ fallback(text); done(); }); }
699 else { fallback(text); done(); }
700 });
701 });
702 }
703 if (document.readyState==='loading'){ document.addEventListener('DOMContentLoaded', attach); } else { attach(); }
704 })();
705 </script>
706 <?php endif; ?>
707 </form>
708 </div>
709
710 <!-- Error Log Display -->
711 <div style="margin-bottom: 30px;">
712 <h4 style="margin-top: 0; color: var(--dashboard-text-primary);">Error Log Contents</h4>
713 <p style="margin-bottom: 15px; color: var(--dashboard-text-secondary);">View the current error log entries for troubleshooting and monitoring.</p>
714
715 <?php
716 $error_logs = new Metasync_Error_Logs();
717
718 if ($error_logs->can_show_error_logs()):
719 $log_content = $error_logs->get_error_logs(50);
720
721 if (!empty(trim($log_content))):
722 $error_logs->show_copy_button();
723 $error_logs->show_logs();
724 $error_logs->show_info();
725 else: ?>
726 <div class="dashboard-empty-state">
727 <p style="color: var(--dashboard-text-secondary); font-style: italic; text-align: center; padding: 40px 20px;">�
728 Log file is empty - no errors recorded.</p>
729 </div>
730 <?php endif;
731 else:
732 $error_message = $error_logs->get_error_message();
733 if (!empty($error_message)): ?>
734 <div class="dashboard-empty-state">
735 <p style="color: var(--dashboard-text-primary); font-weight: bold; text-align: center; padding: 20px; background: rgba(255, 193, 7, 0.1); border: 1px solid rgba(255, 193, 7, 0.3); border-radius: 4px; margin: 20px 0;">
736 ⚠️ <?php echo esc_html($error_message); ?>
737 </p>
738 </div>
739 <?php else: ?>
740 <div class="dashboard-empty-state">
741 <p style="color: var(--dashboard-text-secondary); font-style: italic; text-align: center; padding: 40px 20px;">⚠️ Unable to access error log file. Please check permissions.</p>
742 </div>
743 <?php endif;
744 endif; ?>
745 </div>
746 <?php
747 }
748
749 // ------------------------------------------------------------------
750 // Form-post handlers
751 // ------------------------------------------------------------------
752
753 /**
754 * Handle debug mode operations (enable/disable/extend).
755 */
756 public function handle_debug_mode_operations()
757 {
758 if (isset($_POST['metasync_debug_mode_action_advanced'])) {
759 if (!wp_verify_nonce($_POST['metasync_debug_mode_nonce_advanced'], 'metasync_debug_mode_action_advanced')) {
760 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&debug_error=1');
761 wp_redirect($redirect_url);
762 exit;
763 }
764
765 if (!current_user_can('manage_options')) {
766 wp_die('Unauthorized');
767 }
768
769 if (!class_exists('Metasync_Debug_Mode_Manager')) {
770 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&debug_error=1&msg=manager_not_available');
771 wp_redirect($redirect_url);
772 exit;
773 }
774
775 $debug_manager = Metasync_Debug_Mode_Manager::get_instance();
776 $action = sanitize_text_field($_POST['action_type'] ?? '');
777 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced');
778
779 switch ($action) {
780 case 'enable':
781 $indefinite = isset($_POST['indefinite']) && $_POST['indefinite'] === '1';
782 $result = $debug_manager->enable_debug_mode($indefinite);
783
784 $redirect_url = add_query_arg('debug_mode_enabled', '1', $redirect_url);
785 if ($indefinite) {
786 $redirect_url = add_query_arg('indefinite', '1', $redirect_url);
787 }
788 break;
789
790 case 'disable':
791 $result = $debug_manager->disable_debug_mode('manual');
792 if ($result) {
793 $redirect_url = add_query_arg('debug_mode_disabled', '1', $redirect_url);
794 } else {
795 $redirect_url = add_query_arg('debug_error', '1', $redirect_url);
796 }
797 break;
798
799 case 'extend':
800 $result = $debug_manager->extend_debug_mode();
801
802 $redirect_url = add_query_arg('debug_mode_extended', '1', $redirect_url);
803 break;
804
805 default:
806 $redirect_url = add_query_arg('debug_error', '1', $redirect_url);
807 break;
808 }
809
810 wp_redirect($redirect_url);
811 exit;
812 }
813 }
814
815 /**
816 * Handle error log operations (clear).
817 */
818 public function handle_error_log_operations()
819 {
820 if ((isset($_POST['clear_log']) || isset($_POST['clear_error_summary'])) && !current_user_can('manage_options')) {
821 return;
822 }
823
824 if (isset($_POST['clear_log'])) {
825 if (wp_verify_nonce($_POST['clear_log_nonce'], 'metasync_clear_log_nonce')) {
826 $log_file = WP_CONTENT_DIR . '/metasync_data/plugin_errors.log';
827
828 if (file_exists($log_file)) {
829 file_put_contents($log_file, '');
830
831 $backup_files = glob(WP_CONTENT_DIR . '/metasync_data/plugin_errors.log.old.*');
832 if ($backup_files) {
833 foreach ($backup_files as $backup_file) {
834 @unlink($backup_file);
835 }
836 }
837 }
838
839 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&log_cleared=1');
840 wp_redirect($redirect_url);
841 exit;
842 } else {
843 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&clear_error=1');
844 wp_redirect($redirect_url);
845 exit;
846 }
847 }
848
849 if (isset($_POST['clear_error_summary']) && isset($_POST['clear_error_summary_nonce'])) {
850 if (wp_verify_nonce($_POST['clear_error_summary_nonce'], 'metasync_clear_error_summary_nonce')) {
851 if (class_exists('Metasync_Error_Logger')) {
852 Metasync_Error_Logger::clear_error_summary();
853 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&error_summary_cleared=1');
854 wp_redirect($redirect_url);
855 exit;
856 } else {
857 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&error_summary_error=1');
858 wp_redirect($redirect_url);
859 exit;
860 }
861 } else {
862 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&error_summary_error=1');
863 wp_redirect($redirect_url);
864 exit;
865 }
866 }
867 }
868
869 /**
870 * Handle clear all settings operations.
871 */
872 public function handle_clear_all_settings()
873 {
874 if (isset($_POST['clear_all_settings']) && !current_user_can('manage_options')) {
875 return;
876 }
877
878 if (isset($_POST['clear_all_settings'])) {
879 if (wp_verify_nonce($_POST['clear_all_settings_nonce'], 'metasync_clear_all_settings_nonce')) {
880
881 $metasync_options_to_clear = [
882 'metasync_options',
883 'metasync_options_instant_indexing',
884 'metasync_options_bing_instant_indexing',
885 'metasync_otto_crawldata',
886 'metasync_logging_data',
887 'metasync_wp_sa_connect_token',
888 'wp_debug_enabled',
889 'wp_debug_log_enabled',
890 'wp_debug_display_enabled',
891 // Media Optimization settings & cache
892 'metasync_media_optimization',
893 'metasync_batch_optimize_queue',
894 'metasync_batch_optimize_progress',
895 'metasync_batch_optimize_settings',
896 // Code Minification settings
897 'metasync_code_minification',
898 ];
899
900 $cleared_count = 0;
901 foreach ($metasync_options_to_clear as $option_name) {
902 if (get_option($option_name) !== false) {
903 delete_option($option_name);
904 $cleared_count++;
905 }
906 }
907
908 $transients_to_clear = [
909 'metasync_heartbeat_status_cache',
910 ];
911
912 foreach ($transients_to_clear as $transient_name) {
913 delete_transient($transient_name);
914 }
915
916 $cron_hooks_to_clear = [
917 'metasync_heartbeat_cron_check',
918 'metasync_media_batch_optimize_cron',
919 ];
920
921 foreach ($cron_hooks_to_clear as $cron_hook) {
922 $timestamp = wp_next_scheduled($cron_hook);
923 if ($timestamp) {
924 wp_unschedule_event($timestamp, $cron_hook);
925 }
926 }
927
928 $new_plugin_auth_token = wp_generate_password(32, false, false);
929
930 $fresh_options = [
931 'general' => [
932 'apikey' => $new_plugin_auth_token
933 ]
934 ];
935
936 update_option('metasync_options', $fresh_options);
937
938 Metasync::log_api_key_event('settings_reset', 'plugin_auth_token', array(
939 'options_cleared_count' => $cleared_count,
940 'new_token_prefix' => substr($new_plugin_auth_token, 0, 8) . '...',
941 'triggered_by' => 'settings_reset_action'
942 ), 'info');
943
944 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&settings_cleared=1');
945 wp_redirect($redirect_url);
946 exit;
947 } else {
948 $redirect_url = admin_url('admin.php?page=' . $this->get_page_slug() . '&tab=advanced&clear_settings_error=1');
949 wp_redirect($redirect_url);
950 exit;
951 }
952 }
953 }
954
955 // ------------------------------------------------------------------
956 // Log-file helpers
957 // ------------------------------------------------------------------
958
959 /**
960 * Get error log content for display.
961 */
962 public function get_error_log_content()
963 {
964 $execution_time = $this->get_execution_setting('max_execution_time');
965 if (function_exists('set_time_limit')) {
966 @set_time_limit($execution_time);
967 }
968
969 $log_file = WP_CONTENT_DIR . '/debug.log';
970
971 if (!file_exists($log_file) || !is_readable($log_file)) {
972 return false;
973 }
974
975 $batch_size = $this->get_execution_setting('log_batch_size');
976
977 $file_size = filesize($log_file);
978 if ($file_size > 10 * 1024 * 1024) {
979 return $this->get_log_tail($log_file, $batch_size);
980 }
981
982 $content = file_get_contents($log_file);
983 if ($content === false) {
984 return false;
985 }
986
987 $lines = explode("\n", $content);
988 $recent_lines = array_slice($lines, -$batch_size);
989
990 return implode("\n", $recent_lines);
991 }
992
993 /**
994 * Memory-efficient function to get last N lines from a large file.
995 */
996 public function get_log_tail($file_path, $lines = null)
997 {
998 $execution_time = $this->get_execution_setting('max_execution_time');
999 if (function_exists('set_time_limit')) {
1000 @set_time_limit($execution_time);
1001 }
1002
1003 if ($lines === null) {
1004 $lines = $this->get_execution_setting('log_batch_size');
1005 }
1006
1007 $handle = fopen($file_path, 'r');
1008 if (!$handle) {
1009 return false;
1010 }
1011
1012 fseek($handle, -1, SEEK_END);
1013
1014 $result_lines = array();
1015 $line = '';
1016 $line_count = 0;
1017
1018 while (ftell($handle) > 0 && $line_count < $lines) {
1019 $char = fgetc($handle);
1020
1021 if ($char === "\n") {
1022 if (!empty($line)) {
1023 array_unshift($result_lines, strrev($line));
1024 $line = '';
1025 $line_count++;
1026 }
1027 } else {
1028 $line .= $char;
1029 }
1030
1031 fseek($handle, -2, SEEK_CUR);
1032 }
1033
1034 if (!empty($line) && $line_count < $lines) {
1035 array_unshift($result_lines, strrev($line));
1036 }
1037
1038 fclose($handle);
1039
1040 return implode("\n", $result_lines);
1041 }
1042 }
1043