wpdb = $wpdb; $this->tableName = $wpdb->prefix . 'roi_theme_deprecation_log'; $uploadDir = wp_upload_dir(); $logDir = $uploadDir['basedir'] . '/roi-theme-logs'; if (!file_exists($logDir)) { wp_mkdir_p($logDir); } $this->logFile = $logDir . '/deprecation-' . date('Y-m-d') . '.log'; // Asegurar que la tabla existe $this->ensureTableExists(); } /** * Get singleton instance */ public static function getInstance(): self { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Log deprecation use * * @param string $class Class name * @param string $method Method name * @param array $args Arguments passed * @param string $replacement Suggested replacement * @param string $version Version deprecated in * @return void */ public function log( string $class, string $method, array $args = [], string $replacement = '', string $version = '2.0.0' ): void { if (!$this->enabled) { return; } $logEntry = [ 'timestamp' => current_time('mysql'), 'class' => $class, 'method' => $method, 'args' => json_encode($args), 'replacement' => $replacement, 'version' => $version, 'backtrace' => $this->getSimplifiedBacktrace(), 'url' => $this->getCurrentUrl(), 'user_id' => get_current_user_id() ]; // Log to file $this->logToFile($logEntry); // Log to database (with deduplication) $this->logToDatabase($logEntry); // Trigger action for external monitoring do_action('roi_theme_deprecation_logged', $logEntry); } /** * Get deprecation statistics * * @param int $days Number of days to analyze * @return array Statistics */ public function getStatistics(int $days = 7): array { $since = date('Y-m-d H:i:s', strtotime("-{$days} days")); $sql = $this->wpdb->prepare( "SELECT class, method, COUNT(*) as usage_count, MAX(timestamp) as last_used, replacement FROM {$this->tableName} WHERE timestamp >= %s GROUP BY class, method, replacement ORDER BY usage_count DESC", $since ); return $this->wpdb->get_results($sql, ARRAY_A) ?: []; } /** * Get most used deprecated methods * * @param int $limit Number of results * @return array Top deprecated methods */ public function getTopDeprecated(int $limit = 10): array { $sql = $this->wpdb->prepare( "SELECT class, method, COUNT(*) as usage_count, replacement FROM {$this->tableName} GROUP BY class, method, replacement ORDER BY usage_count DESC LIMIT %d", $limit ); return $this->wpdb->get_results($sql, ARRAY_A) ?: []; } /** * Clear old logs * * @param int $days Keep logs newer than this many days * @return int Number of deleted entries */ public function clearOldLogs(int $days = 30): int { $since = date('Y-m-d H:i:s', strtotime("-{$days} days")); $deleted = $this->wpdb->query( $this->wpdb->prepare( "DELETE FROM {$this->tableName} WHERE timestamp < %s", $since ) ); return (int) $deleted; } /** * Enable/disable logging * * @param bool $enabled */ public function setEnabled(bool $enabled): void { $this->enabled = $enabled; } /** * Log to file * * @param array $entry Log entry */ private function logToFile(array $entry): void { $message = sprintf( "[%s] %s::%s() deprecated in v%s - Use %s instead\n", $entry['timestamp'], $entry['class'], $entry['method'], $entry['version'], $entry['replacement'] ?: 'see documentation' ); if (!empty($entry['backtrace'])) { $message .= " Called from: {$entry['backtrace']}\n"; } if (!empty($entry['url'])) { $message .= " URL: {$entry['url']}\n"; } $message .= " ---\n"; error_log($message, 3, $this->logFile); } /** * Log to database with deduplication * * @param array $entry Log entry */ private function logToDatabase(array $entry): void { // Check if same call was logged in last hour (deduplication) $hash = md5($entry['class'] . $entry['method'] . $entry['url']); $oneHourAgo = date('Y-m-d H:i:s', strtotime('-1 hour')); $exists = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT id FROM {$this->tableName} WHERE call_hash = %s AND timestamp > %s LIMIT 1", $hash, $oneHourAgo ) ); if ($exists) { // Update count instead of creating new entry $this->wpdb->query( $this->wpdb->prepare( "UPDATE {$this->tableName} SET usage_count = usage_count + 1, timestamp = %s WHERE id = %d", $entry['timestamp'], $exists ) ); return; } // Insert new entry $this->wpdb->insert( $this->tableName, [ 'timestamp' => $entry['timestamp'], 'class' => $entry['class'], 'method' => $entry['method'], 'args' => $entry['args'], 'replacement' => $entry['replacement'], 'version' => $entry['version'], 'backtrace' => $entry['backtrace'], 'url' => $entry['url'], 'user_id' => $entry['user_id'], 'call_hash' => $hash, 'usage_count' => 1 ], ['%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%d'] ); } /** * Get simplified backtrace * * @return string Simplified backtrace */ private function getSimplifiedBacktrace(): string { $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5); // Skip first 3 frames (this method, log method, deprecated function) $relevant = array_slice($trace, 3, 2); $parts = []; foreach ($relevant as $frame) { if (isset($frame['file']) && isset($frame['line'])) { $file = basename($frame['file']); $parts[] = "{$file}:{$frame['line']}"; } } return implode(' -> ', $parts); } /** * Get current URL * * @return string Current URL */ private function getCurrentUrl(): string { if (!isset($_SERVER['REQUEST_URI'])) { return ''; } $scheme = is_ssl() ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST'] ?? 'localhost'; $uri = $_SERVER['REQUEST_URI'] ?? ''; return $scheme . '://' . $host . $uri; } /** * Ensure database table exists */ private function ensureTableExists(): void { $charset_collate = $this->wpdb->get_charset_collate(); $sql = "CREATE TABLE IF NOT EXISTS {$this->tableName} ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, timestamp datetime NOT NULL, class varchar(255) NOT NULL, method varchar(255) NOT NULL, args text, replacement varchar(255) DEFAULT '', version varchar(20) DEFAULT '2.0.0', backtrace text, url varchar(500) DEFAULT '', user_id bigint(20) unsigned DEFAULT 0, call_hash varchar(32) NOT NULL, usage_count int(11) DEFAULT 1, PRIMARY KEY (id), KEY class_method (class, method), KEY timestamp (timestamp), KEY call_hash (call_hash) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); } /** * Prevent cloning */ private function __clone() {} /** * Prevent unserialization */ public function __wakeup() { throw new \Exception('Cannot unserialize singleton'); } }