- Added GitHub spec-kit for development workflow - Standardized file signatures to Descomplicar® format - Updated development configuration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
537 lines
18 KiB
PHP
537 lines
18 KiB
PHP
/**
|
|
* Descomplicar® Crescimento Digital
|
|
* https://descomplicar.pt
|
|
*/
|
|
|
|
<?php
|
|
/**
|
|
* Performance Monitor for Care Booking Block plugin
|
|
* Tracks and analyzes performance metrics to ensure <2% overhead target
|
|
*
|
|
* @package CareBookingBlock
|
|
*/
|
|
|
|
// Prevent direct access
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Performance Monitor class for enterprise-grade optimization
|
|
*/
|
|
class Care_Booking_Performance_Monitor
|
|
{
|
|
/**
|
|
* Performance metrics cache key
|
|
*/
|
|
const METRICS_CACHE_KEY = 'care_booking_performance_metrics';
|
|
|
|
/**
|
|
* Performance target: <2% overhead
|
|
*/
|
|
const TARGET_OVERHEAD_PERCENT = 2.0;
|
|
|
|
/**
|
|
* Performance target: <100ms AJAX response
|
|
*/
|
|
const TARGET_AJAX_RESPONSE_MS = 100;
|
|
|
|
/**
|
|
* Performance target: >95% cache hit rate
|
|
*/
|
|
const TARGET_CACHE_HIT_RATE = 95.0;
|
|
|
|
/**
|
|
* Initialize performance monitoring
|
|
*/
|
|
public static function init()
|
|
{
|
|
// Hook into WordPress performance points
|
|
add_action('init', [__CLASS__, 'start_performance_tracking'], 1);
|
|
add_action('wp_footer', [__CLASS__, 'end_performance_tracking'], 999);
|
|
|
|
// AJAX performance tracking
|
|
add_action('wp_ajax_care_booking_get_entities', [__CLASS__, 'track_ajax_start'], 1);
|
|
add_action('wp_ajax_nopriv_care_booking_get_entities', [__CLASS__, 'track_ajax_start'], 1);
|
|
|
|
// Database query performance
|
|
add_filter('query', [__CLASS__, 'track_database_queries'], 10, 1);
|
|
|
|
// Cache performance tracking
|
|
add_action('care_booking_cache_hit', [__CLASS__, 'track_cache_hit']);
|
|
add_action('care_booking_cache_miss', [__CLASS__, 'track_cache_miss']);
|
|
|
|
// Memory usage tracking
|
|
add_action('shutdown', [__CLASS__, 'track_memory_usage'], 1);
|
|
}
|
|
|
|
/**
|
|
* Start performance tracking for page loads
|
|
*/
|
|
public static function start_performance_tracking()
|
|
{
|
|
if (!self::should_track_performance()) {
|
|
return;
|
|
}
|
|
|
|
// Store start time and memory
|
|
if (!defined('CARE_BOOKING_START_TIME')) {
|
|
define('CARE_BOOKING_START_TIME', microtime(true));
|
|
define('CARE_BOOKING_START_MEMORY', memory_get_usage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* End performance tracking and calculate metrics
|
|
*/
|
|
public static function end_performance_tracking()
|
|
{
|
|
if (!defined('CARE_BOOKING_START_TIME')) {
|
|
return;
|
|
}
|
|
|
|
$end_time = microtime(true);
|
|
$end_memory = memory_get_usage();
|
|
|
|
$execution_time = ($end_time - CARE_BOOKING_START_TIME) * 1000; // Convert to ms
|
|
$memory_usage = $end_memory - CARE_BOOKING_START_MEMORY;
|
|
|
|
// Calculate overhead percentage (plugin time vs total page time)
|
|
$total_page_time = (microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000;
|
|
$overhead_percent = ($execution_time / $total_page_time) * 100;
|
|
|
|
$metrics = [
|
|
'execution_time_ms' => round($execution_time, 2),
|
|
'memory_usage_bytes' => $memory_usage,
|
|
'overhead_percent' => round($overhead_percent, 2),
|
|
'timestamp' => time(),
|
|
'url' => $_SERVER['REQUEST_URI'] ?? '',
|
|
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? ''
|
|
];
|
|
|
|
self::store_performance_metrics($metrics);
|
|
self::check_performance_targets($metrics);
|
|
|
|
// Output debug info if enabled
|
|
if (defined('WP_DEBUG') && WP_DEBUG && current_user_can('manage_options')) {
|
|
self::output_debug_info($metrics);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Track AJAX request start time
|
|
*/
|
|
public static function track_ajax_start()
|
|
{
|
|
if (!defined('CARE_BOOKING_AJAX_START')) {
|
|
define('CARE_BOOKING_AJAX_START', microtime(true));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Track AJAX response completion
|
|
*
|
|
* @param mixed $response AJAX response data
|
|
* @return mixed Original response
|
|
*/
|
|
public static function track_ajax_complete($response)
|
|
{
|
|
if (!defined('CARE_BOOKING_AJAX_START')) {
|
|
return $response;
|
|
}
|
|
|
|
$response_time = (microtime(true) - CARE_BOOKING_AJAX_START) * 1000;
|
|
|
|
$metrics = [
|
|
'ajax_response_time_ms' => round($response_time, 2),
|
|
'ajax_action' => $_POST['action'] ?? '',
|
|
'timestamp' => time()
|
|
];
|
|
|
|
self::store_ajax_metrics($metrics);
|
|
|
|
// Check if we're meeting AJAX performance targets
|
|
if ($response_time > self::TARGET_AJAX_RESPONSE_MS) {
|
|
self::log_performance_warning("AJAX response exceeded target: {$response_time}ms > " . self::TARGET_AJAX_RESPONSE_MS . "ms");
|
|
}
|
|
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Track database queries performance
|
|
*
|
|
* @param string $query SQL query
|
|
* @return string Original query
|
|
*/
|
|
public static function track_database_queries($query)
|
|
{
|
|
// Only track Care Booking related queries
|
|
if (strpos($query, 'care_booking_restrictions') === false) {
|
|
return $query;
|
|
}
|
|
|
|
$start_time = microtime(true);
|
|
|
|
// Use a filter to track completion
|
|
add_filter('query_result', function($result) use ($start_time, $query) {
|
|
$execution_time = (microtime(true) - $start_time) * 1000;
|
|
|
|
if ($execution_time > 50) { // Log slow queries > 50ms
|
|
self::log_performance_warning("Slow query detected: {$execution_time}ms - " . substr($query, 0, 100));
|
|
}
|
|
|
|
return $result;
|
|
}, 10, 1);
|
|
|
|
return $query;
|
|
}
|
|
|
|
/**
|
|
* Track cache hit
|
|
*
|
|
* @param string $cache_key Cache key that was hit
|
|
*/
|
|
public static function track_cache_hit($cache_key = '')
|
|
{
|
|
$stats = get_transient('care_booking_cache_stats') ?: ['hits' => 0, 'misses' => 0];
|
|
$stats['hits']++;
|
|
$stats['last_hit'] = time();
|
|
|
|
set_transient('care_booking_cache_stats', $stats, HOUR_IN_SECONDS);
|
|
}
|
|
|
|
/**
|
|
* Track cache miss
|
|
*
|
|
* @param string $cache_key Cache key that was missed
|
|
*/
|
|
public static function track_cache_miss($cache_key = '')
|
|
{
|
|
$stats = get_transient('care_booking_cache_stats') ?: ['hits' => 0, 'misses' => 0];
|
|
$stats['misses']++;
|
|
$stats['last_miss'] = time();
|
|
|
|
set_transient('care_booking_cache_stats', $stats, HOUR_IN_SECONDS);
|
|
|
|
// Log excessive cache misses
|
|
$total = $stats['hits'] + $stats['misses'];
|
|
if ($total > 10 && (($stats['hits'] / $total) * 100) < self::TARGET_CACHE_HIT_RATE) {
|
|
self::log_performance_warning("Cache hit rate below target: " . round(($stats['hits'] / $total) * 100, 1) . "%");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Track memory usage
|
|
*/
|
|
public static function track_memory_usage()
|
|
{
|
|
$current_memory = memory_get_usage();
|
|
$peak_memory = memory_get_peak_usage();
|
|
|
|
// Target: <10MB footprint
|
|
$target_memory = 10 * 1024 * 1024; // 10MB in bytes
|
|
|
|
if (defined('CARE_BOOKING_START_MEMORY')) {
|
|
$plugin_memory = $current_memory - CARE_BOOKING_START_MEMORY;
|
|
|
|
if ($plugin_memory > $target_memory) {
|
|
self::log_performance_warning("Memory usage exceeded target: " . size_format($plugin_memory) . " > 10MB");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Store performance metrics
|
|
*
|
|
* @param array $metrics Performance metrics
|
|
*/
|
|
private static function store_performance_metrics($metrics)
|
|
{
|
|
$stored_metrics = get_transient(self::METRICS_CACHE_KEY) ?: [];
|
|
|
|
// Keep only last 100 measurements for performance
|
|
if (count($stored_metrics) >= 100) {
|
|
$stored_metrics = array_slice($stored_metrics, -99);
|
|
}
|
|
|
|
$stored_metrics[] = $metrics;
|
|
set_transient(self::METRICS_CACHE_KEY, $stored_metrics, DAY_IN_SECONDS);
|
|
}
|
|
|
|
/**
|
|
* Store AJAX performance metrics
|
|
*
|
|
* @param array $metrics AJAX metrics
|
|
*/
|
|
private static function store_ajax_metrics($metrics)
|
|
{
|
|
$ajax_metrics = get_transient('care_booking_ajax_metrics') ?: [];
|
|
|
|
if (count($ajax_metrics) >= 50) {
|
|
$ajax_metrics = array_slice($ajax_metrics, -49);
|
|
}
|
|
|
|
$ajax_metrics[] = $metrics;
|
|
set_transient('care_booking_ajax_metrics', $ajax_metrics, DAY_IN_SECONDS);
|
|
}
|
|
|
|
/**
|
|
* Check if performance targets are being met
|
|
*
|
|
* @param array $metrics Current performance metrics
|
|
*/
|
|
private static function check_performance_targets($metrics)
|
|
{
|
|
$warnings = [];
|
|
|
|
// Check overhead target (<2%)
|
|
if ($metrics['overhead_percent'] > self::TARGET_OVERHEAD_PERCENT) {
|
|
$warnings[] = "Page overhead exceeded target: {$metrics['overhead_percent']}% > " . self::TARGET_OVERHEAD_PERCENT . "%";
|
|
}
|
|
|
|
// Check execution time target (<50ms for plugin operations)
|
|
if ($metrics['execution_time_ms'] > 50) {
|
|
$warnings[] = "Plugin execution time high: {$metrics['execution_time_ms']}ms";
|
|
}
|
|
|
|
// Check memory usage target (<10MB)
|
|
$memory_mb = $metrics['memory_usage_bytes'] / (1024 * 1024);
|
|
if ($memory_mb > 10) {
|
|
$warnings[] = "Memory usage exceeded target: " . round($memory_mb, 2) . "MB > 10MB";
|
|
}
|
|
|
|
foreach ($warnings as $warning) {
|
|
self::log_performance_warning($warning);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log performance warning
|
|
*
|
|
* @param string $message Warning message
|
|
*/
|
|
private static function log_performance_warning($message)
|
|
{
|
|
if (defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) {
|
|
error_log("Care Booking Performance Warning: " . $message);
|
|
}
|
|
|
|
// Store in admin notices if user is admin
|
|
if (current_user_can('manage_options')) {
|
|
$notices = get_transient('care_booking_performance_notices') ?: [];
|
|
$notices[] = [
|
|
'message' => $message,
|
|
'timestamp' => time(),
|
|
'severity' => 'warning'
|
|
];
|
|
|
|
// Keep only last 10 notices
|
|
if (count($notices) > 10) {
|
|
$notices = array_slice($notices, -10);
|
|
}
|
|
|
|
set_transient('care_booking_performance_notices', $notices, HOUR_IN_SECONDS);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get comprehensive performance report
|
|
*
|
|
* @return array Performance report
|
|
*/
|
|
public static function get_performance_report()
|
|
{
|
|
$metrics = get_transient(self::METRICS_CACHE_KEY) ?: [];
|
|
$ajax_metrics = get_transient('care_booking_ajax_metrics') ?: [];
|
|
$cache_stats = get_transient('care_booking_cache_stats') ?: ['hits' => 0, 'misses' => 0];
|
|
|
|
if (empty($metrics)) {
|
|
return ['status' => 'no_data'];
|
|
}
|
|
|
|
// Calculate averages
|
|
$avg_overhead = array_sum(array_column($metrics, 'overhead_percent')) / count($metrics);
|
|
$avg_execution = array_sum(array_column($metrics, 'execution_time_ms')) / count($metrics);
|
|
$avg_memory = array_sum(array_column($metrics, 'memory_usage_bytes')) / count($metrics);
|
|
|
|
// Calculate cache hit rate
|
|
$total_cache_requests = $cache_stats['hits'] + $cache_stats['misses'];
|
|
$cache_hit_rate = $total_cache_requests > 0 ? ($cache_stats['hits'] / $total_cache_requests) * 100 : 0;
|
|
|
|
// Calculate AJAX averages
|
|
$avg_ajax_response = !empty($ajax_metrics)
|
|
? array_sum(array_column($ajax_metrics, 'ajax_response_time_ms')) / count($ajax_metrics)
|
|
: 0;
|
|
|
|
return [
|
|
'status' => 'active',
|
|
'targets' => [
|
|
'overhead_percent' => self::TARGET_OVERHEAD_PERCENT,
|
|
'ajax_response_ms' => self::TARGET_AJAX_RESPONSE_MS,
|
|
'cache_hit_rate' => self::TARGET_CACHE_HIT_RATE
|
|
],
|
|
'current' => [
|
|
'avg_overhead_percent' => round($avg_overhead, 2),
|
|
'avg_execution_time_ms' => round($avg_execution, 2),
|
|
'avg_memory_usage_mb' => round($avg_memory / (1024 * 1024), 2),
|
|
'cache_hit_rate_percent' => round($cache_hit_rate, 2),
|
|
'avg_ajax_response_ms' => round($avg_ajax_response, 2)
|
|
],
|
|
'performance_score' => self::calculate_performance_score($avg_overhead, $avg_ajax_response, $cache_hit_rate),
|
|
'measurements_count' => count($metrics),
|
|
'last_measurement' => max(array_column($metrics, 'timestamp'))
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Calculate overall performance score (0-100)
|
|
*
|
|
* @param float $overhead_percent Current overhead percentage
|
|
* @param float $ajax_response_ms Current AJAX response time
|
|
* @param float $cache_hit_rate Current cache hit rate
|
|
* @return int Performance score
|
|
*/
|
|
private static function calculate_performance_score($overhead_percent, $ajax_response_ms, $cache_hit_rate)
|
|
{
|
|
$score = 100;
|
|
|
|
// Deduct points for overhead (target <2%)
|
|
if ($overhead_percent > self::TARGET_OVERHEAD_PERCENT) {
|
|
$score -= min(30, ($overhead_percent - self::TARGET_OVERHEAD_PERCENT) * 10);
|
|
}
|
|
|
|
// Deduct points for AJAX response time (target <100ms)
|
|
if ($ajax_response_ms > self::TARGET_AJAX_RESPONSE_MS) {
|
|
$score -= min(30, ($ajax_response_ms - self::TARGET_AJAX_RESPONSE_MS) / 10);
|
|
}
|
|
|
|
// Deduct points for cache hit rate (target >95%)
|
|
if ($cache_hit_rate < self::TARGET_CACHE_HIT_RATE) {
|
|
$score -= min(25, (self::TARGET_CACHE_HIT_RATE - $cache_hit_rate));
|
|
}
|
|
|
|
return max(0, (int) $score);
|
|
}
|
|
|
|
/**
|
|
* Should track performance based on current context
|
|
*
|
|
* @return bool True if should track
|
|
*/
|
|
private static function should_track_performance()
|
|
{
|
|
// Don't track in admin area unless specifically enabled
|
|
if (is_admin() && !defined('CARE_BOOKING_TRACK_ADMIN_PERFORMANCE')) {
|
|
return false;
|
|
}
|
|
|
|
// Don't track for bots and crawlers
|
|
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
|
if (preg_match('/bot|crawler|spider|robot/i', $user_agent)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Output debug information
|
|
*
|
|
* @param array $metrics Performance metrics
|
|
*/
|
|
private static function output_debug_info($metrics)
|
|
{
|
|
echo "\n<!-- Care Booking Performance Debug -->\n";
|
|
echo "<!-- Execution Time: {$metrics['execution_time_ms']}ms -->\n";
|
|
echo "<!-- Memory Usage: " . size_format($metrics['memory_usage_bytes']) . " -->\n";
|
|
echo "<!-- Page Overhead: {$metrics['overhead_percent']}% -->\n";
|
|
echo "<!-- Target Overhead: " . self::TARGET_OVERHEAD_PERCENT . "% -->\n";
|
|
|
|
$status = $metrics['overhead_percent'] <= self::TARGET_OVERHEAD_PERCENT ? 'MEETING TARGET' : 'EXCEEDING TARGET';
|
|
echo "<!-- Performance Status: {$status} -->\n";
|
|
echo "<!-- End Care Booking Performance Debug -->\n";
|
|
}
|
|
|
|
/**
|
|
* Get performance notices for admin display
|
|
*
|
|
* @return array Performance notices
|
|
*/
|
|
public static function get_performance_notices()
|
|
{
|
|
return get_transient('care_booking_performance_notices') ?: [];
|
|
}
|
|
|
|
/**
|
|
* Clear performance notices
|
|
*/
|
|
public static function clear_performance_notices()
|
|
{
|
|
delete_transient('care_booking_performance_notices');
|
|
}
|
|
|
|
/**
|
|
* Get asset optimization statistics
|
|
*
|
|
* @return array Asset optimization stats
|
|
*/
|
|
public static function get_asset_stats()
|
|
{
|
|
$asset_files = [
|
|
'admin_css' => [
|
|
'original' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'admin/css/admin-style.css',
|
|
'minified' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'admin/css/admin-style.min.css'
|
|
],
|
|
'admin_js' => [
|
|
'original' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'admin/js/admin-script.js',
|
|
'minified' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'admin/js/admin-script.min.js'
|
|
],
|
|
'frontend_css' => [
|
|
'original' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'public/css/frontend.css',
|
|
'minified' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'public/css/frontend.min.css'
|
|
],
|
|
'frontend_js' => [
|
|
'original' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'public/js/frontend.js',
|
|
'minified' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'public/js/frontend.min.js'
|
|
]
|
|
];
|
|
|
|
$stats = [];
|
|
$total_original = 0;
|
|
$total_minified = 0;
|
|
|
|
foreach ($asset_files as $key => $files) {
|
|
$original_size = file_exists($files['original']) ? filesize($files['original']) : 0;
|
|
$minified_size = file_exists($files['minified']) ? filesize($files['minified']) : 0;
|
|
|
|
$savings_bytes = $original_size - $minified_size;
|
|
$savings_percent = $original_size > 0 ? ($savings_bytes / $original_size) * 100 : 0;
|
|
|
|
$stats[$key] = [
|
|
'original_size' => $original_size,
|
|
'minified_size' => $minified_size,
|
|
'savings_bytes' => $savings_bytes,
|
|
'savings_percent' => round($savings_percent, 1)
|
|
];
|
|
|
|
$total_original += $original_size;
|
|
$total_minified += $minified_size;
|
|
}
|
|
|
|
$total_savings = $total_original - $total_minified;
|
|
$total_savings_percent = $total_original > 0 ? ($total_savings / $total_original) * 100 : 0;
|
|
|
|
$stats['total'] = [
|
|
'original_size' => $total_original,
|
|
'minified_size' => $total_minified,
|
|
'savings_bytes' => $total_savings,
|
|
'savings_percent' => round($total_savings_percent, 1)
|
|
];
|
|
|
|
return $stats;
|
|
}
|
|
}
|
|
|
|
// Initialize performance monitoring
|
|
add_action('plugins_loaded', [Care_Booking_Performance_Monitor::class, 'init'], 5); |