Last active
August 11, 2025 21:15
-
-
Save todditron/080a0a752ee33f4d37ed5dd20b234b05 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Plugin Name: WLM Performance Debug | |
* Description: Debug WishList Member performance issues, especially the "Protect Existing Now" corruption bug | |
* Version: 1.0.4 | |
* | |
* Installation: | |
* 1. Save this file as: /wp-content/plugins/wlm-performance-debug/wlm-performance-debug.php | |
* 2. Activate the plugin from WordPress admin | |
* 3. Save any post to see debug output | |
*/ | |
// Prevent direct access | |
if (!defined('ABSPATH')) { | |
exit; | |
} | |
class WLM_Performance_Debug { | |
private $query_counts = []; | |
private $query_samples = []; | |
private $start_time = 0; | |
private $is_tracking = false; | |
public function __construct() { | |
add_action('init', [$this, 'init']); | |
} | |
public function init() { | |
// Only run for logged-in users with edit capabilities | |
if (!current_user_can('edit_posts')) { | |
return; | |
} | |
add_action('wp_insert_post', [$this, 'start_tracking_save'], 1, 3); | |
add_action('admin_notices', [$this, 'display_performance_results']); | |
add_action('wp_footer', [$this, 'display_performance_results_frontend']); | |
} | |
public function start_tracking_save($post_id, $post, $update) { | |
// Skip autosaves and revisions | |
if (wp_is_post_autosave($post_id) || wp_is_post_revision($post_id)) { | |
return; | |
} | |
$this->reset_tracking(); | |
$this->is_tracking = true; | |
$this->start_time = microtime(true); | |
// Start tracking queries | |
add_filter('query', [$this, 'track_query']); | |
// Schedule results display after save completes | |
add_action('shutdown', function() use ($post_id, $post) { | |
$this->finalize_tracking($post_id, $post); | |
}, 1); | |
} | |
private function reset_tracking() { | |
$this->query_counts = [ | |
'total_queries' => 0, | |
'wlm_contentlevels_deletes' => 0, | |
'wlm_contentsched_deletes' => 0, | |
'wlm_cache_updates' => 0, | |
'wlm_protection_checks' => 0, | |
'wlm_options_reads' => 0, | |
'wlm_api_calls' => 0, | |
'other_wlm_queries' => 0 | |
]; | |
$this->query_samples = [ | |
'wlm_contentlevels_deletes' => [], | |
'wlm_contentsched_deletes' => [], | |
'wlm_cache_updates' => [], | |
'wlm_protection_checks' => [], | |
'wlm_options_reads' => [], | |
'wlm_api_calls' => [], | |
'other_wlm_queries' => [] | |
]; | |
} | |
public function track_query($query) { | |
if (!$this->is_tracking) { | |
return $query; | |
} | |
$time_elapsed = microtime(true) - $this->start_time; | |
// Only count queries after initial processing starts | |
if ($time_elapsed > 0.05) { | |
$this->query_counts['total_queries']++; | |
// Track and sample specific WLM operations | |
if (strpos($query, 'wp_wlm_contentlevels') !== false && strpos($query, 'DELETE') !== false) { | |
$this->query_counts['wlm_contentlevels_deletes']++; | |
$this->add_sample('wlm_contentlevels_deletes', $query); | |
} | |
elseif (strpos($query, 'wp_wlcc_contentsched') !== false && strpos($query, 'DELETE') !== false) { | |
$this->query_counts['wlm_contentsched_deletes']++; | |
$this->add_sample('wlm_contentsched_deletes', $query); | |
} | |
elseif (strpos($query, 'wlm_cache_group_membership_content') !== false) { | |
$this->query_counts['wlm_cache_updates']++; | |
$this->add_sample('wlm_cache_updates', $query); | |
} | |
elseif (strpos($query, 'wp_wlm_contentlevels') !== false || strpos($query, 'wp_wlcc_contentsched') !== false) { | |
$this->query_counts['wlm_protection_checks']++; | |
$this->add_sample('wlm_protection_checks', $query); | |
} | |
elseif (strpos($query, 'wp_wlm_options') !== false) { | |
$this->query_counts['wlm_options_reads']++; | |
$this->add_sample('wlm_options_reads', $query); | |
} | |
elseif (strpos($query, 'wlmapi_get_levels') !== false || (strpos($query, 'wp_wlm') !== false && strpos($query, 'levels') !== false)) { | |
$this->query_counts['wlm_api_calls']++; | |
$this->add_sample('wlm_api_calls', $query); | |
} | |
elseif (strpos($query, 'wlm') !== false || strpos($query, 'WLM') !== false) { | |
$this->query_counts['other_wlm_queries']++; | |
$this->add_sample('other_wlm_queries', $query); | |
} | |
} | |
return $query; | |
} | |
private function add_sample($category, $query) { | |
// Keep max 3 samples per category to avoid overwhelming output | |
if (count($this->query_samples[$category]) < 3) { | |
$this->query_samples[$category][] = $this->sanitize_query_sample($query); | |
} | |
} | |
private function sanitize_query_sample($query) { | |
// Truncate long queries and remove sensitive data | |
$clean_query = preg_replace('/\s+/', ' ', trim($query)); | |
return substr($clean_query, 0, 200) . (strlen($clean_query) > 200 ? '...' : ''); | |
} | |
private function finalize_tracking($post_id, $post) { | |
global $wpdb; | |
if (!$this->is_tracking) { | |
return; | |
} | |
$this->is_tracking = false; | |
$total_time = microtime(true) - $this->start_time; | |
// Store results in transient for display | |
$results = [ | |
'post_id' => $post_id, | |
'post_title' => $post->post_title, | |
'post_type' => $post->post_type, | |
'total_time' => round($total_time, 3), | |
'query_counts' => $this->query_counts, | |
'query_samples' => $this->query_samples, | |
'timestamp' => current_time('mysql') | |
]; | |
// Add performance assessment | |
$total_wlm = array_sum(array_slice($this->query_counts, 1)); // Exclude total_queries | |
$results['performance_status'] = $this->assess_performance($total_wlm); | |
// Add membership level context | |
if (function_exists('wlmapi_get_levels')) { | |
$levels_result = wlmapi_get_levels(); | |
if (isset($levels_result['success']) && $levels_result['success'] == 1) { | |
$results['membership_levels'] = count($levels_result['levels']['level']); | |
} | |
} | |
// Get assigned levels for this specific post | |
$assigned_levels = $wpdb->get_results($wpdb->prepare( | |
"SELECT DISTINCT level_id FROM {$wpdb->prefix}wlm_contentlevels WHERE content_id = %d AND level_id != 'Inherit' AND level_id != 'Protection'", | |
$post_id | |
), ARRAY_A); | |
$results['assigned_levels'] = wp_list_pluck($assigned_levels, 'level_id'); | |
// Add critical debugging info for developers | |
$results['debug_info'] = $this->gather_debug_info($post_id, $post); | |
set_transient('wlm_debug_results_' . get_current_user_id(), $results, 300); // 5 minutes | |
} | |
private function gather_debug_info($post_id, $post) { | |
global $wpdb; | |
$debug_info = []; | |
// WLM uses its own tables, not post meta - skip meta check | |
// Check WLM content levels for this post | |
$content_levels = $wpdb->get_results($wpdb->prepare( | |
"SELECT * FROM {$wpdb->prefix}wlm_contentlevels WHERE content_id = %d", | |
$post_id | |
), ARRAY_A); | |
$debug_info['current_content_levels'] = $content_levels; | |
// Check for "Inherit" levels (these might be the corruption indicator) | |
$inherit_levels = $wpdb->get_results($wpdb->prepare( | |
"SELECT * FROM {$wpdb->prefix}wlm_contentlevels WHERE content_id = %d AND level_id = 'Inherit'", | |
$post_id | |
), ARRAY_A); | |
$debug_info['inherit_levels'] = $inherit_levels; | |
// Check for content scheduling | |
$content_sched = $wpdb->get_results($wpdb->prepare( | |
"SELECT * FROM {$wpdb->prefix}wlcc_contentsched WHERE post_id = %d", | |
$post_id | |
), ARRAY_A); | |
$debug_info['content_scheduling'] = $content_sched; | |
// Check for child posts (inheritance might be causing mass operations) | |
if ($post->post_type === 'course') { | |
$children = get_posts([ | |
'post_type' => 'any', | |
'post_parent' => $post_id, | |
'numberposts' => -1, | |
'post_status' => 'any' | |
]); | |
$debug_info['child_posts_count'] = count($children); | |
if (count($children) > 0) { | |
$debug_info['child_post_ids'] = wp_list_pluck($children, 'ID'); | |
} | |
} | |
// Check WLM options that might indicate bulk operation mode | |
$wlm_options_keys = [ | |
'wlm_bulk_operation_mode', | |
'wlm_protect_existing_mode', | |
'wlm_inheritance_processing', | |
'wlm_batch_processing' | |
]; | |
foreach ($wlm_options_keys as $key) { | |
$value = get_option($key); | |
if ($value !== false) { | |
$debug_info['wlm_options'][$key] = $value; | |
} | |
} | |
// Check for any WLM transients that might indicate ongoing operations | |
$wlm_transients = []; | |
$transient_keys = [ | |
'wlm_bulk_protect', | |
'wlm_protect_existing', | |
'wlm_inheritance_update', | |
'wlm_content_protection_update' | |
]; | |
foreach ($transient_keys as $key) { | |
$value = get_transient($key); | |
if ($value !== false) { | |
$wlm_transients[$key] = $value; | |
} | |
} | |
$debug_info['active_transients'] = $wlm_transients; | |
// Get call stack info if available | |
if (function_exists('debug_backtrace')) { | |
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 15); | |
$wlm_functions = []; | |
foreach ($backtrace as $trace) { | |
if (isset($trace['function']) && | |
(strpos($trace['function'], 'wlm') !== false || | |
strpos($trace['function'], 'WLM') !== false || | |
(isset($trace['file']) && strpos($trace['file'], 'wishlist-member') !== false))) { | |
$wlm_functions[] = [ | |
'function' => $trace['function'] ?? 'unknown', | |
'file' => basename($trace['file'] ?? 'unknown'), | |
'line' => $trace['line'] ?? 'unknown' | |
]; | |
} | |
} | |
$debug_info['wlm_call_stack'] = $wlm_functions; | |
} | |
return $debug_info; | |
} | |
private function assess_performance($total_wlm_queries) { | |
if ($total_wlm_queries > 100) { | |
return [ | |
'level' => 'critical', | |
'message' => 'CRITICAL: Likely affected by "Protect Existing Now" corruption bug' | |
]; | |
} elseif ($total_wlm_queries > 10) { | |
return [ | |
'level' => 'warning', | |
'message' => 'WARNING: Excessive WLM processing detected' | |
]; | |
} else { | |
return [ | |
'level' => 'good', | |
'message' => 'Performance looks normal' | |
]; | |
} | |
} | |
public function display_performance_results() { | |
if (!is_admin()) { | |
return; | |
} | |
$results = get_transient('wlm_debug_results_' . get_current_user_id()); | |
if (!$results) { | |
return; | |
} | |
// Delete the transient so it only shows once | |
delete_transient('wlm_debug_results_' . get_current_user_id()); | |
$this->render_results($results); | |
} | |
public function display_performance_results_frontend() { | |
if (is_admin() || !current_user_can('edit_posts')) { | |
return; | |
} | |
$results = get_transient('wlm_debug_results_' . get_current_user_id()); | |
if (!$results) { | |
return; | |
} | |
// Delete the transient so it only shows once | |
delete_transient('wlm_debug_results_' . get_current_user_id()); | |
echo '<div style="position:fixed;top:32px;right:20px;z-index:99999;max-width:600px;">'; | |
$this->render_results($results); | |
echo '</div>'; | |
} | |
private function render_results($results) { | |
$status_colors = [ | |
'critical' => '#dc3232', | |
'warning' => '#ffb900', | |
'good' => '#00a32a' | |
]; | |
$status_color = $status_colors[$results['performance_status']['level']]; | |
$total_wlm = array_sum(array_slice($results['query_counts'], 1)); | |
?> | |
<div class="notice" style="border-left: 4px solid <?= $status_color ?>; background: white; padding: 15px; margin: 10px 0; box-shadow: 0 2px 5px rgba(0,0,0,0.1);"> | |
<h3 style="margin-top: 0; color: <?= $status_color ?>;"> | |
π WLM Performance Debug Results | |
</h3> | |
<div style="margin-bottom: 15px;"> | |
<strong>Post:</strong> <?= esc_html($results['post_title']) ?> (ID: <?= $results['post_id'] ?>, Type: <?= $results['post_type'] ?>)<br> | |
<strong>Save Time:</strong> <?= $results['total_time'] ?>s<br> | |
<strong>Status:</strong> <span style="color: <?= $status_color ?>; font-weight: bold;"> | |
<?= esc_html($results['performance_status']['message']) ?> | |
</span> | |
</div> | |
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 15px;"> | |
<div> | |
<strong>Query Summary:</strong><br> | |
Total Queries: <?= $results['query_counts']['total_queries'] ?><br> | |
Total WLM Queries: <?= $total_wlm ?><br> | |
<?php if ($results['query_counts']['total_queries'] > 0): ?> | |
WLM Percentage: <?= round(($total_wlm / $results['query_counts']['total_queries']) * 100, 1) ?>% | |
<?php endif; ?> | |
</div> | |
<div> | |
<strong>WLM Breakdown:</strong><br> | |
Content Deletes: <?= $results['query_counts']['wlm_contentlevels_deletes'] ?><br> | |
Schedule Deletes: <?= $results['query_counts']['wlm_contentsched_deletes'] ?><br> | |
Protection Checks: <?= $results['query_counts']['wlm_protection_checks'] ?><br> | |
Options Reads: <?= $results['query_counts']['wlm_options_reads'] ?><br> | |
Cache Updates: <?= $results['query_counts']['wlm_cache_updates'] ?><br> | |
Other WLM: <?= $results['query_counts']['other_wlm_queries'] ?> | |
</div> | |
</div> | |
<?php if (isset($results['assigned_levels']) && isset($results['membership_levels'])): ?> | |
<div style="margin-bottom: 15px;"> | |
<strong>Membership Levels:</strong><br> | |
Assigned to this post: <?= count($results['assigned_levels']) ?> | |
<?php if (count($results['assigned_levels']) > 0): ?> | |
(<?= implode(', ', array_slice($results['assigned_levels'], 0, 3)) ?><?= count($results['assigned_levels']) > 3 ? '...' : '' ?>) | |
<?php endif; ?><br> | |
Total system levels: <?= $results['membership_levels'] ?> | |
(β<?= round($total_wlm / max(1, $results['membership_levels']), 1) ?> queries per system level)<br> | |
<?php if (count($results['assigned_levels']) > 0): ?> | |
<span style="color: #dc3232; font-weight: bold;"> | |
Expected queries: ~<?= count($results['assigned_levels']) * 5 ?>, | |
Actual queries: <?= $total_wlm ?> | |
(<?= round($total_wlm / max(1, count($results['assigned_levels']) * 5), 1) ?>x more than expected) | |
</span> | |
<?php endif; ?> | |
</div> | |
<?php elseif (isset($results['membership_levels'])): ?> | |
<p><strong>Total Membership Levels:</strong> <?= $results['membership_levels'] ?> | |
(β<?= round($total_wlm / max(1, $results['membership_levels']), 1) ?> queries per level)</p> | |
<?php endif; ?> | |
<?php if (isset($results['debug_info'])): ?> | |
<details style="margin-top: 15px;"> | |
<summary style="cursor: pointer; font-weight: bold; color: #dc3232;"> | |
π Developer Debug Info (Click to expand) | |
</summary> | |
<div style="margin-top: 10px; max-height: 400px; overflow-y: auto; background: #f8f8f8; padding: 10px; font-family: monospace; font-size: 11px;"> | |
<?php | |
$debug = $results['debug_info']; | |
if (!empty($debug['current_content_levels'])): ?> | |
<strong>Current Content Levels (<?= count($debug['current_content_levels']) ?>):</strong><br> | |
<?php foreach (array_slice($debug['current_content_levels'], 0, 5) as $level): ?> | |
ID: <?= $level['content_id'] ?>, Level: <?= esc_html($level['level_id']) ?>, Type: <?= esc_html($level['type']) ?><br> | |
<?php endforeach; ?> | |
<?php if (count($debug['current_content_levels']) > 5): ?> | |
... and <?= count($debug['current_content_levels']) - 5 ?> more<br> | |
<?php endif; ?> | |
<br> | |
<?php endif; ?> | |
<?php if (!empty($debug['inherit_levels'])): ?> | |
<strong style="color: #dc3232;">INHERIT LEVELS DETECTED (<?= count($debug['inherit_levels']) ?>):</strong><br> | |
<?php foreach ($debug['inherit_levels'] as $level): ?> | |
ID: <?= $level['content_id'] ?>, Level: <?= esc_html($level['level_id']) ?>, Type: <?= esc_html($level['type']) ?><br> | |
<?php endforeach; ?> | |
<br> | |
<?php endif; ?> | |
<?php if (isset($debug['child_posts_count']) && $debug['child_posts_count'] > 0): ?> | |
<strong>Child Posts: <?= $debug['child_posts_count'] ?></strong><br> | |
Child IDs: <?= implode(', ', array_slice($debug['child_post_ids'], 0, 10)) ?> | |
<?php if (count($debug['child_post_ids']) > 10): ?>... and <?= count($debug['child_post_ids']) - 10 ?> more<?php endif; ?><br><br> | |
<?php endif; ?> | |
<?php if (!empty($debug['content_scheduling'])): ?> | |
<strong>Content Scheduling (<?= count($debug['content_scheduling']) ?>):</strong><br> | |
<?php foreach (array_slice($debug['content_scheduling'], 0, 3) as $sched): ?> | |
Post: <?= $sched['post_id'] ?>, Level: <?= esc_html($sched['mlevel']) ?><br> | |
<?php endforeach; ?> | |
<br> | |
<?php endif; ?> | |
<?php if (!empty($debug['wlm_options'])): ?> | |
<strong style="color: #dc3232;">WLM Options:</strong><br> | |
<?php foreach ($debug['wlm_options'] as $key => $value): ?> | |
<?= esc_html($key) ?>: <?= esc_html(is_array($value) ? json_encode($value) : $value) ?><br> | |
<?php endforeach; ?> | |
<br> | |
<?php endif; ?> | |
<?php if (!empty($debug['active_transients'])): ?> | |
<strong style="color: #dc3232;">Active WLM Transients:</strong><br> | |
<?php foreach ($debug['active_transients'] as $key => $value): ?> | |
<?= esc_html($key) ?>: <?= esc_html(is_array($value) ? json_encode($value) : $value) ?><br> | |
<?php endforeach; ?> | |
<br> | |
<?php endif; ?> | |
<?php if (!empty($debug['wlm_call_stack'])): ?> | |
<strong>WLM Call Stack:</strong><br> | |
<?php foreach ($debug['wlm_call_stack'] as $call): ?> | |
<?= esc_html($call['function']) ?> (<?= esc_html($call['file']) ?>:<?= esc_html($call['line']) ?>)<br> | |
<?php endforeach; ?> | |
<?php endif; ?> | |
</div> | |
</details> | |
<?php endif; ?> | |
<?php | |
// Show query samples if any significant activity | |
$has_samples = false; | |
foreach ($results['query_samples'] as $category => $samples) { | |
if (!empty($samples)) { | |
$has_samples = true; | |
break; | |
} | |
} | |
if ($has_samples): ?> | |
<details style="margin-top: 15px;"> | |
<summary style="cursor: pointer; font-weight: bold; color: <?= $status_color ?>;"> | |
π Query Samples (Click to expand) | |
</summary> | |
<div style="margin-top: 10px; max-height: 300px; overflow-y: auto;"> | |
<?php foreach ($results['query_samples'] as $category => $samples): ?> | |
<?php if (!empty($samples)): ?> | |
<div style="margin-bottom: 10px;"> | |
<strong><?= ucwords(str_replace('_', ' ', $category)) ?>:</strong> | |
<?php foreach ($samples as $sample): ?> | |
<div style="background: #f5f5f5; padding: 5px; margin: 3px 0; font-family: monospace; font-size: 11px; word-break: break-all;"> | |
<?= esc_html($sample) ?> | |
</div> | |
<?php endforeach; ?> | |
</div> | |
<?php endif; ?> | |
<?php endforeach; ?> | |
</div> | |
</details> | |
<?php endif; ?> | |
<p style="margin-bottom: 0; font-size: 12px; color: #666;"> | |
<em>Saved at <?= $results['timestamp'] ?> | Plugin can be deactivated when not needed</em> | |
</p> | |
</div> | |
<?php | |
} | |
} | |
// Initialize the plugin | |
new WLM_Performance_Debug(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment