Agregar plugin WP Debug para diagnóstico sistemático
This commit is contained in:
457
wp-content/plugins/wp-debug/assets/css/debug-panel.css
Normal file
457
wp-content/plugins/wp-debug/assets/css/debug-panel.css
Normal file
@@ -0,0 +1,457 @@
|
||||
/**
|
||||
* WP Debug Frontend Panel Styles
|
||||
*
|
||||
* @package WP_Debug
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
/* Toggle Button */
|
||||
.wp-debug-toggle {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
z-index: 99998;
|
||||
background: #2271b1;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 10px 15px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.wp-debug-toggle:hover {
|
||||
background: #135e96;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.wp-debug-toggle .dashicons {
|
||||
font-size: 18px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.wp-debug-toggle .label {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
/* Main Panel */
|
||||
.wp-debug-panel {
|
||||
position: fixed;
|
||||
bottom: 80px;
|
||||
right: 20px;
|
||||
width: 800px;
|
||||
max-width: calc(100vw - 40px);
|
||||
max-height: calc(100vh - 120px);
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.2);
|
||||
z-index: 99999;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
/* Panel Header */
|
||||
.wp-debug-panel-header {
|
||||
background: #2271b1;
|
||||
color: #fff;
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-shrink: 0;
|
||||
border-bottom: 3px solid #135e96;
|
||||
}
|
||||
|
||||
.wp-debug-panel-header h3 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
.wp-debug-panel-tabs {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.wp-debug-tab {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.wp-debug-tab:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.wp-debug-tab.active {
|
||||
background: #fff;
|
||||
color: #2271b1;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Close Button */
|
||||
.wp-debug-panel-close {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: #fff;
|
||||
border: none;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.wp-debug-panel-close:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* Panel Content */
|
||||
.wp-debug-panel-content {
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
background: #f9f9f9;
|
||||
}
|
||||
|
||||
.wp-debug-tab-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wp-debug-tab-content.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
.wp-debug-panel-content h4 {
|
||||
margin: 0 0 15px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #1d2327;
|
||||
border-bottom: 2px solid #2271b1;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.wp-debug-panel-content h5 {
|
||||
margin: 20px 0 10px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1d2327;
|
||||
}
|
||||
|
||||
.wp-debug-panel-content p {
|
||||
margin: 10px 0;
|
||||
color: #50575e;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
.wp-debug-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
margin: 10px 0 20px 0;
|
||||
}
|
||||
|
||||
.wp-debug-table thead {
|
||||
background: #f0f0f1;
|
||||
}
|
||||
|
||||
.wp-debug-table th {
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
color: #1d2327;
|
||||
border-bottom: 2px solid #dcdcde;
|
||||
}
|
||||
|
||||
.wp-debug-table td {
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid #f0f0f1;
|
||||
font-size: 13px;
|
||||
color: #50575e;
|
||||
}
|
||||
|
||||
.wp-debug-table tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.wp-debug-table tr:hover {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
|
||||
.wp-debug-table code {
|
||||
background: #f0f0f1;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
color: #d63638;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
}
|
||||
|
||||
/* Lists */
|
||||
.wp-debug-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.wp-debug-list li {
|
||||
background: #fff;
|
||||
padding: 10px 12px;
|
||||
margin-bottom: 5px;
|
||||
border-radius: 4px;
|
||||
border-left: 3px solid #2271b1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 13px;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.wp-debug-list li.missing {
|
||||
border-left-color: #d63638;
|
||||
}
|
||||
|
||||
.wp-debug-list li.duplicate {
|
||||
border-left-color: #dba617;
|
||||
}
|
||||
|
||||
.wp-debug-list code {
|
||||
background: #f0f0f1;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
}
|
||||
|
||||
/* Module List */
|
||||
.wp-debug-module-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.wp-debug-module-list li {
|
||||
background: #fff;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* Status Badges */
|
||||
.status-active {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
background: #00a32a;
|
||||
color: #fff;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.status-inactive {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
background: #dcdcde;
|
||||
color: #50575e;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.status-error {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
background: #d63638;
|
||||
color: #fff;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.template-type {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
background: #f0f0f1;
|
||||
color: #2271b1;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Query Items */
|
||||
.wp-debug-queries {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.query-item {
|
||||
background: #fff;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
border-left: 3px solid #2271b1;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.query-item.n-plus-one {
|
||||
border-left-color: #d63638;
|
||||
}
|
||||
|
||||
.query-time {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
background: #f0f0f1;
|
||||
color: #d63638;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.query-count {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
background: #d63638;
|
||||
color: #fff;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.query-sql {
|
||||
display: block;
|
||||
background: #f9f9f9;
|
||||
padding: 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
color: #1d2327;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.count {
|
||||
background: #dba617;
|
||||
color: #fff;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Scrollbar */
|
||||
.wp-debug-panel-content::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.wp-debug-panel-content::-webkit-scrollbar-track {
|
||||
background: #f0f0f1;
|
||||
}
|
||||
|
||||
.wp-debug-panel-content::-webkit-scrollbar-thumb {
|
||||
background: #c3c4c7;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.wp-debug-panel-content::-webkit-scrollbar-thumb:hover {
|
||||
background: #a7aaad;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.wp-debug-panel {
|
||||
width: calc(100vw - 40px);
|
||||
bottom: 80px;
|
||||
right: 20px;
|
||||
max-height: calc(100vh - 120px);
|
||||
}
|
||||
|
||||
.wp-debug-panel-header {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.wp-debug-panel-tabs {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.wp-debug-tab {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.wp-debug-panel-close {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
.wp-debug-module-list {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.wp-debug-table {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.wp-debug-table th,
|
||||
.wp-debug-table td {
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animation */
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.wp-debug-panel[data-visible="true"] {
|
||||
animation: slideUp 0.3s ease;
|
||||
}
|
||||
229
wp-content/plugins/wp-debug/assets/js/debug-panel.js
Normal file
229
wp-content/plugins/wp-debug/assets/js/debug-panel.js
Normal file
@@ -0,0 +1,229 @@
|
||||
/**
|
||||
* WP Debug Frontend Panel JavaScript
|
||||
*
|
||||
* @package WP_Debug
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* WP Debug Panel
|
||||
*/
|
||||
const WPDebugPanel = {
|
||||
|
||||
/**
|
||||
* Initialize
|
||||
*/
|
||||
init: function() {
|
||||
this.toggleButton = $('#wp-debug-toggle');
|
||||
this.panel = $('#wp-debug-panel');
|
||||
this.closeButton = $('.wp-debug-panel-close');
|
||||
this.tabs = $('.wp-debug-tab');
|
||||
|
||||
// Restore panel state from localStorage
|
||||
this.restoreState();
|
||||
|
||||
// Bind events
|
||||
this.bindEvents();
|
||||
|
||||
// Log initialization
|
||||
if (window.console && console.log) {
|
||||
console.log('[WP Debug] Panel initialized');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Bind events
|
||||
*/
|
||||
bindEvents: function() {
|
||||
const self = this;
|
||||
|
||||
// Toggle button click
|
||||
this.toggleButton.on('click', function(e) {
|
||||
e.preventDefault();
|
||||
self.togglePanel();
|
||||
});
|
||||
|
||||
// Close button click
|
||||
this.closeButton.on('click', function(e) {
|
||||
e.preventDefault();
|
||||
self.hidePanel();
|
||||
});
|
||||
|
||||
// Tab clicks
|
||||
this.tabs.on('click', function(e) {
|
||||
e.preventDefault();
|
||||
const tabName = $(this).data('tab');
|
||||
self.switchTab(tabName);
|
||||
});
|
||||
|
||||
// Press Escape to close
|
||||
$(document).on('keydown', function(e) {
|
||||
if (e.key === 'Escape' && self.panel.is(':visible')) {
|
||||
self.hidePanel();
|
||||
}
|
||||
});
|
||||
|
||||
// Click outside to close
|
||||
$(document).on('click', function(e) {
|
||||
if (self.panel.is(':visible') &&
|
||||
!self.panel.is(e.target) &&
|
||||
self.panel.has(e.target).length === 0 &&
|
||||
!self.toggleButton.is(e.target) &&
|
||||
self.toggleButton.has(e.target).length === 0) {
|
||||
self.hidePanel();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle panel visibility
|
||||
*/
|
||||
togglePanel: function() {
|
||||
if (this.panel.is(':visible')) {
|
||||
this.hidePanel();
|
||||
} else {
|
||||
this.showPanel();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show panel
|
||||
*/
|
||||
showPanel: function() {
|
||||
this.panel.attr('data-visible', 'true').fadeIn(300);
|
||||
this.saveState('visible', true);
|
||||
|
||||
// Log
|
||||
if (window.console && console.log) {
|
||||
console.log('[WP Debug] Panel opened');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide panel
|
||||
*/
|
||||
hidePanel: function() {
|
||||
this.panel.fadeOut(300, function() {
|
||||
$(this).attr('data-visible', 'false');
|
||||
});
|
||||
this.saveState('visible', false);
|
||||
|
||||
// Log
|
||||
if (window.console && console.log) {
|
||||
console.log('[WP Debug] Panel closed');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Switch tab
|
||||
*/
|
||||
switchTab: function(tabName) {
|
||||
// Update tab buttons
|
||||
this.tabs.removeClass('active');
|
||||
this.tabs.filter('[data-tab="' + tabName + '"]').addClass('active');
|
||||
|
||||
// Update tab content
|
||||
$('.wp-debug-tab-content').removeClass('active');
|
||||
$('.wp-debug-tab-content[data-tab="' + tabName + '"]').addClass('active');
|
||||
|
||||
// Save active tab
|
||||
this.saveState('activeTab', tabName);
|
||||
|
||||
// Log
|
||||
if (window.console && console.log) {
|
||||
console.log('[WP Debug] Switched to tab: ' + tabName);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Save state to localStorage
|
||||
*/
|
||||
saveState: function(key, value) {
|
||||
if (typeof Storage !== 'undefined') {
|
||||
try {
|
||||
localStorage.setItem('wpDebugPanel_' + key, JSON.stringify(value));
|
||||
} catch (e) {
|
||||
// Ignore storage errors
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get state from localStorage
|
||||
*/
|
||||
getState: function(key, defaultValue) {
|
||||
if (typeof Storage !== 'undefined') {
|
||||
try {
|
||||
const value = localStorage.getItem('wpDebugPanel_' + key);
|
||||
return value !== null ? JSON.parse(value) : defaultValue;
|
||||
} catch (e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
},
|
||||
|
||||
/**
|
||||
* Restore panel state
|
||||
*/
|
||||
restoreState: function() {
|
||||
// Restore visibility
|
||||
const isVisible = this.getState('visible', false);
|
||||
if (isVisible) {
|
||||
this.panel.show().attr('data-visible', 'true');
|
||||
}
|
||||
|
||||
// Restore active tab
|
||||
const activeTab = this.getState('activeTab', 'overview');
|
||||
this.switchTab(activeTab);
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh panel data (for future AJAX updates)
|
||||
*/
|
||||
refresh: function() {
|
||||
if (window.console && console.log) {
|
||||
console.log('[WP Debug] Refreshing panel data...');
|
||||
}
|
||||
|
||||
// This could be extended to fetch fresh data via AJAX
|
||||
// For now, it just logs
|
||||
if (typeof wpDebugData !== 'undefined' && wpDebugData.ajaxUrl) {
|
||||
$.ajax({
|
||||
url: wpDebugData.ajaxUrl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'wp_debug_refresh',
|
||||
nonce: wpDebugData.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (window.console && console.log) {
|
||||
console.log('[WP Debug] Panel data refreshed', response);
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
if (window.console && console.error) {
|
||||
console.error('[WP Debug] Refresh failed:', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize on document ready
|
||||
*/
|
||||
$(document).ready(function() {
|
||||
WPDebugPanel.init();
|
||||
});
|
||||
|
||||
/**
|
||||
* Expose to window for external access
|
||||
*/
|
||||
window.WPDebugPanel = WPDebugPanel;
|
||||
|
||||
})(jQuery);
|
||||
145
wp-content/plugins/wp-debug/inc/activation.php
Normal file
145
wp-content/plugins/wp-debug/inc/activation.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Activation
|
||||
*
|
||||
* @package WP_Debug
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WP_Debug_Activation class
|
||||
*/
|
||||
class WP_Debug_Activation {
|
||||
|
||||
/**
|
||||
* Run activation tasks
|
||||
*/
|
||||
public static function activate() {
|
||||
// Set default options
|
||||
self::set_default_options();
|
||||
|
||||
// Create logs directory
|
||||
self::create_logs_directory();
|
||||
|
||||
// Create database tables if needed
|
||||
self::create_tables();
|
||||
|
||||
// Set activation timestamp
|
||||
update_option('wp_debug_activated_at', time());
|
||||
|
||||
// Flush rewrite rules
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default plugin options
|
||||
*/
|
||||
private static function set_default_options() {
|
||||
// Main toggle
|
||||
add_option('wp_debug_enabled', true);
|
||||
|
||||
// Module toggles (all enabled by default)
|
||||
add_option('wp_debug_module_logger', true);
|
||||
add_option('wp_debug_module_hook_monitor', true);
|
||||
add_option('wp_debug_module_template_tracer', true);
|
||||
add_option('wp_debug_module_asset_tracker', true);
|
||||
add_option('wp_debug_module_profiler', true);
|
||||
add_option('wp_debug_module_query_analyzer', true);
|
||||
add_option('wp_debug_module_frontend_panel', true);
|
||||
add_option('wp_debug_module_cli_commands', true);
|
||||
|
||||
// Logger settings
|
||||
add_option('wp_debug_log_level', 'INFO'); // INFO, WARNING, ERROR, DEBUG
|
||||
add_option('wp_debug_log_retention_days', 7); // Keep logs for 7 days
|
||||
add_option('wp_debug_log_to_file', true);
|
||||
add_option('wp_debug_log_to_db', true);
|
||||
|
||||
// Performance settings
|
||||
add_option('wp_debug_profiler_enabled', true);
|
||||
add_option('wp_debug_profiler_threshold', 1000); // Log functions taking >1000ms
|
||||
|
||||
// Frontend panel settings
|
||||
add_option('wp_debug_panel_enabled', true);
|
||||
add_option('wp_debug_panel_position', 'bottom-right'); // bottom-right, bottom-left, top-right, top-left
|
||||
|
||||
// Query analyzer settings
|
||||
add_option('wp_debug_query_threshold', 100); // Log queries taking >100ms
|
||||
add_option('wp_debug_detect_n_plus_one', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create logs directory
|
||||
*/
|
||||
private static function create_logs_directory() {
|
||||
$logs_dir = WP_CONTENT_DIR . '/logs/wp-debug';
|
||||
|
||||
if (!file_exists($logs_dir)) {
|
||||
wp_mkdir_p($logs_dir);
|
||||
|
||||
// Create .htaccess to protect logs
|
||||
$htaccess_file = $logs_dir . '/.htaccess';
|
||||
$htaccess_content = "Order deny,allow\nDeny from all";
|
||||
file_put_contents($htaccess_file, $htaccess_content);
|
||||
|
||||
// Create index.php to prevent directory listing
|
||||
$index_file = $logs_dir . '/index.php';
|
||||
$index_content = "<?php\n// Silence is golden.";
|
||||
file_put_contents($index_file, $index_content);
|
||||
}
|
||||
|
||||
// Set proper permissions
|
||||
if (file_exists($logs_dir)) {
|
||||
chmod($logs_dir, 0755);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create database tables
|
||||
*/
|
||||
private static function create_tables() {
|
||||
global $wpdb;
|
||||
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
|
||||
// Logs table
|
||||
$table_logs = $wpdb->prefix . 'wp_debug_logs';
|
||||
$sql_logs = "CREATE TABLE IF NOT EXISTS $table_logs (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
timestamp datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
level varchar(20) DEFAULT 'INFO' NOT NULL,
|
||||
message text NOT NULL,
|
||||
context text,
|
||||
user_id bigint(20),
|
||||
url varchar(255),
|
||||
ip_address varchar(45),
|
||||
PRIMARY KEY (id),
|
||||
KEY level (level),
|
||||
KEY timestamp (timestamp),
|
||||
KEY user_id (user_id)
|
||||
) $charset_collate;";
|
||||
|
||||
// Hooks table
|
||||
$table_hooks = $wpdb->prefix . 'wp_debug_hooks';
|
||||
$sql_hooks = "CREATE TABLE IF NOT EXISTS $table_hooks (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
timestamp datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
hook_name varchar(255) NOT NULL,
|
||||
hook_type varchar(20) NOT NULL,
|
||||
execution_time float,
|
||||
args_count int,
|
||||
PRIMARY KEY (id),
|
||||
KEY hook_name (hook_name),
|
||||
KEY timestamp (timestamp),
|
||||
KEY hook_type (hook_type)
|
||||
) $charset_collate;";
|
||||
|
||||
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||||
dbDelta($sql_logs);
|
||||
dbDelta($sql_hooks);
|
||||
}
|
||||
}
|
||||
224
wp-content/plugins/wp-debug/inc/admin-menu.php
Normal file
224
wp-content/plugins/wp-debug/inc/admin-menu.php
Normal file
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin Menu
|
||||
*
|
||||
* @package WP_Debug
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WP_Debug_Admin_Menu class
|
||||
*/
|
||||
class WP_Debug_Admin_Menu {
|
||||
|
||||
/**
|
||||
* Initialize admin menu
|
||||
*/
|
||||
public static function init() {
|
||||
// Add menu page under Tools
|
||||
add_management_page(
|
||||
__('WP Debug', 'wp-debug'), // Page title
|
||||
__('WP Debug', 'wp-debug'), // Menu title
|
||||
'manage_options', // Capability
|
||||
'wp-debug', // Menu slug
|
||||
array(__CLASS__, 'render_dashboard'), // Callback
|
||||
99 // Position
|
||||
);
|
||||
|
||||
// Enqueue admin styles and scripts
|
||||
add_action('admin_enqueue_scripts', array(__CLASS__, 'enqueue_admin_assets'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Render dashboard page
|
||||
*/
|
||||
public static function render_dashboard() {
|
||||
// Check user capabilities
|
||||
if (!current_user_can('manage_options')) {
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="wrap wp-debug-dashboard">
|
||||
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
|
||||
|
||||
<div class="wp-debug-header">
|
||||
<p><?php _e('Sistema avanzado de debug y diagnóstico para WordPress', 'wp-debug'); ?></p>
|
||||
<p><strong><?php _e('Versión:', 'wp-debug'); ?></strong> <?php echo esc_html(WP_DEBUG_VERSION); ?></p>
|
||||
</div>
|
||||
|
||||
<div class="wp-debug-cards">
|
||||
<!-- Status Card -->
|
||||
<div class="wp-debug-card">
|
||||
<h2><?php _e('Estado del Sistema', 'wp-debug'); ?></h2>
|
||||
<p>
|
||||
<strong><?php _e('Debug Activo:', 'wp-debug'); ?></strong>
|
||||
<?php echo get_option('wp_debug_enabled', true) ? '<span class="status-active">✓ Activo</span>' : '<span class="status-inactive">✗ Inactivo</span>'; ?>
|
||||
</p>
|
||||
<p>
|
||||
<strong><?php _e('WordPress:', 'wp-debug'); ?></strong>
|
||||
<?php echo esc_html(get_bloginfo('version')); ?>
|
||||
</p>
|
||||
<p>
|
||||
<strong><?php _e('PHP:', 'wp-debug'); ?></strong>
|
||||
<?php echo esc_html(phpversion()); ?>
|
||||
</p>
|
||||
<p>
|
||||
<strong><?php _e('Tema Activo:', 'wp-debug'); ?></strong>
|
||||
<?php echo esc_html(wp_get_theme()->get('Name')); ?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Modules Card -->
|
||||
<div class="wp-debug-card">
|
||||
<h2><?php _e('Módulos Instalados', 'wp-debug'); ?></h2>
|
||||
<?php
|
||||
$modules = WP_Debug_Autoloader::get_modules();
|
||||
foreach ($modules as $module_name => $module_data) {
|
||||
$enabled = get_option('wp_debug_module_' . $module_name, $module_data['enabled']);
|
||||
$status_class = $enabled ? 'status-active' : 'status-inactive';
|
||||
$status_text = $enabled ? '✓ Activo' : '✗ Inactivo';
|
||||
echo '<p><strong>' . esc_html(ucwords(str_replace('_', ' ', $module_name))) . ':</strong> <span class="' . esc_attr($status_class) . '">' . esc_html($status_text) . '</span></p>';
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions Card -->
|
||||
<div class="wp-debug-card">
|
||||
<h2><?php _e('Acciones Rápidas', 'wp-debug'); ?></h2>
|
||||
<p><a href="#" class="button button-secondary" onclick="alert('Panel de logs próximamente')">📋 Ver Logs</a></p>
|
||||
<p><a href="#" class="button button-secondary" onclick="alert('Monitor de hooks próximamente')">🔌 Hooks Monitor</a></p>
|
||||
<p><a href="#" class="button button-secondary" onclick="alert('Template tracer próximamente')">📄 Templates</a></p>
|
||||
<p><a href="#" class="button button-secondary" onclick="alert('Asset tracker próximamente')">📦 Assets</a></p>
|
||||
<p><a href="#" class="button button-secondary" onclick="alert('Performance profiler próximamente')">⚡ Performance</a></p>
|
||||
<p><a href="#" class="button button-secondary" onclick="alert('Query analyzer próximamente')">🗄️ Queries</a></p>
|
||||
</div>
|
||||
|
||||
<!-- WP-CLI Commands Card -->
|
||||
<div class="wp-debug-card">
|
||||
<h2><?php _e('Comandos WP-CLI', 'wp-debug'); ?></h2>
|
||||
<code>wp debug status</code><br>
|
||||
<code>wp debug hooks</code><br>
|
||||
<code>wp debug templates</code><br>
|
||||
<code>wp debug export</code><br>
|
||||
<code>wp debug clear</code>
|
||||
<p><small><?php _e('Ejecuta estos comandos desde la terminal para diagnóstico avanzado', 'wp-debug'); ?></small></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wp-debug-info">
|
||||
<h3><?php _e('Información del Plugin', 'wp-debug'); ?></h3>
|
||||
<p><?php _e('WP Debug es un sistema de diagnóstico avanzado que te permite:', 'wp-debug'); ?></p>
|
||||
<ul>
|
||||
<li>✓ <?php _e('Monitorear hooks de WordPress en tiempo real', 'wp-debug'); ?></li>
|
||||
<li>✓ <?php _e('Rastrear templates y template-parts cargados', 'wp-debug'); ?></li>
|
||||
<li>✓ <?php _e('Analizar assets CSS/JS con dependencias', 'wp-debug'); ?></li>
|
||||
<li>✓ <?php _e('Medir performance y detectar bottlenecks', 'wp-debug'); ?></li>
|
||||
<li>✓ <?php _e('Analizar queries SQL y detectar problemas N+1', 'wp-debug'); ?></li>
|
||||
<li>✓ <?php _e('Panel visual flotante en frontend (solo admins)', 'wp-debug'); ?></li>
|
||||
<li>✓ <?php _e('Comandos WP-CLI para diagnóstico desde terminal', 'wp-debug'); ?></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.wp-debug-dashboard {
|
||||
max-width: 1200px;
|
||||
}
|
||||
.wp-debug-header {
|
||||
background: #f0f0f1;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border-left: 4px solid #2271b1;
|
||||
}
|
||||
.wp-debug-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.wp-debug-card {
|
||||
background: #fff;
|
||||
border: 1px solid #c3c4c7;
|
||||
padding: 20px;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.04);
|
||||
}
|
||||
.wp-debug-card h2 {
|
||||
margin-top: 0;
|
||||
font-size: 1.2em;
|
||||
border-bottom: 2px solid #2271b1;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.wp-debug-card p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
.wp-debug-card code {
|
||||
display: block;
|
||||
background: #f0f0f1;
|
||||
padding: 5px 10px;
|
||||
margin: 5px 0;
|
||||
font-family: monospace;
|
||||
}
|
||||
.status-active {
|
||||
color: #00a32a;
|
||||
font-weight: bold;
|
||||
}
|
||||
.status-inactive {
|
||||
color: #d63638;
|
||||
font-weight: bold;
|
||||
}
|
||||
.wp-debug-info {
|
||||
background: #fff;
|
||||
border: 1px solid #c3c4c7;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.wp-debug-info ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
.wp-debug-info li {
|
||||
padding: 5px 0;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin assets
|
||||
*
|
||||
* @param string $hook Current admin page hook
|
||||
*/
|
||||
public static function enqueue_admin_assets($hook) {
|
||||
// Only load on our admin page
|
||||
if ('tools_page_wp-debug' !== $hook) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Enqueue admin CSS if exists
|
||||
if (file_exists(WP_DEBUG_PLUGIN_DIR . 'assets/css/admin.css')) {
|
||||
wp_enqueue_style(
|
||||
'wp-debug-admin',
|
||||
WP_DEBUG_PLUGIN_URL . 'assets/css/admin.css',
|
||||
array(),
|
||||
WP_DEBUG_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
// Enqueue admin JS if exists
|
||||
if (file_exists(WP_DEBUG_PLUGIN_DIR . 'assets/js/admin.js')) {
|
||||
wp_enqueue_script(
|
||||
'wp-debug-admin',
|
||||
WP_DEBUG_PLUGIN_URL . 'assets/js/admin.js',
|
||||
array('jquery'),
|
||||
WP_DEBUG_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
369
wp-content/plugins/wp-debug/inc/asset-tracker.php
Normal file
369
wp-content/plugins/wp-debug/inc/asset-tracker.php
Normal file
@@ -0,0 +1,369 @@
|
||||
<?php
|
||||
/**
|
||||
* Asset Tracker Module
|
||||
*
|
||||
* Tracks all enqueued CSS and JS assets, detects duplicates,
|
||||
* missing versions, and calculates file sizes.
|
||||
*
|
||||
* @package WP_Debug
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class WP_Debug_Asset_Tracker
|
||||
*
|
||||
* Tracks and analyzes enqueued CSS and JS assets.
|
||||
*/
|
||||
class WP_Debug_Asset_Tracker {
|
||||
|
||||
/**
|
||||
* Enqueued assets storage
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $assets_enqueued = array(
|
||||
'styles' => array(),
|
||||
'scripts' => array()
|
||||
);
|
||||
|
||||
/**
|
||||
* Initialize the asset tracker
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init() {
|
||||
// Hook into wp_enqueue_scripts with high priority to catch all assets
|
||||
add_action('wp_enqueue_scripts', array(__CLASS__, 'track_assets'), PHP_INT_MAX);
|
||||
|
||||
// Hook into admin_enqueue_scripts for admin assets
|
||||
add_action('admin_enqueue_scripts', array(__CLASS__, 'track_assets'), PHP_INT_MAX);
|
||||
|
||||
// Record assets on shutdown
|
||||
add_action('shutdown', array(__CLASS__, 'record_assets'), 1);
|
||||
|
||||
if (class_exists('WP_Debug_Logger')) {
|
||||
WP_Debug_Logger::debug('Asset Tracker initialized');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Track enqueued assets
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function track_assets() {
|
||||
global $wp_styles, $wp_scripts;
|
||||
|
||||
// Track styles
|
||||
if (isset($wp_styles) && is_object($wp_styles)) {
|
||||
self::track_styles($wp_styles);
|
||||
}
|
||||
|
||||
// Track scripts
|
||||
if (isset($wp_scripts) && is_object($wp_scripts)) {
|
||||
self::track_scripts($wp_scripts);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Track stylesheet assets
|
||||
*
|
||||
* @param WP_Styles $wp_styles WordPress styles object
|
||||
* @return void
|
||||
*/
|
||||
private static function track_styles($wp_styles) {
|
||||
if (empty($wp_styles->queue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($wp_styles->queue as $handle) {
|
||||
if (!isset($wp_styles->registered[$handle])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$style = $wp_styles->registered[$handle];
|
||||
|
||||
$asset_data = array(
|
||||
'handle' => $handle,
|
||||
'src' => $style->src,
|
||||
'dependencies' => $style->deps,
|
||||
'version' => $style->ver,
|
||||
'media' => $style->args,
|
||||
'type' => 'style',
|
||||
'size' => self::get_file_size($style->src),
|
||||
'local' => self::is_local_asset($style->src),
|
||||
'has_version' => !empty($style->ver),
|
||||
'extra' => isset($style->extra) ? $style->extra : array()
|
||||
);
|
||||
|
||||
self::$assets_enqueued['styles'][$handle] = $asset_data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Track script assets
|
||||
*
|
||||
* @param WP_Scripts $wp_scripts WordPress scripts object
|
||||
* @return void
|
||||
*/
|
||||
private static function track_scripts($wp_scripts) {
|
||||
if (empty($wp_scripts->queue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($wp_scripts->queue as $handle) {
|
||||
if (!isset($wp_scripts->registered[$handle])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$script = $wp_scripts->registered[$handle];
|
||||
|
||||
$asset_data = array(
|
||||
'handle' => $handle,
|
||||
'src' => $script->src,
|
||||
'dependencies' => $script->deps,
|
||||
'version' => $script->ver,
|
||||
'in_footer' => isset($script->extra['group']) && $script->extra['group'] === 1,
|
||||
'type' => 'script',
|
||||
'size' => self::get_file_size($script->src),
|
||||
'local' => self::is_local_asset($script->src),
|
||||
'has_version' => !empty($script->ver),
|
||||
'extra' => isset($script->extra) ? $script->extra : array()
|
||||
);
|
||||
|
||||
self::$assets_enqueued['scripts'][$handle] = $asset_data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if asset is local
|
||||
*
|
||||
* @param string $src Asset source URL
|
||||
* @return bool
|
||||
*/
|
||||
private static function is_local_asset($src) {
|
||||
if (empty($src)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$home_url = home_url();
|
||||
$site_url = site_url();
|
||||
|
||||
// Check if it starts with home or site URL
|
||||
if (strpos($src, $home_url) === 0 || strpos($src, $site_url) === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if it starts with relative path
|
||||
if (strpos($src, '/') === 0 && strpos($src, '//') !== 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file size for local assets
|
||||
*
|
||||
* @param string $src Asset source URL
|
||||
* @return int|null File size in bytes or null if not available
|
||||
*/
|
||||
private static function get_file_size($src) {
|
||||
if (empty($src) || !self::is_local_asset($src)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Convert URL to file path
|
||||
$file_path = self::url_to_path($src);
|
||||
|
||||
if (!$file_path || !file_exists($file_path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$size = @filesize($file_path);
|
||||
return $size !== false ? $size : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert asset URL to file system path
|
||||
*
|
||||
* @param string $src Asset source URL
|
||||
* @return string|false File path or false if not convertible
|
||||
*/
|
||||
private static function url_to_path($src) {
|
||||
// Remove query string
|
||||
$src = strtok($src, '?');
|
||||
|
||||
// Handle absolute URLs
|
||||
$home_url = home_url();
|
||||
$site_url = site_url();
|
||||
|
||||
if (strpos($src, $home_url) === 0) {
|
||||
$relative_path = str_replace($home_url, '', $src);
|
||||
return ABSPATH . ltrim($relative_path, '/');
|
||||
}
|
||||
|
||||
if (strpos($src, $site_url) === 0) {
|
||||
$relative_path = str_replace($site_url, '', $src);
|
||||
return ABSPATH . ltrim($relative_path, '/');
|
||||
}
|
||||
|
||||
// Handle relative URLs
|
||||
if (strpos($src, '/') === 0 && strpos($src, '//') !== 0) {
|
||||
return ABSPATH . ltrim($src, '/');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all tracked assets
|
||||
*
|
||||
* @return array All tracked assets
|
||||
*/
|
||||
public static function get_assets() {
|
||||
return self::$assets_enqueued;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect duplicate assets (same src, different handles)
|
||||
*
|
||||
* @return array Detected duplicates
|
||||
*/
|
||||
public static function get_duplicates() {
|
||||
$duplicates = array();
|
||||
$src_map = array();
|
||||
|
||||
// Check styles
|
||||
foreach (self::$assets_enqueued['styles'] as $handle => $asset) {
|
||||
if (empty($asset['src'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Normalize source (remove query strings for comparison)
|
||||
$normalized_src = strtok($asset['src'], '?');
|
||||
|
||||
if (!isset($src_map['styles'][$normalized_src])) {
|
||||
$src_map['styles'][$normalized_src] = array();
|
||||
}
|
||||
|
||||
$src_map['styles'][$normalized_src][] = $handle;
|
||||
}
|
||||
|
||||
// Check scripts
|
||||
foreach (self::$assets_enqueued['scripts'] as $handle => $asset) {
|
||||
if (empty($asset['src'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Normalize source (remove query strings for comparison)
|
||||
$normalized_src = strtok($asset['src'], '?');
|
||||
|
||||
if (!isset($src_map['scripts'][$normalized_src])) {
|
||||
$src_map['scripts'][$normalized_src] = array();
|
||||
}
|
||||
|
||||
$src_map['scripts'][$normalized_src][] = $handle;
|
||||
}
|
||||
|
||||
// Find duplicates
|
||||
foreach ($src_map as $type => $sources) {
|
||||
foreach ($sources as $src => $handles) {
|
||||
if (count($handles) > 1) {
|
||||
$duplicates[] = array(
|
||||
'type' => $type,
|
||||
'src' => $src,
|
||||
'handles' => $handles,
|
||||
'count' => count($handles)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $duplicates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get statistics about tracked assets
|
||||
*
|
||||
* @return array Asset statistics
|
||||
*/
|
||||
public static function get_statistics() {
|
||||
$total_styles = count(self::$assets_enqueued['styles']);
|
||||
$total_scripts = count(self::$assets_enqueued['scripts']);
|
||||
$total_size = 0;
|
||||
$local_count = 0;
|
||||
$external_count = 0;
|
||||
|
||||
// Calculate statistics for styles
|
||||
foreach (self::$assets_enqueued['styles'] as $asset) {
|
||||
if ($asset['size'] !== null) {
|
||||
$total_size += $asset['size'];
|
||||
}
|
||||
if ($asset['local']) {
|
||||
$local_count++;
|
||||
} else {
|
||||
$external_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate statistics for scripts
|
||||
foreach (self::$assets_enqueued['scripts'] as $asset) {
|
||||
if ($asset['size'] !== null) {
|
||||
$total_size += $asset['size'];
|
||||
}
|
||||
if ($asset['local']) {
|
||||
$local_count++;
|
||||
} else {
|
||||
$external_count++;
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'total_assets' => $total_styles + $total_scripts,
|
||||
'total_styles' => $total_styles,
|
||||
'total_scripts' => $total_scripts,
|
||||
'total_size' => $total_size,
|
||||
'total_size_formatted' => size_format($total_size),
|
||||
'local_assets' => $local_count,
|
||||
'external_assets' => $external_count,
|
||||
'duplicates_count' => count(self::get_duplicates()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record assets and save to transient
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function record_assets() {
|
||||
if (empty(self::$assets_enqueued['styles']) && empty(self::$assets_enqueued['scripts'])) {
|
||||
if (class_exists('WP_Debug_Logger')) {
|
||||
WP_Debug_Logger::debug('No assets to record');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$assets_data = array(
|
||||
'assets' => self::$assets_enqueued,
|
||||
'duplicates' => self::get_duplicates(),
|
||||
'statistics' => self::get_statistics(),
|
||||
'timestamp' => current_time('timestamp'),
|
||||
);
|
||||
|
||||
// Save to transient (expires in 1 hour)
|
||||
set_transient('wp_debug_assets_data', $assets_data, HOUR_IN_SECONDS);
|
||||
|
||||
if (class_exists('WP_Debug_Logger')) {
|
||||
WP_Debug_Logger::debug('Asset Tracker recorded assets', array(
|
||||
'total_styles' => count(self::$assets_enqueued['styles']),
|
||||
'total_scripts' => count(self::$assets_enqueued['scripts']),
|
||||
'duplicates' => count($assets_data['duplicates']),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
154
wp-content/plugins/wp-debug/inc/autoloader.php
Normal file
154
wp-content/plugins/wp-debug/inc/autoloader.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
/**
|
||||
* Autoloader - Loads all plugin modules
|
||||
*
|
||||
* @package WP_Debug
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WP_Debug_Autoloader class
|
||||
*/
|
||||
class WP_Debug_Autoloader {
|
||||
|
||||
/**
|
||||
* Modules to load
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $modules = array(
|
||||
'logger' => array(
|
||||
'file' => 'logger.php',
|
||||
'class' => 'WP_Debug_Logger',
|
||||
'enabled' => true,
|
||||
),
|
||||
'hook_monitor' => array(
|
||||
'file' => 'hook-monitor.php',
|
||||
'class' => 'WP_Debug_Hook_Monitor',
|
||||
'enabled' => true,
|
||||
),
|
||||
'template_tracer' => array(
|
||||
'file' => 'template-tracer.php',
|
||||
'class' => 'WP_Debug_Template_Tracer',
|
||||
'enabled' => true,
|
||||
),
|
||||
'asset_tracker' => array(
|
||||
'file' => 'asset-tracker.php',
|
||||
'class' => 'WP_Debug_Asset_Tracker',
|
||||
'enabled' => true,
|
||||
),
|
||||
'profiler' => array(
|
||||
'file' => 'profiler.php',
|
||||
'class' => 'WP_Debug_Profiler',
|
||||
'enabled' => true,
|
||||
),
|
||||
'query_analyzer' => array(
|
||||
'file' => 'query-analyzer.php',
|
||||
'class' => 'WP_Debug_Query_Analyzer',
|
||||
'enabled' => true,
|
||||
),
|
||||
'frontend_panel' => array(
|
||||
'file' => 'frontend-panel.php',
|
||||
'class' => 'WP_Debug_Frontend_Panel',
|
||||
'enabled' => true,
|
||||
),
|
||||
'cli_commands' => array(
|
||||
'file' => 'cli-commands.php',
|
||||
'class' => 'WP_Debug_CLI_Commands',
|
||||
'enabled' => true,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Initialize autoloader
|
||||
*/
|
||||
public static function init() {
|
||||
// Check if debug is enabled globally
|
||||
$debug_enabled = get_option('wp_debug_enabled', true);
|
||||
|
||||
if (!$debug_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load each module
|
||||
foreach (self::$modules as $module_name => $module_data) {
|
||||
self::load_module($module_name, $module_data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a single module
|
||||
*
|
||||
* @param string $module_name Module name
|
||||
* @param array $module_data Module data
|
||||
*/
|
||||
private static function load_module($module_name, $module_data) {
|
||||
// Check if module is enabled
|
||||
$module_enabled = get_option('wp_debug_module_' . $module_name, $module_data['enabled']);
|
||||
|
||||
if (!$module_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build file path
|
||||
$file_path = WP_DEBUG_PLUGIN_DIR . 'inc/' . $module_data['file'];
|
||||
|
||||
// Check if file exists
|
||||
if (!file_exists($file_path)) {
|
||||
if (WP_DEBUG) {
|
||||
error_log(sprintf('WP Debug: Module file not found: %s', $file_path));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the file
|
||||
require_once $file_path;
|
||||
|
||||
// Initialize the class if it has an init method
|
||||
if (class_exists($module_data['class']) && method_exists($module_data['class'], 'init')) {
|
||||
call_user_func(array($module_data['class'], 'init'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of available modules
|
||||
*
|
||||
* @return array List of modules
|
||||
*/
|
||||
public static function get_modules() {
|
||||
return self::$modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a module
|
||||
*
|
||||
* @param string $module_name Module name
|
||||
* @return bool True on success
|
||||
*/
|
||||
public static function enable_module($module_name) {
|
||||
if (!isset(self::$modules[$module_name])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return update_option('wp_debug_module_' . $module_name, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable a module
|
||||
*
|
||||
* @param string $module_name Module name
|
||||
* @return bool True on success
|
||||
*/
|
||||
public static function disable_module($module_name) {
|
||||
if (!isset(self::$modules[$module_name])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return update_option('wp_debug_module_' . $module_name, false);
|
||||
}
|
||||
}
|
||||
543
wp-content/plugins/wp-debug/inc/cli-commands.php
Normal file
543
wp-content/plugins/wp-debug/inc/cli-commands.php
Normal file
@@ -0,0 +1,543 @@
|
||||
<?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");
|
||||
}
|
||||
}
|
||||
47
wp-content/plugins/wp-debug/inc/deactivation.php
Normal file
47
wp-content/plugins/wp-debug/inc/deactivation.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Deactivation
|
||||
*
|
||||
* @package WP_Debug
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WP_Debug_Deactivation class
|
||||
*/
|
||||
class WP_Debug_Deactivation {
|
||||
|
||||
/**
|
||||
* Run deactivation tasks
|
||||
*/
|
||||
public static function deactivate() {
|
||||
// Clean up transients
|
||||
self::cleanup_transients();
|
||||
|
||||
// Flush rewrite rules
|
||||
flush_rewrite_rules();
|
||||
|
||||
// Note: We DO NOT delete logs or database tables
|
||||
// This preserves data in case user reactivates the plugin
|
||||
// User can manually delete via Tools > WP Debug if desired
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up temporary transients
|
||||
*/
|
||||
private static function cleanup_transients() {
|
||||
// Delete temporary transients only
|
||||
delete_transient('wp_debug_temp_data');
|
||||
delete_transient('wp_debug_cache');
|
||||
|
||||
// Clean up expired transients
|
||||
global $wpdb;
|
||||
$wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_wp_debug_%' AND option_value < UNIX_TIMESTAMP()");
|
||||
$wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_wp_debug_%' AND option_name NOT IN (SELECT CONCAT('_transient_', SUBSTRING(option_name, 19)) FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_wp_debug_%')");
|
||||
}
|
||||
}
|
||||
373
wp-content/plugins/wp-debug/inc/frontend-panel.php
Normal file
373
wp-content/plugins/wp-debug/inc/frontend-panel.php
Normal file
@@ -0,0 +1,373 @@
|
||||
<?php
|
||||
/**
|
||||
* Frontend Panel Module
|
||||
*
|
||||
* Panel visual flotante en frontend para administradores
|
||||
*
|
||||
* @package WP_Debug
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WP_Debug_Frontend_Panel class
|
||||
*/
|
||||
class WP_Debug_Frontend_Panel {
|
||||
|
||||
/**
|
||||
* Initialize frontend panel
|
||||
*/
|
||||
public static function init() {
|
||||
// Only for admins
|
||||
if (!current_user_can('manage_options')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Enqueue assets
|
||||
add_action('wp_enqueue_scripts', array(__CLASS__, 'enqueue_assets'));
|
||||
|
||||
// Render panel
|
||||
add_action('wp_footer', array(__CLASS__, 'render_panel'), 999);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue assets
|
||||
*/
|
||||
public static function enqueue_assets() {
|
||||
// Enqueue CSS
|
||||
wp_enqueue_style(
|
||||
'wp-debug-panel',
|
||||
WP_DEBUG_PLUGIN_URL . 'assets/css/debug-panel.css',
|
||||
array(),
|
||||
WP_DEBUG_VERSION
|
||||
);
|
||||
|
||||
// Enqueue JS
|
||||
wp_enqueue_script(
|
||||
'wp-debug-panel',
|
||||
WP_DEBUG_PLUGIN_URL . 'assets/js/debug-panel.js',
|
||||
array('jquery'),
|
||||
WP_DEBUG_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
// Localize script with data
|
||||
wp_localize_script('wp-debug-panel', 'wpDebugData', array(
|
||||
'ajaxUrl' => admin_url('admin-ajax.php'),
|
||||
'nonce' => wp_create_nonce('wp_debug_panel'),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Render panel
|
||||
*/
|
||||
public static function render_panel() {
|
||||
// Load data from transients
|
||||
$metrics = get_transient('wp_debug_last_metrics');
|
||||
$hooks_data = get_transient('wp_debug_hooks_data');
|
||||
$templates_data = get_transient('wp_debug_templates_data');
|
||||
$assets_data = get_transient('wp_debug_assets_data');
|
||||
$queries_data = get_transient('wp_debug_queries_data');
|
||||
|
||||
?>
|
||||
<div id="wp-debug-panel" class="wp-debug-panel" style="display: none;">
|
||||
<div class="wp-debug-panel-header">
|
||||
<h3>WP Debug Panel</h3>
|
||||
<div class="wp-debug-panel-tabs">
|
||||
<button class="wp-debug-tab active" data-tab="overview">Overview</button>
|
||||
<button class="wp-debug-tab" data-tab="performance">Performance</button>
|
||||
<button class="wp-debug-tab" data-tab="hooks">Hooks</button>
|
||||
<button class="wp-debug-tab" data-tab="templates">Templates</button>
|
||||
<button class="wp-debug-tab" data-tab="assets">Assets</button>
|
||||
<button class="wp-debug-tab" data-tab="queries">Queries</button>
|
||||
</div>
|
||||
<button class="wp-debug-panel-close">×</button>
|
||||
</div>
|
||||
|
||||
<div class="wp-debug-panel-content">
|
||||
<!-- Overview Tab -->
|
||||
<div class="wp-debug-tab-content active" data-tab="overview">
|
||||
<h4>System Overview</h4>
|
||||
<table class="wp-debug-table">
|
||||
<tr>
|
||||
<td><strong>WordPress Version:</strong></td>
|
||||
<td><?php echo get_bloginfo('version'); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>PHP Version:</strong></td>
|
||||
<td><?php echo phpversion(); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Active Theme:</strong></td>
|
||||
<td><?php echo wp_get_theme()->get('Name'); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Memory Usage:</strong></td>
|
||||
<td><?php echo size_format(memory_get_usage()); ?> / <?php echo size_format(memory_get_peak_usage()); ?> peak</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Execution Time:</strong></td>
|
||||
<td><?php echo round(timer_stop(0), 3); ?>s</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h4>Module Status</h4>
|
||||
<ul class="wp-debug-module-list">
|
||||
<?php
|
||||
$modules = array(
|
||||
'logger' => 'Logger',
|
||||
'profiler' => 'Profiler',
|
||||
'hook_monitor' => 'Hook Monitor',
|
||||
'template_tracer' => 'Template Tracer',
|
||||
'asset_tracker' => 'Asset Tracker',
|
||||
'query_analyzer' => 'Query Analyzer',
|
||||
);
|
||||
|
||||
foreach ($modules as $module_key => $module_name) {
|
||||
$enabled = get_option('wp_debug_module_' . $module_key, true);
|
||||
$status = $enabled ? '<span class="status-active">Active</span>' : '<span class="status-inactive">Inactive</span>';
|
||||
echo "<li>{$module_name}: {$status}</li>";
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Performance Tab -->
|
||||
<div class="wp-debug-tab-content" data-tab="performance">
|
||||
<h4>Performance Metrics</h4>
|
||||
<?php if ($metrics): ?>
|
||||
<table class="wp-debug-table">
|
||||
<tr>
|
||||
<td><strong>Execution Time:</strong></td>
|
||||
<td><?php echo round($metrics['execution_time'], 3); ?>s</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Memory Peak:</strong></td>
|
||||
<td><?php echo size_format($metrics['memory_peak']); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Memory Current:</strong></td>
|
||||
<td><?php echo size_format($metrics['memory_current']); ?></td>
|
||||
</tr>
|
||||
<?php if (isset($metrics['server_load'])): ?>
|
||||
<tr>
|
||||
<td><strong>Server Load:</strong></td>
|
||||
<td><?php echo esc_html($metrics['server_load']); ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
|
||||
<?php if (!empty($metrics['timers'])): ?>
|
||||
<h4>Custom Timers</h4>
|
||||
<table class="wp-debug-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Timer</th>
|
||||
<th>Elapsed</th>
|
||||
<th>Memory Used</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($metrics['timers'] as $name => $timer): ?>
|
||||
<tr>
|
||||
<td><?php echo esc_html($name); ?></td>
|
||||
<td><?php echo round($timer['elapsed'], 4); ?>s</td>
|
||||
<td><?php echo size_format($timer['memory_used']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
<?php else: ?>
|
||||
<p>No performance data available.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Hooks Tab -->
|
||||
<div class="wp-debug-tab-content" data-tab="hooks">
|
||||
<h4>WordPress Hooks</h4>
|
||||
<?php if ($hooks_data): ?>
|
||||
<p><strong>Total Hooks:</strong> <?php echo $hooks_data['total']; ?> | <strong>Unique:</strong> <?php echo $hooks_data['unique']; ?></p>
|
||||
|
||||
<h5>Most Frequent Hooks</h5>
|
||||
<table class="wp-debug-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Hook Name</th>
|
||||
<th>Count</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$top_hooks = array_slice($hooks_data['counts'], 0, 20, true);
|
||||
foreach ($top_hooks as $hook => $count):
|
||||
?>
|
||||
<tr>
|
||||
<td><code><?php echo esc_html($hook); ?></code></td>
|
||||
<td><?php echo $count; ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php else: ?>
|
||||
<p>No hooks data available.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Templates Tab -->
|
||||
<div class="wp-debug-tab-content" data-tab="templates">
|
||||
<h4>Loaded Templates</h4>
|
||||
<?php if ($templates_data): ?>
|
||||
<?php if (!empty($templates_data['templates'])): ?>
|
||||
<h5>Main Templates</h5>
|
||||
<ul class="wp-debug-list">
|
||||
<?php foreach ($templates_data['templates'] as $template): ?>
|
||||
<li>
|
||||
<strong><?php echo esc_html($template['name']); ?></strong>
|
||||
<span class="template-type"><?php echo esc_html($template['type']); ?></span>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($templates_data['parts'])): ?>
|
||||
<h5>Template Parts (<?php echo count($templates_data['parts']); ?>)</h5>
|
||||
<ul class="wp-debug-list">
|
||||
<?php foreach (array_slice($templates_data['parts'], 0, 20) as $part): ?>
|
||||
<li>
|
||||
<code><?php echo esc_html($part['file']); ?></code>
|
||||
<?php if (!$part['found']): ?>
|
||||
<span class="status-error">Not Found</span>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($templates_data['missing'])): ?>
|
||||
<h5>Missing Templates</h5>
|
||||
<ul class="wp-debug-list">
|
||||
<?php foreach ($templates_data['missing'] as $missing): ?>
|
||||
<li class="missing">
|
||||
<code><?php echo esc_html($missing['file']); ?></code>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
<?php else: ?>
|
||||
<p>No template data available.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Assets Tab -->
|
||||
<div class="wp-debug-tab-content" data-tab="assets">
|
||||
<h4>Enqueued Assets</h4>
|
||||
<?php if ($assets_data): ?>
|
||||
<?php if (isset($assets_data['statistics'])): ?>
|
||||
<table class="wp-debug-table">
|
||||
<tr>
|
||||
<td><strong>Total Assets:</strong></td>
|
||||
<td><?php echo $assets_data['statistics']['total_assets']; ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Styles:</strong></td>
|
||||
<td><?php echo $assets_data['statistics']['total_styles']; ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Scripts:</strong></td>
|
||||
<td><?php echo $assets_data['statistics']['total_scripts']; ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Total Size:</strong></td>
|
||||
<td><?php echo $assets_data['statistics']['total_size_formatted']; ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Duplicates:</strong></td>
|
||||
<td><?php echo $assets_data['statistics']['duplicates_count']; ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($assets_data['duplicates'])): ?>
|
||||
<h5>Duplicate Assets</h5>
|
||||
<ul class="wp-debug-list">
|
||||
<?php foreach ($assets_data['duplicates'] as $dup): ?>
|
||||
<li class="duplicate">
|
||||
<strong><?php echo esc_html($dup['type']); ?>:</strong>
|
||||
<?php echo implode(', ', array_map('esc_html', $dup['handles'])); ?>
|
||||
<span class="count">(<?php echo $dup['count']; ?>x)</span>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
<?php else: ?>
|
||||
<p>No assets data available.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Queries Tab -->
|
||||
<div class="wp-debug-tab-content" data-tab="queries">
|
||||
<h4>Database Queries</h4>
|
||||
<?php if ($queries_data): ?>
|
||||
<table class="wp-debug-table">
|
||||
<tr>
|
||||
<td><strong>Total Queries:</strong></td>
|
||||
<td><?php echo $queries_data['total_queries']; ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Total Time:</strong></td>
|
||||
<td><?php echo round($queries_data['total_time'], 4); ?>s</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Slow Queries:</strong></td>
|
||||
<td><?php echo count($queries_data['slow_queries']); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>N+1 Patterns:</strong></td>
|
||||
<td><?php echo count($queries_data['n_plus_one']); ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<?php if (!empty($queries_data['slow_queries'])): ?>
|
||||
<h5>Slow Queries</h5>
|
||||
<div class="wp-debug-queries">
|
||||
<?php foreach (array_slice($queries_data['slow_queries'], 0, 5) as $query): ?>
|
||||
<div class="query-item">
|
||||
<div class="query-time"><?php echo round($query['elapsed'], 4); ?>s</div>
|
||||
<code class="query-sql"><?php echo esc_html(substr($query['sql'], 0, 150)); ?>...</code>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($queries_data['n_plus_one'])): ?>
|
||||
<h5>N+1 Query Patterns</h5>
|
||||
<div class="wp-debug-queries">
|
||||
<?php foreach (array_slice($queries_data['n_plus_one'], 0, 3) as $pattern): ?>
|
||||
<div class="query-item n-plus-one">
|
||||
<div class="query-count"><?php echo $pattern['count']; ?>x</div>
|
||||
<div class="query-time"><?php echo round($pattern['total_time'], 4); ?>s total</div>
|
||||
<code class="query-sql"><?php echo esc_html(substr($pattern['example'], 0, 150)); ?>...</code>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php else: ?>
|
||||
<p>No query data available. Make sure SAVEQUERIES is enabled.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toggle Button -->
|
||||
<button id="wp-debug-toggle" class="wp-debug-toggle">
|
||||
<span class="dashicons dashicons-admin-tools"></span>
|
||||
<span class="label">Debug</span>
|
||||
</button>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
193
wp-content/plugins/wp-debug/inc/hook-monitor.php
Normal file
193
wp-content/plugins/wp-debug/inc/hook-monitor.php
Normal file
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
/**
|
||||
* Hook Monitor Module
|
||||
*
|
||||
* Rastrea todos los hooks de WordPress ejecutados
|
||||
*
|
||||
* @package WP_Debug
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WP_Debug_Hook_Monitor class
|
||||
*/
|
||||
class WP_Debug_Hook_Monitor {
|
||||
|
||||
/**
|
||||
* Hooks executed
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $hooks_executed = array();
|
||||
|
||||
/**
|
||||
* Hook execution count
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $hook_counts = array();
|
||||
|
||||
/**
|
||||
* Initialize hook monitor
|
||||
*/
|
||||
public static function init() {
|
||||
// Monitor all actions
|
||||
add_action('all', array(__CLASS__, 'track_hook'), 1);
|
||||
|
||||
// Record hooks on shutdown
|
||||
add_action('shutdown', array(__CLASS__, 'record_hooks'), 999);
|
||||
}
|
||||
|
||||
/**
|
||||
* Track hook execution
|
||||
*
|
||||
* @param string $hook Hook name
|
||||
*/
|
||||
public static function track_hook($hook) {
|
||||
// Skip our own hooks to avoid recursion
|
||||
if (strpos($hook, 'wp_debug_') === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip some noisy hooks
|
||||
$skip_hooks = array('gettext', 'gettext_with_context', 'ngettext', 'all');
|
||||
if (in_array($hook, $skip_hooks)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$start_time = microtime(true);
|
||||
|
||||
// Get backtrace
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
|
||||
$caller = 'unknown';
|
||||
if (isset($backtrace[2]['file'])) {
|
||||
$caller = basename($backtrace[2]['file']) . ':' . $backtrace[2]['line'];
|
||||
}
|
||||
|
||||
// Store hook info
|
||||
$hook_info = array(
|
||||
'hook' => $hook,
|
||||
'type' => current_filter() === $hook ? 'action' : 'filter',
|
||||
'timestamp' => microtime(true),
|
||||
'caller' => $caller,
|
||||
'args_count' => func_num_args() - 1,
|
||||
);
|
||||
|
||||
self::$hooks_executed[] = $hook_info;
|
||||
|
||||
// Count executions
|
||||
if (!isset(self::$hook_counts[$hook])) {
|
||||
self::$hook_counts[$hook] = 0;
|
||||
}
|
||||
self::$hook_counts[$hook]++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get executed hooks
|
||||
*
|
||||
* @param array $args Query arguments
|
||||
* @return array Hooks
|
||||
*/
|
||||
public static function get_hooks($args = array()) {
|
||||
$defaults = array(
|
||||
'search' => '',
|
||||
'limit' => 100,
|
||||
);
|
||||
|
||||
$args = wp_parse_args($args, $defaults);
|
||||
|
||||
$hooks = self::$hooks_executed;
|
||||
|
||||
// Filter by search
|
||||
if ($args['search']) {
|
||||
$hooks = array_filter($hooks, function($hook) use ($args) {
|
||||
return strpos($hook['hook'], $args['search']) !== false;
|
||||
});
|
||||
}
|
||||
|
||||
// Limit results
|
||||
if ($args['limit'] > 0) {
|
||||
$hooks = array_slice($hooks, 0, $args['limit']);
|
||||
}
|
||||
|
||||
return $hooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hook counts
|
||||
*
|
||||
* @return array Hook counts
|
||||
*/
|
||||
public static function get_hook_counts() {
|
||||
// Sort by count descending
|
||||
arsort(self::$hook_counts);
|
||||
return self::$hook_counts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hooks called multiple times
|
||||
*
|
||||
* @param int $threshold Minimum count
|
||||
* @return array Hooks
|
||||
*/
|
||||
public static function get_frequent_hooks($threshold = 10) {
|
||||
return array_filter(self::$hook_counts, function($count) use ($threshold) {
|
||||
return $count >= $threshold;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Record hooks on shutdown
|
||||
*/
|
||||
public static function record_hooks() {
|
||||
$total_hooks = count(self::$hooks_executed);
|
||||
$unique_hooks = count(self::$hook_counts);
|
||||
|
||||
// Log summary
|
||||
if (class_exists('WP_Debug_Logger')) {
|
||||
WP_Debug_Logger::debug("Hooks executed", array(
|
||||
'total' => $total_hooks,
|
||||
'unique' => $unique_hooks,
|
||||
'top_5' => array_slice(self::$hook_counts, 0, 5, true),
|
||||
));
|
||||
}
|
||||
|
||||
// Store for frontend panel
|
||||
set_transient('wp_debug_hooks_data', array(
|
||||
'total' => $total_hooks,
|
||||
'unique' => $unique_hooks,
|
||||
'hooks' => self::$hooks_executed,
|
||||
'counts' => self::$hook_counts,
|
||||
), 3600);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if hook was executed
|
||||
*
|
||||
* @param string $hook Hook name
|
||||
* @return bool True if executed
|
||||
*/
|
||||
public static function was_hook_executed($hook) {
|
||||
return isset(self::$hook_counts[$hook]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hook execution time
|
||||
*
|
||||
* @param string $hook Hook name
|
||||
* @return float|null Execution time in seconds
|
||||
*/
|
||||
public static function get_hook_execution_time($hook) {
|
||||
foreach (self::$hooks_executed as $hook_info) {
|
||||
if ($hook_info['hook'] === $hook && isset($hook_info['execution_time'])) {
|
||||
return $hook_info['execution_time'];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
335
wp-content/plugins/wp-debug/inc/logger.php
Normal file
335
wp-content/plugins/wp-debug/inc/logger.php
Normal file
@@ -0,0 +1,335 @@
|
||||
<?php
|
||||
/**
|
||||
* Logger Module
|
||||
*
|
||||
* Sistema de logging centralizado con soporte para archivos y base de datos
|
||||
*
|
||||
* @package WP_Debug
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WP_Debug_Logger class
|
||||
*/
|
||||
class WP_Debug_Logger {
|
||||
|
||||
/**
|
||||
* Log levels
|
||||
*/
|
||||
const LEVEL_INFO = 'INFO';
|
||||
const LEVEL_WARNING = 'WARNING';
|
||||
const LEVEL_ERROR = 'ERROR';
|
||||
const LEVEL_DEBUG = 'DEBUG';
|
||||
|
||||
/**
|
||||
* Log file handle
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
private static $file_handle = null;
|
||||
|
||||
/**
|
||||
* Initialize logger
|
||||
*/
|
||||
public static function init() {
|
||||
// Hook into shutdown to close file handle
|
||||
add_action('shutdown', array(__CLASS__, 'close_file_handle'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message
|
||||
*
|
||||
* @param string $message Message to log
|
||||
* @param string $level Log level
|
||||
* @param array $context Additional context
|
||||
* @return bool True on success
|
||||
*/
|
||||
public static function log($message, $level = self::LEVEL_INFO, $context = array()) {
|
||||
// Check if logging is enabled
|
||||
if (!get_option('wp_debug_enabled', true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate level
|
||||
if (!in_array($level, array(self::LEVEL_INFO, self::LEVEL_WARNING, self::LEVEL_ERROR, self::LEVEL_DEBUG))) {
|
||||
$level = self::LEVEL_INFO;
|
||||
}
|
||||
|
||||
// Get current log level setting
|
||||
$min_level = get_option('wp_debug_log_level', self::LEVEL_INFO);
|
||||
if (!self::should_log($level, $min_level)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepare log entry
|
||||
$log_entry = self::prepare_log_entry($message, $level, $context);
|
||||
|
||||
// Log to file
|
||||
$log_to_file = get_option('wp_debug_log_to_file', true);
|
||||
if ($log_to_file) {
|
||||
self::log_to_file($log_entry);
|
||||
}
|
||||
|
||||
// Log to database
|
||||
$log_to_db = get_option('wp_debug_log_to_db', true);
|
||||
if ($log_to_db) {
|
||||
self::log_to_database($log_entry);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if message should be logged based on level
|
||||
*
|
||||
* @param string $level Message level
|
||||
* @param string $min_level Minimum level to log
|
||||
* @return bool True if should log
|
||||
*/
|
||||
private static function should_log($level, $min_level) {
|
||||
$levels = array(
|
||||
self::LEVEL_DEBUG => 0,
|
||||
self::LEVEL_INFO => 1,
|
||||
self::LEVEL_WARNING => 2,
|
||||
self::LEVEL_ERROR => 3,
|
||||
);
|
||||
|
||||
return $levels[$level] >= $levels[$min_level];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare log entry with all context
|
||||
*
|
||||
* @param string $message Message
|
||||
* @param string $level Level
|
||||
* @param array $context Context
|
||||
* @return array Log entry
|
||||
*/
|
||||
private static function prepare_log_entry($message, $level, $context) {
|
||||
$user = wp_get_current_user();
|
||||
|
||||
return array(
|
||||
'timestamp' => current_time('mysql'),
|
||||
'level' => $level,
|
||||
'message' => $message,
|
||||
'context' => !empty($context) ? json_encode($context) : null,
|
||||
'user_id' => $user->ID,
|
||||
'url' => isset($_SERVER['REQUEST_URI']) ? esc_url_raw($_SERVER['REQUEST_URI']) : '',
|
||||
'ip_address' => self::get_client_ip(),
|
||||
'backtrace' => self::get_backtrace(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log to file
|
||||
*
|
||||
* @param array $log_entry Log entry
|
||||
* @return bool True on success
|
||||
*/
|
||||
private static function log_to_file($log_entry) {
|
||||
$logs_dir = WP_CONTENT_DIR . '/logs/wp-debug';
|
||||
$log_file = $logs_dir . '/debug-' . date('Y-m-d') . '.log';
|
||||
|
||||
// Ensure directory exists
|
||||
if (!file_exists($logs_dir)) {
|
||||
wp_mkdir_p($logs_dir);
|
||||
}
|
||||
|
||||
// Prepare log line
|
||||
$log_line = sprintf(
|
||||
"[%s] [%s] %s | User: %s | URL: %s | IP: %s\n",
|
||||
$log_entry['timestamp'],
|
||||
$log_entry['level'],
|
||||
$log_entry['message'],
|
||||
$log_entry['user_id'],
|
||||
$log_entry['url'],
|
||||
$log_entry['ip_address']
|
||||
);
|
||||
|
||||
// Add context if exists
|
||||
if ($log_entry['context']) {
|
||||
$log_line .= sprintf(" Context: %s\n", $log_entry['context']);
|
||||
}
|
||||
|
||||
// Add backtrace if exists
|
||||
if ($log_entry['backtrace']) {
|
||||
$log_line .= sprintf(" Backtrace: %s\n", $log_entry['backtrace']);
|
||||
}
|
||||
|
||||
// Write to file
|
||||
return file_put_contents($log_file, $log_line, FILE_APPEND | LOCK_EX) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log to database
|
||||
*
|
||||
* @param array $log_entry Log entry
|
||||
* @return bool True on success
|
||||
*/
|
||||
private static function log_to_database($log_entry) {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->prefix . 'wp_debug_logs';
|
||||
|
||||
return $wpdb->insert(
|
||||
$table,
|
||||
array(
|
||||
'timestamp' => $log_entry['timestamp'],
|
||||
'level' => $log_entry['level'],
|
||||
'message' => $log_entry['message'],
|
||||
'context' => $log_entry['context'],
|
||||
'user_id' => $log_entry['user_id'],
|
||||
'url' => $log_entry['url'],
|
||||
'ip_address' => $log_entry['ip_address'],
|
||||
),
|
||||
array('%s', '%s', '%s', '%s', '%d', '%s', '%s')
|
||||
) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get client IP address
|
||||
*
|
||||
* @return string IP address
|
||||
*/
|
||||
private static function get_client_ip() {
|
||||
$ip = '';
|
||||
|
||||
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
|
||||
$ip = $_SERVER['HTTP_CLIENT_IP'];
|
||||
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
} elseif (!empty($_SERVER['REMOTE_ADDR'])) {
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
|
||||
return sanitize_text_field($ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get backtrace
|
||||
*
|
||||
* @return string Backtrace string
|
||||
*/
|
||||
private static function get_backtrace() {
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5);
|
||||
$trace_string = '';
|
||||
|
||||
foreach ($backtrace as $index => $trace) {
|
||||
if (isset($trace['file']) && isset($trace['line'])) {
|
||||
$trace_string .= sprintf(
|
||||
"%s:%s",
|
||||
basename($trace['file']),
|
||||
$trace['line']
|
||||
);
|
||||
if ($index < count($backtrace) - 1) {
|
||||
$trace_string .= ' -> ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $trace_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get logs from database
|
||||
*
|
||||
* @param array $args Query arguments
|
||||
* @return array Logs
|
||||
*/
|
||||
public static function get_logs($args = array()) {
|
||||
global $wpdb;
|
||||
|
||||
$defaults = array(
|
||||
'level' => null,
|
||||
'limit' => 100,
|
||||
'offset' => 0,
|
||||
'orderby' => 'timestamp',
|
||||
'order' => 'DESC',
|
||||
);
|
||||
|
||||
$args = wp_parse_args($args, $defaults);
|
||||
|
||||
$table = $wpdb->prefix . 'wp_debug_logs';
|
||||
$where = '1=1';
|
||||
|
||||
if ($args['level']) {
|
||||
$where .= $wpdb->prepare(' AND level = %s', $args['level']);
|
||||
}
|
||||
|
||||
$query = $wpdb->prepare(
|
||||
"SELECT * FROM {$table} WHERE {$where} ORDER BY {$args['orderby']} {$args['order']} LIMIT %d OFFSET %d",
|
||||
$args['limit'],
|
||||
$args['offset']
|
||||
);
|
||||
|
||||
return $wpdb->get_results($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear old logs
|
||||
*
|
||||
* @param int $days Days to keep
|
||||
* @return bool True on success
|
||||
*/
|
||||
public static function clear_old_logs($days = null) {
|
||||
if ($days === null) {
|
||||
$days = get_option('wp_debug_log_retention_days', 7);
|
||||
}
|
||||
|
||||
// Clear database logs
|
||||
global $wpdb;
|
||||
$table = $wpdb->prefix . 'wp_debug_logs';
|
||||
$date = date('Y-m-d H:i:s', strtotime("-{$days} days"));
|
||||
|
||||
$wpdb->query($wpdb->prepare("DELETE FROM {$table} WHERE timestamp < %s", $date));
|
||||
|
||||
// Clear file logs
|
||||
$logs_dir = WP_CONTENT_DIR . '/logs/wp-debug';
|
||||
if (file_exists($logs_dir)) {
|
||||
$files = glob($logs_dir . '/debug-*.log');
|
||||
$cutoff = strtotime("-{$days} days");
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (filemtime($file) < $cutoff) {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close file handle on shutdown
|
||||
*/
|
||||
public static function close_file_handle() {
|
||||
if (self::$file_handle) {
|
||||
fclose(self::$file_handle);
|
||||
self::$file_handle = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience methods for different log levels
|
||||
*/
|
||||
public static function info($message, $context = array()) {
|
||||
return self::log($message, self::LEVEL_INFO, $context);
|
||||
}
|
||||
|
||||
public static function warning($message, $context = array()) {
|
||||
return self::log($message, self::LEVEL_WARNING, $context);
|
||||
}
|
||||
|
||||
public static function error($message, $context = array()) {
|
||||
return self::log($message, self::LEVEL_ERROR, $context);
|
||||
}
|
||||
|
||||
public static function debug($message, $context = array()) {
|
||||
return self::log($message, self::LEVEL_DEBUG, $context);
|
||||
}
|
||||
}
|
||||
266
wp-content/plugins/wp-debug/inc/profiler.php
Normal file
266
wp-content/plugins/wp-debug/inc/profiler.php
Normal file
@@ -0,0 +1,266 @@
|
||||
<?php
|
||||
/**
|
||||
* Performance Profiler Module
|
||||
*
|
||||
* Mide tiempos de ejecución, memoria y detecta bottlenecks
|
||||
*
|
||||
* @package WP_Debug
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WP_Debug_Profiler class
|
||||
*/
|
||||
class WP_Debug_Profiler {
|
||||
|
||||
/**
|
||||
* Active timers
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $timers = array();
|
||||
|
||||
/**
|
||||
* Completed timers
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $completed_timers = array();
|
||||
|
||||
/**
|
||||
* Memory snapshots
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $memory_snapshots = array();
|
||||
|
||||
/**
|
||||
* Initialize profiler
|
||||
*/
|
||||
public static function init() {
|
||||
// Start tracking on init
|
||||
add_action('init', array(__CLASS__, 'start_tracking'), 1);
|
||||
|
||||
// Record metrics on shutdown
|
||||
add_action('shutdown', array(__CLASS__, 'record_metrics'), 999);
|
||||
|
||||
// Track query execution
|
||||
add_filter('query', array(__CLASS__, 'track_query'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Start tracking
|
||||
*/
|
||||
public static function start_tracking() {
|
||||
self::$memory_snapshots['init'] = memory_get_usage();
|
||||
|
||||
// Log page load start
|
||||
if (class_exists('WP_Debug_Logger')) {
|
||||
WP_Debug_Logger::debug('Page load started', array(
|
||||
'url' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '',
|
||||
'memory' => self::format_bytes(memory_get_usage()),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a timer
|
||||
*
|
||||
* @param string $name Timer name
|
||||
* @return bool True on success
|
||||
*/
|
||||
public static function start_timer($name) {
|
||||
self::$timers[$name] = array(
|
||||
'start' => microtime(true),
|
||||
'memory_start' => memory_get_usage(),
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* End a timer
|
||||
*
|
||||
* @param string $name Timer name
|
||||
* @return float|bool Elapsed time in seconds, or false
|
||||
*/
|
||||
public static function end_timer($name) {
|
||||
if (!isset(self::$timers[$name])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$end_time = microtime(true);
|
||||
$memory_end = memory_get_usage();
|
||||
|
||||
$elapsed = $end_time - self::$timers[$name]['start'];
|
||||
$memory_used = $memory_end - self::$timers[$name]['memory_start'];
|
||||
|
||||
self::$completed_timers[$name] = array(
|
||||
'elapsed' => $elapsed,
|
||||
'memory_used' => $memory_used,
|
||||
'memory_peak' => memory_get_peak_usage(),
|
||||
);
|
||||
|
||||
// Log if exceeds threshold
|
||||
$threshold = get_option('wp_debug_profiler_threshold', 1000); // milliseconds
|
||||
if ($elapsed * 1000 > $threshold) {
|
||||
if (class_exists('WP_Debug_Logger')) {
|
||||
WP_Debug_Logger::warning("Slow execution: {$name}", array(
|
||||
'elapsed_ms' => round($elapsed * 1000, 2),
|
||||
'memory' => self::format_bytes($memory_used),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
unset(self::$timers[$name]);
|
||||
|
||||
return $elapsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get completed timers
|
||||
*
|
||||
* @return array Timers
|
||||
*/
|
||||
public static function get_timers() {
|
||||
return self::$completed_timers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Track query execution
|
||||
*
|
||||
* @param string $query SQL query
|
||||
* @return string Query
|
||||
*/
|
||||
public static function track_query($query) {
|
||||
// This will be used by query analyzer
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record metrics on shutdown
|
||||
*/
|
||||
public static function record_metrics() {
|
||||
// Calculate total execution time
|
||||
$total_time = timer_stop(0, 3);
|
||||
|
||||
// Get memory stats
|
||||
$memory_current = memory_get_usage();
|
||||
$memory_peak = memory_get_peak_usage();
|
||||
|
||||
// Get server load if available
|
||||
$server_load = self::get_server_load();
|
||||
|
||||
// Prepare metrics
|
||||
$metrics = array(
|
||||
'execution_time' => $total_time,
|
||||
'memory_current' => $memory_current,
|
||||
'memory_peak' => $memory_peak,
|
||||
'server_load' => $server_load,
|
||||
'timers' => self::$completed_timers,
|
||||
'url' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '',
|
||||
'timestamp' => current_time('mysql'),
|
||||
);
|
||||
|
||||
// Log metrics
|
||||
if (class_exists('WP_Debug_Logger')) {
|
||||
WP_Debug_Logger::info('Page load completed', array(
|
||||
'time' => round($total_time, 3) . 's',
|
||||
'memory_peak' => self::format_bytes($memory_peak),
|
||||
'timers_count' => count(self::$completed_timers),
|
||||
));
|
||||
}
|
||||
|
||||
// Store metrics in transient for frontend panel
|
||||
set_transient('wp_debug_last_metrics', $metrics, 3600);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get server load
|
||||
*
|
||||
* @return string|null Server load
|
||||
*/
|
||||
private static function get_server_load() {
|
||||
if (function_exists('sys_getloadavg')) {
|
||||
$load = sys_getloadavg();
|
||||
return implode(', ', array_map(function($val) {
|
||||
return round($val, 2);
|
||||
}, $load));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format bytes to human readable
|
||||
*
|
||||
* @param int $bytes Bytes
|
||||
* @return string Formatted string
|
||||
*/
|
||||
public static function format_bytes($bytes) {
|
||||
$units = array('B', 'KB', 'MB', 'GB');
|
||||
$bytes = max($bytes, 0);
|
||||
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||
$pow = min($pow, count($units) - 1);
|
||||
|
||||
$bytes /= (1 << (10 * $pow));
|
||||
|
||||
return round($bytes, 2) . ' ' . $units[$pow];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get performance report
|
||||
*
|
||||
* @return array Performance report
|
||||
*/
|
||||
public static function get_performance_report() {
|
||||
$metrics = get_transient('wp_debug_last_metrics');
|
||||
|
||||
if (!$metrics) {
|
||||
return array(
|
||||
'error' => 'No metrics available',
|
||||
);
|
||||
}
|
||||
|
||||
// Analyze and provide suggestions
|
||||
$suggestions = array();
|
||||
|
||||
// Check execution time
|
||||
if ($metrics['execution_time'] > 3) {
|
||||
$suggestions[] = 'Page load time exceeds 3 seconds. Consider caching or optimizing queries.';
|
||||
}
|
||||
|
||||
// Check memory
|
||||
if ($metrics['memory_peak'] > 128 * 1024 * 1024) { // 128MB
|
||||
$suggestions[] = 'Memory usage is high (' . self::format_bytes($metrics['memory_peak']) . '). Check for memory leaks.';
|
||||
}
|
||||
|
||||
// Check slow timers
|
||||
$slow_timers = array();
|
||||
foreach ($metrics['timers'] as $name => $timer) {
|
||||
if ($timer['elapsed'] > 1) {
|
||||
$slow_timers[$name] = round($timer['elapsed'], 3) . 's';
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($slow_timers)) {
|
||||
$suggestions[] = 'Slow functions detected: ' . implode(', ', array_keys($slow_timers));
|
||||
}
|
||||
|
||||
return array(
|
||||
'metrics' => $metrics,
|
||||
'suggestions' => $suggestions,
|
||||
'formatted' => array(
|
||||
'execution_time' => round($metrics['execution_time'], 3) . 's',
|
||||
'memory_current' => self::format_bytes($metrics['memory_current']),
|
||||
'memory_peak' => self::format_bytes($metrics['memory_peak']),
|
||||
'server_load' => $metrics['server_load'],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
266
wp-content/plugins/wp-debug/inc/query-analyzer.php
Normal file
266
wp-content/plugins/wp-debug/inc/query-analyzer.php
Normal file
@@ -0,0 +1,266 @@
|
||||
<?php
|
||||
/**
|
||||
* Query Analyzer Module
|
||||
*
|
||||
* Analiza queries SQL, detecta N+1 queries y queries lentas
|
||||
*
|
||||
* @package WP_Debug
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WP_Debug_Query_Analyzer class
|
||||
*/
|
||||
class WP_Debug_Query_Analyzer {
|
||||
|
||||
/**
|
||||
* Queries executed
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $queries_executed = array();
|
||||
|
||||
/**
|
||||
* Query patterns
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $query_patterns = array();
|
||||
|
||||
/**
|
||||
* Initialize query analyzer
|
||||
*/
|
||||
public static function init() {
|
||||
// Enable query saving
|
||||
if (!defined('SAVEQUERIES')) {
|
||||
define('SAVEQUERIES', true);
|
||||
}
|
||||
|
||||
// Track queries on shutdown
|
||||
add_action('shutdown', array(__CLASS__, 'analyze_queries'), 999);
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze queries
|
||||
*/
|
||||
public static function analyze_queries() {
|
||||
global $wpdb;
|
||||
|
||||
if (empty($wpdb->queries)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Process each query
|
||||
foreach ($wpdb->queries as $query) {
|
||||
list($sql, $elapsed, $caller) = $query;
|
||||
|
||||
$query_info = array(
|
||||
'sql' => $sql,
|
||||
'elapsed' => $elapsed,
|
||||
'caller' => $caller,
|
||||
'timestamp' => microtime(true),
|
||||
);
|
||||
|
||||
self::$queries_executed[] = $query_info;
|
||||
|
||||
// Group by pattern
|
||||
$normalized = self::normalize_query($sql);
|
||||
if (!isset(self::$query_patterns[$normalized])) {
|
||||
self::$query_patterns[$normalized] = array(
|
||||
'count' => 0,
|
||||
'total_time' => 0,
|
||||
'queries' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
self::$query_patterns[$normalized]['count']++;
|
||||
self::$query_patterns[$normalized]['total_time'] += floatval($elapsed);
|
||||
self::$query_patterns[$normalized]['queries'][] = $query_info;
|
||||
}
|
||||
|
||||
// Log statistics
|
||||
$total_queries = count($wpdb->queries);
|
||||
$total_time = array_sum(array_column($wpdb->queries, 1));
|
||||
$slow_queries = self::get_slow_queries();
|
||||
$n_plus_one = self::detect_n_plus_one();
|
||||
|
||||
if (class_exists('WP_Debug_Logger')) {
|
||||
WP_Debug_Logger::info('Query Analysis Complete', array(
|
||||
'total_queries' => $total_queries,
|
||||
'total_time' => round($total_time, 4) . 's',
|
||||
'slow_queries' => count($slow_queries),
|
||||
'n_plus_one_patterns' => count($n_plus_one),
|
||||
));
|
||||
|
||||
// Log slow queries
|
||||
foreach ($slow_queries as $query) {
|
||||
WP_Debug_Logger::warning('Slow Query Detected', array(
|
||||
'elapsed' => round($query['elapsed'], 4) . 's',
|
||||
'sql' => substr($query['sql'], 0, 200),
|
||||
'caller' => $query['caller'],
|
||||
));
|
||||
}
|
||||
|
||||
// Log N+1 patterns
|
||||
foreach ($n_plus_one as $pattern) {
|
||||
WP_Debug_Logger::warning('N+1 Query Pattern Detected', array(
|
||||
'count' => $pattern['count'],
|
||||
'total_time' => round($pattern['total_time'], 4) . 's',
|
||||
'example' => substr($pattern['example'], 0, 200),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Store for frontend panel
|
||||
set_transient('wp_debug_queries_data', array(
|
||||
'total_queries' => $total_queries,
|
||||
'total_time' => $total_time,
|
||||
'queries' => self::$queries_executed,
|
||||
'slow_queries' => $slow_queries,
|
||||
'n_plus_one' => $n_plus_one,
|
||||
'patterns' => self::$query_patterns,
|
||||
), 3600);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize query for pattern detection
|
||||
*
|
||||
* @param string $query SQL query
|
||||
* @return string Normalized query
|
||||
*/
|
||||
private static function normalize_query($query) {
|
||||
// Remove numeric literals
|
||||
$query = preg_replace('/\b\d+\b/', '?', $query);
|
||||
|
||||
// Remove string literals
|
||||
$query = preg_replace("/'[^']*'/", '?', $query);
|
||||
|
||||
// Remove extra whitespace
|
||||
$query = preg_replace('/\s+/', ' ', $query);
|
||||
|
||||
return trim($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect N+1 query patterns
|
||||
*
|
||||
* @return array N+1 patterns
|
||||
*/
|
||||
public static function detect_n_plus_one() {
|
||||
$patterns = array();
|
||||
|
||||
// Find patterns executed 5+ times
|
||||
foreach (self::$query_patterns as $normalized => $pattern) {
|
||||
if ($pattern['count'] >= 5) {
|
||||
$patterns[] = array(
|
||||
'count' => $pattern['count'],
|
||||
'total_time' => $pattern['total_time'],
|
||||
'avg_time' => $pattern['total_time'] / $pattern['count'],
|
||||
'example' => $pattern['queries'][0]['sql'],
|
||||
'normalized' => $normalized,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by count descending
|
||||
usort($patterns, function($a, $b) {
|
||||
return $b['count'] - $a['count'];
|
||||
});
|
||||
|
||||
return $patterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get slow queries
|
||||
*
|
||||
* @param float $threshold Threshold in seconds
|
||||
* @return array Slow queries
|
||||
*/
|
||||
public static function get_slow_queries($threshold = 0.05) {
|
||||
return array_filter(self::$queries_executed, function($query) use ($threshold) {
|
||||
return floatval($query['elapsed']) > $threshold;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get queries by caller
|
||||
*
|
||||
* @param string $caller Caller filter
|
||||
* @return array Queries
|
||||
*/
|
||||
public static function get_queries_by_caller($caller) {
|
||||
return array_filter(self::$queries_executed, function($query) use ($caller) {
|
||||
return strpos($query['caller'], $caller) !== false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query statistics
|
||||
*
|
||||
* @return array Statistics
|
||||
*/
|
||||
public static function get_statistics() {
|
||||
global $wpdb;
|
||||
|
||||
if (empty($wpdb->queries)) {
|
||||
return array(
|
||||
'error' => 'SAVEQUERIES not enabled or no queries executed',
|
||||
);
|
||||
}
|
||||
|
||||
$total_queries = count($wpdb->queries);
|
||||
$total_time = array_sum(array_column($wpdb->queries, 1));
|
||||
$avg_time = $total_time / $total_queries;
|
||||
|
||||
return array(
|
||||
'total_queries' => $total_queries,
|
||||
'total_time' => $total_time,
|
||||
'avg_time' => $avg_time,
|
||||
'slow_queries' => count(self::get_slow_queries()),
|
||||
'n_plus_one_patterns' => count(self::detect_n_plus_one()),
|
||||
'unique_patterns' => count(self::$query_patterns),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query report
|
||||
*
|
||||
* @return array Query report
|
||||
*/
|
||||
public static function get_query_report() {
|
||||
$stats = self::get_statistics();
|
||||
$slow_queries = self::get_slow_queries();
|
||||
$n_plus_one = self::detect_n_plus_one();
|
||||
|
||||
$suggestions = array();
|
||||
|
||||
// Suggestions based on analysis
|
||||
if (isset($stats['total_queries']) && $stats['total_queries'] > 100) {
|
||||
$suggestions[] = 'High number of queries detected (' . $stats['total_queries'] . '). Consider using object caching.';
|
||||
}
|
||||
|
||||
if (isset($stats['total_time']) && $stats['total_time'] > 1) {
|
||||
$suggestions[] = 'Total query time exceeds 1 second. Optimize slow queries.';
|
||||
}
|
||||
|
||||
if (!empty($n_plus_one)) {
|
||||
$suggestions[] = 'N+1 query patterns detected. Consider using WP_Query with proper parameters or caching.';
|
||||
}
|
||||
|
||||
if (!empty($slow_queries)) {
|
||||
$suggestions[] = count($slow_queries) . ' slow queries detected. Add indexes or optimize WHERE clauses.';
|
||||
}
|
||||
|
||||
return array(
|
||||
'statistics' => $stats,
|
||||
'slow_queries' => array_slice($slow_queries, 0, 10),
|
||||
'n_plus_one_patterns' => array_slice($n_plus_one, 0, 5),
|
||||
'suggestions' => $suggestions,
|
||||
);
|
||||
}
|
||||
}
|
||||
253
wp-content/plugins/wp-debug/inc/template-tracer.php
Normal file
253
wp-content/plugins/wp-debug/inc/template-tracer.php
Normal file
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
/**
|
||||
* Template Tracer Module
|
||||
*
|
||||
* Rastrea qué templates se cargan y en qué orden
|
||||
*
|
||||
* @package WP_Debug
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WP_Debug_Template_Tracer class
|
||||
*/
|
||||
class WP_Debug_Template_Tracer {
|
||||
|
||||
/**
|
||||
* Templates loaded
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $templates_loaded = array();
|
||||
|
||||
/**
|
||||
* Template parts loaded
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $template_parts = array();
|
||||
|
||||
/**
|
||||
* Initialize template tracer
|
||||
*/
|
||||
public static function init() {
|
||||
// Track template loading
|
||||
add_filter('template_include', array(__CLASS__, 'track_template'), 999);
|
||||
|
||||
// Track template parts
|
||||
add_filter('get_template_part', array(__CLASS__, 'track_template_part'), 10, 3);
|
||||
|
||||
// Track template hierarchy
|
||||
add_filter('template_hierarchy', array(__CLASS__, 'track_template_hierarchy'));
|
||||
|
||||
// Record templates on shutdown
|
||||
add_action('shutdown', array(__CLASS__, 'record_templates'), 999);
|
||||
}
|
||||
|
||||
/**
|
||||
* Track main template
|
||||
*
|
||||
* @param string $template Template path
|
||||
* @return string Template path
|
||||
*/
|
||||
public static function track_template($template) {
|
||||
$template_info = array(
|
||||
'path' => $template,
|
||||
'name' => basename($template),
|
||||
'type' => self::get_template_type(),
|
||||
'timestamp' => microtime(true),
|
||||
'backtrace' => self::get_caller(),
|
||||
);
|
||||
|
||||
self::$templates_loaded[] = $template_info;
|
||||
|
||||
// Log template load
|
||||
if (class_exists('WP_Debug_Logger')) {
|
||||
WP_Debug_Logger::debug("Template loaded: " . basename($template), array(
|
||||
'type' => $template_info['type'],
|
||||
'path' => $template,
|
||||
));
|
||||
}
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Track template part
|
||||
*
|
||||
* @param string $slug Template slug
|
||||
* @param string $name Template name
|
||||
* @param array $args Template args
|
||||
*/
|
||||
public static function track_template_part($slug, $name = null, $args = array()) {
|
||||
$template_file = $slug;
|
||||
if ($name) {
|
||||
$template_file .= '-' . $name;
|
||||
}
|
||||
$template_file .= '.php';
|
||||
|
||||
$template_path = locate_template($template_file);
|
||||
|
||||
$part_info = array(
|
||||
'slug' => $slug,
|
||||
'name' => $name,
|
||||
'file' => $template_file,
|
||||
'path' => $template_path,
|
||||
'found' => !empty($template_path),
|
||||
'args' => $args,
|
||||
'timestamp' => microtime(true),
|
||||
);
|
||||
|
||||
self::$template_parts[] = $part_info;
|
||||
|
||||
// Log if not found
|
||||
if (!$part_info['found']) {
|
||||
if (class_exists('WP_Debug_Logger')) {
|
||||
WP_Debug_Logger::warning("Template part not found: {$template_file}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Track template hierarchy
|
||||
*
|
||||
* @param array $templates Template hierarchy
|
||||
* @return array Template hierarchy
|
||||
*/
|
||||
public static function track_template_hierarchy($templates) {
|
||||
if (class_exists('WP_Debug_Logger')) {
|
||||
WP_Debug_Logger::debug("Template hierarchy", array(
|
||||
'templates' => $templates,
|
||||
));
|
||||
}
|
||||
|
||||
return $templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template type
|
||||
*
|
||||
* @return string Template type
|
||||
*/
|
||||
private static function get_template_type() {
|
||||
if (is_404()) return '404';
|
||||
if (is_search()) return 'search';
|
||||
if (is_front_page()) return 'front-page';
|
||||
if (is_home()) return 'home';
|
||||
if (is_post_type_archive()) return 'archive-' . get_post_type();
|
||||
if (is_tax()) return 'taxonomy';
|
||||
if (is_attachment()) return 'attachment';
|
||||
if (is_single()) return 'single-' . get_post_type();
|
||||
if (is_page()) return 'page';
|
||||
if (is_category()) return 'category';
|
||||
if (is_tag()) return 'tag';
|
||||
if (is_author()) return 'author';
|
||||
if (is_date()) return 'date';
|
||||
if (is_archive()) return 'archive';
|
||||
|
||||
return 'index';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get caller information
|
||||
*
|
||||
* @return string Caller info
|
||||
*/
|
||||
private static function get_caller() {
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5);
|
||||
$callers = array();
|
||||
|
||||
foreach ($backtrace as $trace) {
|
||||
if (isset($trace['file']) && isset($trace['line'])) {
|
||||
$callers[] = basename($trace['file']) . ':' . $trace['line'];
|
||||
}
|
||||
}
|
||||
|
||||
return implode(' -> ', $callers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get loaded templates
|
||||
*
|
||||
* @return array Templates
|
||||
*/
|
||||
public static function get_templates() {
|
||||
return self::$templates_loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template parts
|
||||
*
|
||||
* @return array Template parts
|
||||
*/
|
||||
public static function get_template_parts() {
|
||||
return self::$template_parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get missing template parts
|
||||
*
|
||||
* @return array Missing template parts
|
||||
*/
|
||||
public static function get_missing_parts() {
|
||||
return array_filter(self::$template_parts, function($part) {
|
||||
return !$part['found'];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Record templates on shutdown
|
||||
*/
|
||||
public static function record_templates() {
|
||||
$templates_count = count(self::$templates_loaded);
|
||||
$parts_count = count(self::$template_parts);
|
||||
$missing_count = count(self::get_missing_parts());
|
||||
|
||||
// Log summary
|
||||
if (class_exists('WP_Debug_Logger')) {
|
||||
WP_Debug_Logger::info("Templates loaded", array(
|
||||
'templates' => $templates_count,
|
||||
'parts' => $parts_count,
|
||||
'missing' => $missing_count,
|
||||
));
|
||||
}
|
||||
|
||||
// Store for frontend panel
|
||||
set_transient('wp_debug_templates_data', array(
|
||||
'templates' => self::$templates_loaded,
|
||||
'parts' => self::$template_parts,
|
||||
'missing' => self::get_missing_parts(),
|
||||
), 3600);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template load order
|
||||
*
|
||||
* @return array Template load order
|
||||
*/
|
||||
public static function get_load_order() {
|
||||
$order = array();
|
||||
|
||||
// Combine templates and parts
|
||||
$all = array_merge(
|
||||
array_map(function($t) {
|
||||
return array('type' => 'template', 'data' => $t);
|
||||
}, self::$templates_loaded),
|
||||
array_map(function($p) {
|
||||
return array('type' => 'part', 'data' => $p);
|
||||
}, self::$template_parts)
|
||||
);
|
||||
|
||||
// Sort by timestamp
|
||||
usort($all, function($a, $b) {
|
||||
return $a['data']['timestamp'] - $b['data']['timestamp'];
|
||||
});
|
||||
|
||||
return $all;
|
||||
}
|
||||
}
|
||||
106
wp-content/plugins/wp-debug/readme.txt
Normal file
106
wp-content/plugins/wp-debug/readme.txt
Normal file
@@ -0,0 +1,106 @@
|
||||
=== WP Debug ===
|
||||
Contributors: Developer Team
|
||||
Tags: debug, diagnostics, performance, developer tools, wp-cli
|
||||
Requires at least: 6.0
|
||||
Tested up to: 6.8
|
||||
Requires PHP: 7.4
|
||||
Stable tag: 1.0.0
|
||||
License: GPLv2 or later
|
||||
License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||||
|
||||
Sistema avanzado de debug y diagnóstico para WordPress. Monitorea hooks, templates, assets, queries y performance en tiempo real.
|
||||
|
||||
== Description ==
|
||||
|
||||
WP Debug es un sistema de diagnóstico avanzado diseñado para desarrolladores de WordPress que necesitan visibilidad completa del ciclo de rendering, hooks, queries y performance.
|
||||
|
||||
= Características =
|
||||
|
||||
* **Hook Monitor:** Rastrea todos los hooks de WordPress ejecutados con timestamps
|
||||
* **Template Tracer:** Identifica qué templates se cargan y en qué orden
|
||||
* **Asset Tracker:** Monitorea CSS/JS encolados con dependencias y duplicados
|
||||
* **Performance Profiler:** Mide tiempos de ejecución y detecta bottlenecks
|
||||
* **Query Analyzer:** Analiza queries SQL y detecta problemas N+1
|
||||
* **Frontend Panel:** Panel visual flotante en frontend (solo admins)
|
||||
* **WP-CLI Commands:** Comandos desde terminal para diagnóstico avanzado
|
||||
|
||||
= Casos de Uso =
|
||||
|
||||
* Diagnosticar por qué un sidebar no es sticky
|
||||
* Identificar hooks que no se ejecutan
|
||||
* Detectar CSS/JS que se cargan en orden incorrecto
|
||||
* Encontrar queries lentas o duplicadas
|
||||
* Medir performance de funciones críticas
|
||||
* Debugging de templates y template-parts
|
||||
|
||||
= Comandos WP-CLI =
|
||||
|
||||
* `wp debug status` - Ver estado del sistema
|
||||
* `wp debug hooks` - Listar hooks ejecutados
|
||||
* `wp debug templates` - Listar templates cargados
|
||||
* `wp debug export` - Exportar reporte completo
|
||||
* `wp debug clear` - Limpiar logs
|
||||
|
||||
= API Programática =
|
||||
|
||||
```php
|
||||
// Logging
|
||||
wp_debug_log('Mensaje de debug', 'INFO');
|
||||
|
||||
// Performance profiling
|
||||
wp_debug_start_timer('mi_funcion');
|
||||
// ... tu código ...
|
||||
wp_debug_end_timer('mi_funcion');
|
||||
|
||||
// Obtener datos
|
||||
$hooks = wp_debug_get_hooks();
|
||||
$templates = wp_debug_get_templates();
|
||||
```
|
||||
|
||||
== Installation ==
|
||||
|
||||
1. Sube la carpeta `wp-debug` a `/wp-content/plugins/`
|
||||
2. Activa el plugin desde el menú 'Plugins' de WordPress
|
||||
3. Ve a Tools → WP Debug para ver el dashboard
|
||||
4. (Opcional) Usa comandos WP-CLI para diagnóstico avanzado
|
||||
|
||||
== Frequently Asked Questions ==
|
||||
|
||||
= ¿Afecta el performance del sitio? =
|
||||
|
||||
El plugin está optimizado para mínimo overhead (<5%). Se recomienda usar solo en staging/desarrollo.
|
||||
|
||||
= ¿Puedo usarlo en producción? =
|
||||
|
||||
Sí, pero se recomienda desactivar módulos innecesarios y usar solo en modo debug.
|
||||
|
||||
= ¿Dónde se guardan los logs? =
|
||||
|
||||
En `/wp-content/logs/wp-debug/` y en tablas de base de datos.
|
||||
|
||||
= ¿Cómo lo uso en mi plugin? =
|
||||
|
||||
Simplemente llama a las funciones API: `wp_debug_log()`, `wp_debug_start_timer()`, etc.
|
||||
|
||||
== Screenshots ==
|
||||
|
||||
1. Dashboard principal en Tools → WP Debug
|
||||
2. Frontend panel flotante (solo admins)
|
||||
3. Hook monitor en tiempo real
|
||||
4. Template tracer con backtraces
|
||||
5. Performance profiler con métricas
|
||||
|
||||
== Changelog ==
|
||||
|
||||
= 1.0.0 =
|
||||
* Release inicial
|
||||
* 8 módulos de diagnóstico
|
||||
* WP-CLI commands
|
||||
* API programática
|
||||
* Frontend panel
|
||||
* Admin dashboard
|
||||
|
||||
== Upgrade Notice ==
|
||||
|
||||
= 1.0.0 =
|
||||
Primera versión del plugin.
|
||||
145
wp-content/plugins/wp-debug/wp-debug.php
Normal file
145
wp-content/plugins/wp-debug/wp-debug.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: WP Debug
|
||||
* Plugin URI: https://github.com/prime-leads-app/wp-debug
|
||||
* Description: Sistema avanzado de debug y diagnóstico para WordPress. Monitorea hooks, templates, assets, queries y performance en tiempo real.
|
||||
* Version: 1.0.0
|
||||
* Author: Developer Team
|
||||
* Author URI: https://github.com/prime-leads-app
|
||||
* License: GPL v2 or later
|
||||
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||||
* Text Domain: wp-debug
|
||||
* Domain Path: /languages
|
||||
* Requires at least: 6.0
|
||||
* Requires PHP: 7.4
|
||||
*
|
||||
* @package WP_Debug
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin Constants
|
||||
*/
|
||||
define('WP_DEBUG_VERSION', '1.0.0');
|
||||
define('WP_DEBUG_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
||||
define('WP_DEBUG_PLUGIN_URL', plugin_dir_url(__FILE__));
|
||||
define('WP_DEBUG_PLUGIN_BASENAME', plugin_basename(__FILE__));
|
||||
|
||||
/**
|
||||
* Activation Hook
|
||||
*/
|
||||
function wp_debug_activate() {
|
||||
require_once WP_DEBUG_PLUGIN_DIR . 'inc/activation.php';
|
||||
WP_Debug_Activation::activate();
|
||||
}
|
||||
register_activation_hook(__FILE__, 'wp_debug_activate');
|
||||
|
||||
/**
|
||||
* Deactivation Hook
|
||||
*/
|
||||
function wp_debug_deactivate() {
|
||||
require_once WP_DEBUG_PLUGIN_DIR . 'inc/deactivation.php';
|
||||
WP_Debug_Deactivation::deactivate();
|
||||
}
|
||||
register_deactivation_hook(__FILE__, 'wp_debug_deactivate');
|
||||
|
||||
/**
|
||||
* Autoloader - Load all modules
|
||||
*/
|
||||
require_once WP_DEBUG_PLUGIN_DIR . 'inc/autoloader.php';
|
||||
|
||||
/**
|
||||
* Initialize the plugin
|
||||
*/
|
||||
function wp_debug_init() {
|
||||
// Load text domain for translations
|
||||
load_plugin_textdomain('wp-debug', false, dirname(WP_DEBUG_PLUGIN_BASENAME) . '/languages');
|
||||
|
||||
// Initialize autoloader
|
||||
WP_Debug_Autoloader::init();
|
||||
}
|
||||
add_action('plugins_loaded', 'wp_debug_init');
|
||||
|
||||
/**
|
||||
* Admin Menu
|
||||
*/
|
||||
function wp_debug_admin_menu() {
|
||||
require_once WP_DEBUG_PLUGIN_DIR . 'inc/admin-menu.php';
|
||||
WP_Debug_Admin_Menu::init();
|
||||
}
|
||||
add_action('admin_menu', 'wp_debug_admin_menu');
|
||||
|
||||
/**
|
||||
* Public API Functions
|
||||
*
|
||||
* These functions can be used by themes and other plugins
|
||||
*/
|
||||
|
||||
/**
|
||||
* Log a debug message
|
||||
*
|
||||
* @param string $message Message to log
|
||||
* @param string $level Log level: INFO, WARNING, ERROR, DEBUG
|
||||
* @return bool True on success, false on failure
|
||||
*/
|
||||
function wp_debug_log($message, $level = 'INFO') {
|
||||
if (class_exists('WP_Debug_Logger')) {
|
||||
return WP_Debug_Logger::log($message, $level);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a performance timer
|
||||
*
|
||||
* @param string $name Timer name
|
||||
* @return bool True on success
|
||||
*/
|
||||
function wp_debug_start_timer($name) {
|
||||
if (class_exists('WP_Debug_Profiler')) {
|
||||
return WP_Debug_Profiler::start_timer($name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* End a performance timer
|
||||
*
|
||||
* @param string $name Timer name
|
||||
* @return float|bool Elapsed time in seconds, or false on failure
|
||||
*/
|
||||
function wp_debug_end_timer($name) {
|
||||
if (class_exists('WP_Debug_Profiler')) {
|
||||
return WP_Debug_Profiler::end_timer($name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of executed hooks
|
||||
*
|
||||
* @return array List of hooks
|
||||
*/
|
||||
function wp_debug_get_hooks() {
|
||||
if (class_exists('WP_Debug_Hook_Monitor')) {
|
||||
return WP_Debug_Hook_Monitor::get_hooks();
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of loaded templates
|
||||
*
|
||||
* @return array List of templates
|
||||
*/
|
||||
function wp_debug_get_templates() {
|
||||
if (class_exists('WP_Debug_Template_Tracer')) {
|
||||
return WP_Debug_Template_Tracer::get_templates();
|
||||
}
|
||||
return array();
|
||||
}
|
||||
Reference in New Issue
Block a user