544 lines
16 KiB
PHP
544 lines
16 KiB
PHP
<?php
|
|
/**
|
|
* CLI Commands Module
|
|
*
|
|
* Comandos WP-CLI para debug y diagnóstico
|
|
*
|
|
* @package WP_Debug
|
|
* @since 1.0.0
|
|
*/
|
|
|
|
// Exit if accessed directly
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
// Check if WP-CLI is available
|
|
if (!class_exists('WP_CLI')) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* WP_Debug_CLI_Commands class
|
|
*/
|
|
class WP_Debug_CLI_Commands extends WP_CLI_Command {
|
|
|
|
/**
|
|
* Initialize CLI commands
|
|
*/
|
|
public static function init() {
|
|
if (class_exists('WP_CLI')) {
|
|
WP_CLI::add_command('debug', 'WP_Debug_CLI_Commands');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Show plugin status and modules information
|
|
*
|
|
* ## EXAMPLES
|
|
*
|
|
* wp debug status
|
|
*
|
|
* @when after_wp_load
|
|
*/
|
|
public function status($args, $assoc_args) {
|
|
WP_CLI::log('WP Debug Plugin Status');
|
|
WP_CLI::log('======================');
|
|
WP_CLI::log('');
|
|
|
|
// Plugin info
|
|
WP_CLI::log('Version: ' . WP_DEBUG_VERSION);
|
|
WP_CLI::log('Plugin Directory: ' . WP_DEBUG_PLUGIN_DIR);
|
|
WP_CLI::log('');
|
|
|
|
// Global status
|
|
$enabled = get_option('wp_debug_enabled', true);
|
|
WP_CLI::log('Plugin Status: ' . ($enabled ? 'Enabled' : 'Disabled'));
|
|
WP_CLI::log('');
|
|
|
|
// Modules status
|
|
WP_CLI::log('Modules:');
|
|
WP_CLI::log('--------');
|
|
|
|
$modules = WP_Debug_Autoloader::get_modules();
|
|
$items = array();
|
|
|
|
foreach ($modules as $module_name => $module_data) {
|
|
$module_enabled = get_option('wp_debug_module_' . $module_name, $module_data['enabled']);
|
|
$class_exists = class_exists($module_data['class']);
|
|
|
|
$items[] = array(
|
|
'module' => $module_name,
|
|
'status' => $module_enabled ? 'enabled' : 'disabled',
|
|
'loaded' => $class_exists ? 'yes' : 'no',
|
|
'class' => $module_data['class'],
|
|
);
|
|
}
|
|
|
|
WP_CLI\Utils\format_items('table', $items, array('module', 'status', 'loaded', 'class'));
|
|
|
|
// Database info
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . 'wp_debug_logs';
|
|
$count = $wpdb->get_var("SELECT COUNT(*) FROM {$table}");
|
|
|
|
WP_CLI::log('');
|
|
WP_CLI::log('Database Logs: ' . number_format($count) . ' entries');
|
|
|
|
// File logs
|
|
$logs_dir = WP_CONTENT_DIR . '/logs/wp-debug';
|
|
if (file_exists($logs_dir)) {
|
|
$files = glob($logs_dir . '/debug-*.log');
|
|
WP_CLI::log('Log Files: ' . count($files));
|
|
}
|
|
|
|
WP_CLI::success('Status check complete');
|
|
}
|
|
|
|
/**
|
|
* List hooks executed
|
|
*
|
|
* ## OPTIONS
|
|
*
|
|
* [--search=<pattern>]
|
|
* : Filter hooks by pattern
|
|
*
|
|
* [--limit=<number>]
|
|
* : Limit number of results
|
|
* ---
|
|
* default: 50
|
|
* ---
|
|
*
|
|
* [--format=<format>]
|
|
* : Output format
|
|
* ---
|
|
* default: table
|
|
* options:
|
|
* - table
|
|
* - json
|
|
* - csv
|
|
* ---
|
|
*
|
|
* ## EXAMPLES
|
|
*
|
|
* wp debug hooks
|
|
* wp debug hooks --search=init
|
|
* wp debug hooks --limit=100 --format=json
|
|
*
|
|
* @when after_wp_load
|
|
*/
|
|
public function hooks($args, $assoc_args) {
|
|
if (!class_exists('WP_Debug_Hook_Monitor')) {
|
|
WP_CLI::error('Hook Monitor module is not loaded');
|
|
return;
|
|
}
|
|
|
|
$search = isset($assoc_args['search']) ? $assoc_args['search'] : '';
|
|
$limit = isset($assoc_args['limit']) ? intval($assoc_args['limit']) : 50;
|
|
$format = isset($assoc_args['format']) ? $assoc_args['format'] : 'table';
|
|
|
|
// Get hooks from transient or current execution
|
|
$transient_data = get_transient('wp_debug_hooks_data');
|
|
|
|
if (!$transient_data) {
|
|
WP_CLI::warning('No hook data available. Run a page request first.');
|
|
return;
|
|
}
|
|
|
|
$hooks = $transient_data['hooks'];
|
|
|
|
// Filter by search
|
|
if ($search) {
|
|
$hooks = array_filter($hooks, function($hook) use ($search) {
|
|
return strpos($hook['hook'], $search) !== false;
|
|
});
|
|
WP_CLI::log('Filtered by: ' . $search);
|
|
}
|
|
|
|
// Limit results
|
|
if ($limit > 0) {
|
|
$hooks = array_slice($hooks, 0, $limit);
|
|
}
|
|
|
|
if (empty($hooks)) {
|
|
WP_CLI::warning('No hooks found');
|
|
return;
|
|
}
|
|
|
|
// Prepare items for display
|
|
$items = array();
|
|
foreach ($hooks as $hook) {
|
|
$items[] = array(
|
|
'hook' => $hook['hook'],
|
|
'type' => $hook['type'],
|
|
'caller' => isset($hook['caller']) ? $hook['caller'] : 'unknown',
|
|
'args' => isset($hook['args_count']) ? $hook['args_count'] : 0,
|
|
);
|
|
}
|
|
|
|
WP_CLI\Utils\format_items($format, $items, array('hook', 'type', 'caller', 'args'));
|
|
|
|
WP_CLI::log('');
|
|
WP_CLI::log('Total hooks: ' . $transient_data['total']);
|
|
WP_CLI::log('Unique hooks: ' . $transient_data['unique']);
|
|
WP_CLI::success('Retrieved ' . count($items) . ' hooks');
|
|
}
|
|
|
|
/**
|
|
* List templates loaded
|
|
*
|
|
* ## OPTIONS
|
|
*
|
|
* [--format=<format>]
|
|
* : Output format
|
|
* ---
|
|
* default: table
|
|
* options:
|
|
* - table
|
|
* - json
|
|
* - csv
|
|
* ---
|
|
*
|
|
* [--parts]
|
|
* : Show template parts instead of main templates
|
|
*
|
|
* [--missing]
|
|
* : Show only missing template parts
|
|
*
|
|
* ## EXAMPLES
|
|
*
|
|
* wp debug templates
|
|
* wp debug templates --parts
|
|
* wp debug templates --missing
|
|
*
|
|
* @when after_wp_load
|
|
*/
|
|
public function templates($args, $assoc_args) {
|
|
if (!class_exists('WP_Debug_Template_Tracer')) {
|
|
WP_CLI::error('Template Tracer module is not loaded');
|
|
return;
|
|
}
|
|
|
|
$format = isset($assoc_args['format']) ? $assoc_args['format'] : 'table';
|
|
$show_parts = isset($assoc_args['parts']);
|
|
$show_missing = isset($assoc_args['missing']);
|
|
|
|
// Get templates from transient
|
|
$transient_data = get_transient('wp_debug_templates_data');
|
|
|
|
if (!$transient_data) {
|
|
WP_CLI::warning('No template data available. Run a page request first.');
|
|
return;
|
|
}
|
|
|
|
if ($show_missing) {
|
|
// Show missing template parts
|
|
$items = array();
|
|
foreach ($transient_data['missing'] as $part) {
|
|
$items[] = array(
|
|
'slug' => $part['slug'],
|
|
'name' => $part['name'] ? $part['name'] : 'N/A',
|
|
'file' => $part['file'],
|
|
);
|
|
}
|
|
|
|
if (empty($items)) {
|
|
WP_CLI::success('No missing templates found');
|
|
return;
|
|
}
|
|
|
|
WP_CLI\Utils\format_items($format, $items, array('slug', 'name', 'file'));
|
|
WP_CLI::warning('Found ' . count($items) . ' missing templates');
|
|
|
|
} elseif ($show_parts) {
|
|
// Show template parts
|
|
$items = array();
|
|
foreach ($transient_data['parts'] as $part) {
|
|
$items[] = array(
|
|
'slug' => $part['slug'],
|
|
'name' => $part['name'] ? $part['name'] : 'N/A',
|
|
'file' => $part['file'],
|
|
'found' => $part['found'] ? 'yes' : 'no',
|
|
);
|
|
}
|
|
|
|
if (empty($items)) {
|
|
WP_CLI::warning('No template parts found');
|
|
return;
|
|
}
|
|
|
|
WP_CLI\Utils\format_items($format, $items, array('slug', 'name', 'file', 'found'));
|
|
WP_CLI::success('Retrieved ' . count($items) . ' template parts');
|
|
|
|
} else {
|
|
// Show main templates
|
|
$items = array();
|
|
foreach ($transient_data['templates'] as $template) {
|
|
$items[] = array(
|
|
'name' => $template['name'],
|
|
'type' => $template['type'],
|
|
'path' => $template['path'],
|
|
);
|
|
}
|
|
|
|
if (empty($items)) {
|
|
WP_CLI::warning('No templates found');
|
|
return;
|
|
}
|
|
|
|
WP_CLI\Utils\format_items($format, $items, array('name', 'type', 'path'));
|
|
WP_CLI::success('Retrieved ' . count($items) . ' templates');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Show performance metrics
|
|
*
|
|
* ## OPTIONS
|
|
*
|
|
* [--format=<format>]
|
|
* : Output format
|
|
* ---
|
|
* default: table
|
|
* options:
|
|
* - table
|
|
* - json
|
|
* ---
|
|
*
|
|
* ## EXAMPLES
|
|
*
|
|
* wp debug performance
|
|
* wp debug performance --format=json
|
|
*
|
|
* @when after_wp_load
|
|
*/
|
|
public function performance($args, $assoc_args) {
|
|
if (!class_exists('WP_Debug_Profiler')) {
|
|
WP_CLI::error('Profiler module is not loaded');
|
|
return;
|
|
}
|
|
|
|
$format = isset($assoc_args['format']) ? $assoc_args['format'] : 'table';
|
|
|
|
// Get performance report
|
|
$report = WP_Debug_Profiler::get_performance_report();
|
|
|
|
if (isset($report['error'])) {
|
|
WP_CLI::warning($report['error']);
|
|
return;
|
|
}
|
|
|
|
if ($format === 'json') {
|
|
WP_CLI::log(json_encode($report, JSON_PRETTY_PRINT));
|
|
return;
|
|
}
|
|
|
|
WP_CLI::log('Performance Metrics');
|
|
WP_CLI::log('===================');
|
|
WP_CLI::log('');
|
|
|
|
// Display formatted metrics
|
|
WP_CLI::log('Execution Time: ' . $report['formatted']['execution_time']);
|
|
WP_CLI::log('Memory Current: ' . $report['formatted']['memory_current']);
|
|
WP_CLI::log('Memory Peak: ' . $report['formatted']['memory_peak']);
|
|
if ($report['formatted']['server_load']) {
|
|
WP_CLI::log('Server Load: ' . $report['formatted']['server_load']);
|
|
}
|
|
WP_CLI::log('');
|
|
|
|
// Display timers if available
|
|
if (!empty($report['metrics']['timers'])) {
|
|
WP_CLI::log('Function Timers:');
|
|
WP_CLI::log('----------------');
|
|
|
|
$items = array();
|
|
foreach ($report['metrics']['timers'] as $name => $timer) {
|
|
$items[] = array(
|
|
'name' => $name,
|
|
'time' => round($timer['elapsed'] * 1000, 2) . 'ms',
|
|
'memory' => WP_Debug_Profiler::format_bytes($timer['memory_used']),
|
|
);
|
|
}
|
|
|
|
WP_CLI\Utils\format_items('table', $items, array('name', 'time', 'memory'));
|
|
WP_CLI::log('');
|
|
}
|
|
|
|
// Display suggestions
|
|
if (!empty($report['suggestions'])) {
|
|
WP_CLI::log('Suggestions:');
|
|
WP_CLI::log('------------');
|
|
foreach ($report['suggestions'] as $suggestion) {
|
|
WP_CLI::warning($suggestion);
|
|
}
|
|
} else {
|
|
WP_CLI::success('Performance looks good!');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export complete debug report in JSON
|
|
*
|
|
* ## OPTIONS
|
|
*
|
|
* [<file>]
|
|
* : Output file path (optional, defaults to stdout)
|
|
*
|
|
* ## EXAMPLES
|
|
*
|
|
* wp debug export
|
|
* wp debug export report.json
|
|
*
|
|
* @when after_wp_load
|
|
*/
|
|
public function export($args, $assoc_args) {
|
|
WP_CLI::log('Generating debug report...');
|
|
|
|
// Collect all data
|
|
$report = array(
|
|
'timestamp' => current_time('mysql'),
|
|
'site_url' => get_site_url(),
|
|
'wp_version' => get_bloginfo('version'),
|
|
'plugin_version' => WP_DEBUG_VERSION,
|
|
);
|
|
|
|
// Add hooks data
|
|
if (class_exists('WP_Debug_Hook_Monitor')) {
|
|
$hooks_data = get_transient('wp_debug_hooks_data');
|
|
if ($hooks_data) {
|
|
$report['hooks'] = array(
|
|
'total' => $hooks_data['total'],
|
|
'unique' => $hooks_data['unique'],
|
|
'top_10' => array_slice($hooks_data['counts'], 0, 10, true),
|
|
);
|
|
}
|
|
}
|
|
|
|
// Add templates data
|
|
if (class_exists('WP_Debug_Template_Tracer')) {
|
|
$templates_data = get_transient('wp_debug_templates_data');
|
|
if ($templates_data) {
|
|
$report['templates'] = array(
|
|
'templates' => $templates_data['templates'],
|
|
'parts_count' => count($templates_data['parts']),
|
|
'missing_count' => count($templates_data['missing']),
|
|
);
|
|
}
|
|
}
|
|
|
|
// Add performance data
|
|
if (class_exists('WP_Debug_Profiler')) {
|
|
$perf_report = WP_Debug_Profiler::get_performance_report();
|
|
if (!isset($perf_report['error'])) {
|
|
$report['performance'] = $perf_report;
|
|
}
|
|
}
|
|
|
|
// Add recent logs
|
|
if (class_exists('WP_Debug_Logger')) {
|
|
$logs = WP_Debug_Logger::get_logs(array('limit' => 50));
|
|
$report['recent_logs'] = $logs;
|
|
}
|
|
|
|
// Add module status
|
|
$modules = WP_Debug_Autoloader::get_modules();
|
|
$modules_status = array();
|
|
foreach ($modules as $module_name => $module_data) {
|
|
$modules_status[$module_name] = array(
|
|
'enabled' => get_option('wp_debug_module_' . $module_name, $module_data['enabled']),
|
|
'loaded' => class_exists($module_data['class']),
|
|
);
|
|
}
|
|
$report['modules'] = $modules_status;
|
|
|
|
// Convert to JSON
|
|
$json = json_encode($report, JSON_PRETTY_PRINT);
|
|
|
|
// Output to file or stdout
|
|
if (isset($args[0])) {
|
|
$file = $args[0];
|
|
$result = file_put_contents($file, $json);
|
|
|
|
if ($result === false) {
|
|
WP_CLI::error('Failed to write to file: ' . $file);
|
|
} else {
|
|
WP_CLI::success('Report exported to: ' . $file);
|
|
}
|
|
} else {
|
|
WP_CLI::log($json);
|
|
WP_CLI::success('Report generated successfully');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear old logs
|
|
*
|
|
* ## OPTIONS
|
|
*
|
|
* [--days=<days>]
|
|
* : Number of days to keep logs
|
|
* ---
|
|
* default: 7
|
|
* ---
|
|
*
|
|
* [--yes]
|
|
* : Skip confirmation
|
|
*
|
|
* ## EXAMPLES
|
|
*
|
|
* wp debug clear
|
|
* wp debug clear --days=30
|
|
* wp debug clear --yes
|
|
*
|
|
* @when after_wp_load
|
|
*/
|
|
public function clear($args, $assoc_args) {
|
|
if (!class_exists('WP_Debug_Logger')) {
|
|
WP_CLI::error('Logger module is not loaded');
|
|
return;
|
|
}
|
|
|
|
$days = isset($assoc_args['days']) ? intval($assoc_args['days']) : 7;
|
|
$yes = isset($assoc_args['yes']);
|
|
|
|
// Count logs to be deleted
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . 'wp_debug_logs';
|
|
$date = date('Y-m-d H:i:s', strtotime("-{$days} days"));
|
|
$count = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$table} WHERE timestamp < %s", $date));
|
|
|
|
// Count log files to be deleted
|
|
$logs_dir = WP_CONTENT_DIR . '/logs/wp-debug';
|
|
$files_count = 0;
|
|
if (file_exists($logs_dir)) {
|
|
$files = glob($logs_dir . '/debug-*.log');
|
|
$cutoff = strtotime("-{$days} days");
|
|
foreach ($files as $file) {
|
|
if (filemtime($file) < $cutoff) {
|
|
$files_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
WP_CLI::log("Logs older than {$days} days:");
|
|
WP_CLI::log(" Database entries: {$count}");
|
|
WP_CLI::log(" Log files: {$files_count}");
|
|
|
|
if ($count == 0 && $files_count == 0) {
|
|
WP_CLI::success('No old logs to clear');
|
|
return;
|
|
}
|
|
|
|
// Ask for confirmation
|
|
if (!$yes) {
|
|
WP_CLI::confirm('Are you sure you want to delete these logs?');
|
|
}
|
|
|
|
// Clear logs
|
|
WP_CLI::log('Clearing logs...');
|
|
WP_Debug_Logger::clear_old_logs($days);
|
|
|
|
WP_CLI::success("Cleared {$count} database entries and {$files_count} log files");
|
|
}
|
|
}
|