wpdb = $wpdb; $this->connectionConfig = [ 'charset' => $this->wpdb->charset, 'collate' => $this->wpdb->collate, 'host' => DB_HOST, 'database' => DB_NAME, 'username' => DB_USER, 'password' => DB_PASSWORD ]; $this->initializePerformanceMetrics(); $this->setupQueryLogging(); } /** * Get singleton instance * * @return self * @since 1.0.0 */ public static function getInstance(): self { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Initialize performance metrics tracking * * @return void * @since 1.0.0 */ private function initializePerformanceMetrics(): void { $this->performanceMetrics = [ 'total_queries' => 0, 'slow_queries' => 0, 'failed_queries' => 0, 'total_execution_time' => 0.0, 'connection_count' => 0, 'active_connections' => 0, 'connection_errors' => 0, 'query_cache_hits' => 0, 'query_cache_misses' => 0, 'last_reset' => time() ]; } /** * Setup query logging if enabled * * @return void * @since 1.0.0 */ private function setupQueryLogging(): void { if ($this->enableQueryLogging) { add_filter('query', [$this, 'logQuery'], 10, 1); } } /** * Get primary database connection * * @return \wpdb * @since 1.0.0 */ public function getConnection(): \wpdb { return $this->wpdb; } /** * Get or create read-only connection for read replicas * * @return \wpdb * @since 1.0.0 */ public function getReadConnection(): \wpdb { // In a real environment, this would connect to read replicas // For now, return the main connection return $this->getConnection(); } /** * Get or create write connection for master database * * @return \wpdb * @since 1.0.0 */ public function getWriteConnection(): \wpdb { return $this->getConnection(); } /** * Execute query with connection management * * @param string $sql * @param array $parameters * @param string $connectionType * @return mixed * @since 1.0.0 */ public function query(string $sql, array $parameters = [], string $connectionType = 'read') { $startTime = microtime(true); $connection = $connectionType === 'write' ? $this->getWriteConnection() : $this->getReadConnection(); try { // Prepare query if parameters provided if (!empty($parameters)) { $sql = $connection->prepare($sql, ...$parameters); } // Execute query $result = $connection->get_results($sql, ARRAY_A); // Track performance metrics $executionTime = (microtime(true) - $startTime) * 1000; // Convert to milliseconds $this->trackQueryExecution($sql, $executionTime, true); return $result; } catch (\Exception $e) { $executionTime = (microtime(true) - $startTime) * 1000; $this->trackQueryExecution($sql, $executionTime, false, $e->getMessage()); throw $e; } } /** * Execute single row query * * @param string $sql * @param array $parameters * @param string $connectionType * @return mixed * @since 1.0.0 */ public function queryRow(string $sql, array $parameters = [], string $connectionType = 'read') { $startTime = microtime(true); $connection = $connectionType === 'write' ? $this->getWriteConnection() : $this->getReadConnection(); try { if (!empty($parameters)) { $sql = $connection->prepare($sql, ...$parameters); } $result = $connection->get_row($sql, ARRAY_A); $executionTime = (microtime(true) - $startTime) * 1000; $this->trackQueryExecution($sql, $executionTime, true); return $result; } catch (\Exception $e) { $executionTime = (microtime(true) - $startTime) * 1000; $this->trackQueryExecution($sql, $executionTime, false, $e->getMessage()); throw $e; } } /** * Execute single value query * * @param string $sql * @param array $parameters * @param string $connectionType * @return mixed * @since 1.0.0 */ public function queryValue(string $sql, array $parameters = [], string $connectionType = 'read') { $startTime = microtime(true); $connection = $connectionType === 'write' ? $this->getWriteConnection() : $this->getReadConnection(); try { if (!empty($parameters)) { $sql = $connection->prepare($sql, ...$parameters); } $result = $connection->get_var($sql); $executionTime = (microtime(true) - $startTime) * 1000; $this->trackQueryExecution($sql, $executionTime, true); return $result; } catch (\Exception $e) { $executionTime = (microtime(true) - $startTime) * 1000; $this->trackQueryExecution($sql, $executionTime, false, $e->getMessage()); throw $e; } } /** * Execute insert query * * @param string $table * @param array $data * @param array $format * @return int * @since 1.0.0 */ public function insert(string $table, array $data, array $format = []): int { $startTime = microtime(true); $connection = $this->getWriteConnection(); try { $result = $connection->insert($table, $data, $format); if ($result === false) { throw new \RuntimeException('Insert failed: ' . $connection->last_error); } $executionTime = (microtime(true) - $startTime) * 1000; $this->trackQueryExecution("INSERT INTO {$table}", $executionTime, true); return $connection->insert_id; } catch (\Exception $e) { $executionTime = (microtime(true) - $startTime) * 1000; $this->trackQueryExecution("INSERT INTO {$table}", $executionTime, false, $e->getMessage()); throw $e; } } /** * Execute update query * * @param string $table * @param array $data * @param array $where * @param array $format * @param array $whereFormat * @return int * @since 1.0.0 */ public function update(string $table, array $data, array $where, array $format = [], array $whereFormat = []): int { $startTime = microtime(true); $connection = $this->getWriteConnection(); try { $result = $connection->update($table, $data, $where, $format, $whereFormat); if ($result === false) { throw new \RuntimeException('Update failed: ' . $connection->last_error); } $executionTime = (microtime(true) - $startTime) * 1000; $this->trackQueryExecution("UPDATE {$table}", $executionTime, true); return $result; } catch (\Exception $e) { $executionTime = (microtime(true) - $startTime) * 1000; $this->trackQueryExecution("UPDATE {$table}", $executionTime, false, $e->getMessage()); throw $e; } } /** * Execute delete query * * @param string $table * @param array $where * @param array $whereFormat * @return int * @since 1.0.0 */ public function delete(string $table, array $where, array $whereFormat = []): int { $startTime = microtime(true); $connection = $this->getWriteConnection(); try { $result = $connection->delete($table, $where, $whereFormat); if ($result === false) { throw new \RuntimeException('Delete failed: ' . $connection->last_error); } $executionTime = (microtime(true) - $startTime) * 1000; $this->trackQueryExecution("DELETE FROM {$table}", $executionTime, true); return $result; } catch (\Exception $e) { $executionTime = (microtime(true) - $startTime) * 1000; $this->trackQueryExecution("DELETE FROM {$table}", $executionTime, false, $e->getMessage()); throw $e; } } /** * Start database transaction * * @return bool * @since 1.0.0 */ public function beginTransaction(): bool { $connection = $this->getWriteConnection(); return $connection->query('START TRANSACTION') !== false; } /** * Commit database transaction * * @return bool * @since 1.0.0 */ public function commit(): bool { $connection = $this->getWriteConnection(); return $connection->query('COMMIT') !== false; } /** * Rollback database transaction * * @return bool * @since 1.0.0 */ public function rollback(): bool { $connection = $this->getWriteConnection(); return $connection->query('ROLLBACK') !== false; } /** * Execute query within transaction * * @param callable $callback * @return mixed * @throws \Exception * @since 1.0.0 */ public function transaction(callable $callback) { $this->beginTransaction(); try { $result = $callback($this); $this->commit(); return $result; } catch (\Exception $e) { $this->rollback(); throw $e; } } /** * Test database connection * * @return array * @since 1.0.0 */ public function testConnection(): array { $startTime = microtime(true); try { $result = $this->queryValue('SELECT 1'); $responseTime = (microtime(true) - $startTime) * 1000; return [ 'status' => 'success', 'connected' => $result === '1', 'response_time_ms' => round($responseTime, 2), 'server_version' => $this->getServerVersion(), 'connection_id' => $this->getConnectionId() ]; } catch (\Exception $e) { $responseTime = (microtime(true) - $startTime) * 1000; return [ 'status' => 'error', 'connected' => false, 'response_time_ms' => round($responseTime, 2), 'error' => $e->getMessage() ]; } } /** * Get MySQL server version * * @return string * @since 1.0.0 */ public function getServerVersion(): string { try { return $this->queryValue('SELECT VERSION()') ?: 'Unknown'; } catch (\Exception $e) { return 'Unknown'; } } /** * Get current connection ID * * @return int * @since 1.0.0 */ public function getConnectionId(): int { try { return (int) $this->queryValue('SELECT CONNECTION_ID()'); } catch (\Exception $e) { return 0; } } /** * Get database statistics * * @return array * @since 1.0.0 */ public function getDatabaseStats(): array { try { $stats = [ 'server_version' => $this->getServerVersion(), 'connection_id' => $this->getConnectionId(), 'uptime' => $this->queryValue("SHOW STATUS LIKE 'Uptime'") ?: 0, 'queries' => $this->queryValue("SHOW STATUS LIKE 'Queries'") ?: 0, 'slow_queries' => $this->queryValue("SHOW STATUS LIKE 'Slow_queries'") ?: 0, 'connections' => $this->queryValue("SHOW STATUS LIKE 'Connections'") ?: 0, 'aborted_connects' => $this->queryValue("SHOW STATUS LIKE 'Aborted_connects'") ?: 0, 'max_connections' => $this->queryValue("SHOW VARIABLES LIKE 'max_connections'") ?: 0, 'thread_cache_size' => $this->queryValue("SHOW VARIABLES LIKE 'thread_cache_size'") ?: 0 ]; return $stats; } catch (\Exception $e) { return [ 'error' => $e->getMessage(), 'server_version' => $this->getServerVersion(), 'connection_id' => $this->getConnectionId() ]; } } /** * Get table information and statistics * * @param string $tableName * @return array * @since 1.0.0 */ public function getTableStats(string $tableName): array { try { $tableStatus = $this->queryRow("SHOW TABLE STATUS LIKE '{$tableName}'"); if (!$tableStatus) { return ['error' => 'Table not found']; } return [ 'name' => $tableStatus['Name'], 'engine' => $tableStatus['Engine'], 'rows' => (int) $tableStatus['Rows'], 'data_length' => (int) $tableStatus['Data_length'], 'index_length' => (int) $tableStatus['Index_length'], 'data_free' => (int) $tableStatus['Data_free'], 'auto_increment' => (int) $tableStatus['Auto_increment'], 'create_time' => $tableStatus['Create_time'], 'update_time' => $tableStatus['Update_time'], 'collation' => $tableStatus['Collation'], 'comment' => $tableStatus['Comment'] ]; } catch (\Exception $e) { return ['error' => $e->getMessage()]; } } /** * Optimize table * * @param string $tableName * @return array * @since 1.0.0 */ public function optimizeTable(string $tableName): array { try { $result = $this->query("OPTIMIZE TABLE {$tableName}"); return [ 'success' => true, 'result' => $result, 'message' => 'Table optimized successfully' ]; } catch (\Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Analyze table * * @param string $tableName * @return array * @since 1.0.0 */ public function analyzeTable(string $tableName): array { try { $result = $this->query("ANALYZE TABLE {$tableName}"); return [ 'success' => true, 'result' => $result, 'message' => 'Table analyzed successfully' ]; } catch (\Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Track query execution metrics * * @param string $sql * @param float $executionTime * @param bool $success * @param string|null $error * @return void * @since 1.0.0 */ private function trackQueryExecution(string $sql, float $executionTime, bool $success, ?string $error = null): void { $this->performanceMetrics['total_queries']++; $this->performanceMetrics['total_execution_time'] += $executionTime; if (!$success) { $this->performanceMetrics['failed_queries']++; } if ($executionTime > $this->slowQueryThreshold) { $this->performanceMetrics['slow_queries']++; } // Log query if enabled if ($this->enableQueryLogging) { $this->queryLog[] = [ 'sql' => substr($sql, 0, 200) . (strlen($sql) > 200 ? '...' : ''), 'execution_time' => $executionTime, 'success' => $success, 'error' => $error, 'timestamp' => microtime(true) ]; // Keep only last 1000 queries if (count($this->queryLog) > 1000) { $this->queryLog = array_slice($this->queryLog, -1000); } } // Trigger action for monitoring do_action('care_book_query_executed', [ 'sql' => $sql, 'execution_time' => $executionTime, 'success' => $success, 'error' => $error ]); } /** * Log query for WordPress query logging * * @param string $query * @return string * @since 1.0.0 */ public function logQuery(string $query): string { // This is called by WordPress query filter // You can add custom logging logic here return $query; } /** * Get performance metrics * * @return array * @since 1.0.0 */ public function getPerformanceMetrics(): array { $metrics = $this->performanceMetrics; // Calculate derived metrics $metrics['average_execution_time'] = $metrics['total_queries'] > 0 ? round($metrics['total_execution_time'] / $metrics['total_queries'], 2) : 0; $metrics['success_rate'] = $metrics['total_queries'] > 0 ? round((($metrics['total_queries'] - $metrics['failed_queries']) / $metrics['total_queries']) * 100, 2) : 100; $metrics['slow_query_percentage'] = $metrics['total_queries'] > 0 ? round(($metrics['slow_queries'] / $metrics['total_queries']) * 100, 2) : 0; return $metrics; } /** * Reset performance metrics * * @return void * @since 1.0.0 */ public function resetPerformanceMetrics(): void { $this->initializePerformanceMetrics(); } /** * Get recent query log * * @param int $limit * @return array> * @since 1.0.0 */ public function getQueryLog(int $limit = 100): array { if (!$this->enableQueryLogging) { return []; } return array_slice($this->queryLog, -$limit); } /** * Get slow queries from log * * @param int $limit * @return array> * @since 1.0.0 */ public function getSlowQueries(int $limit = 50): array { if (!$this->enableQueryLogging) { return []; } $slowQueries = array_filter($this->queryLog, function($query) { return $query['execution_time'] > $this->slowQueryThreshold; }); // Sort by execution time descending usort($slowQueries, function($a, $b) { return $b['execution_time'] <=> $a['execution_time']; }); return array_slice($slowQueries, 0, $limit); } /** * Enable or disable query logging * * @param bool $enabled * @return void * @since 1.0.0 */ public function setQueryLogging(bool $enabled): void { $this->enableQueryLogging = $enabled; if ($enabled) { $this->setupQueryLogging(); } } /** * Set slow query threshold * * @param int $milliseconds * @return void * @since 1.0.0 */ public function setSlowQueryThreshold(int $milliseconds): void { $this->slowQueryThreshold = $milliseconds; } /** * Get connection configuration * * @return array * @since 1.0.0 */ public function getConnectionConfig(): array { // Return safe config without password return [ 'charset' => $this->connectionConfig['charset'], 'collate' => $this->connectionConfig['collate'], 'host' => $this->connectionConfig['host'], 'database' => $this->connectionConfig['database'], 'username' => $this->connectionConfig['username'] ]; } /** * Prevent cloning * * @return void * @since 1.0.0 */ private function __clone() {} /** * Prevent unserialization * * @return void * @since 1.0.0 */ public function __wakeup() { throw new \Exception("Cannot unserialize singleton"); } }