add(new CacheMiddleware( $cacheStrategy, $cacheConfig['ttl'] ?? 300, $cacheConfig['enabled'] ?? true, $cacheConfig['cacheable_operations'] ?? ['query', 'queryOne', 'queryColumn', 'queryScalar'] )); } if ($retry) { $retryConfig = is_array($retry) ? $retry : []; $pipeline->add(new RetryMiddleware( $retryConfig['max_retries'] ?? 3, $retryConfig['delay_ms'] ?? 100, $retryConfig['retryable_exceptions'] ?? [\PDOException::class, DatabaseException::class] )); } if ($healthCheck) { $healthConfig = is_array($healthCheck) ? $healthCheck : []; $pipeline->add(new HealthCheckMiddleware( $healthConfig['interval'] ?? 30, $healthConfig['enabled'] ?? true )); } // Wenn Pipeline leer ist, gib direkt die Base Connection zurück if (empty($pipeline->getMiddleware())) { return $baseConnection; } return new MiddlewareConnection($baseConnection, $pipeline); } public static function createDirectConnection(array|DriverConfig $config): ConnectionInterface { if (is_array($config)) { $config = DriverConfig::fromArray($config); } $driver = self::createDriver($config); $pdo = self::createPdo($driver); // Create PdoConnection with SqlStateErrorMapper for enhanced error handling return new PdoConnection($pdo, new SqlStateErrorMapper()); } public static function createLazyConnection( array|DriverConfig $config, array $additionalMiddleware = [] ): ConnectionInterface { return self::createConnection($config, array_merge( ['lazy' => true], $additionalMiddleware )); } public static function createRetryableConnection( array|DriverConfig $config, int $maxRetries = 3, int $retryDelayMs = 100 ): ConnectionInterface { return self::createConnection($config, [ 'retry' => [ 'max_retries' => $maxRetries, 'delay_ms' => $retryDelayMs, ], ]); } public static function createRobustConnection( array|DriverConfig $config, array $middlewareConfig = [] ): ConnectionInterface { // Standard "robuste" Konfiguration $defaultConfig = [ 'lazy' => true, 'retry' => [ 'max_retries' => 3, 'delay_ms' => 100, ], 'health_check' => [ 'interval' => 30, 'enabled' => true, ], ]; return self::createConnection($config, array_merge($defaultConfig, $middlewareConfig)); } public static function createPureLazyConnection(array|DriverConfig $config): ConnectionInterface { // Reine LazyGhost-Connection ohne zusätzliche Middleware return LazyConnectionFactory::createLazyGhost($config); } public static function isLazyConnection(ConnectionInterface $connection): bool { if ($connection instanceof MiddlewareConnection) { $baseConnection = $connection->getBaseConnection(); return LazyConnectionFactory::isLazyGhost($baseConnection); } return LazyConnectionFactory::isLazyGhost($connection); } public static function forceLazyInitialization(ConnectionInterface $connection): void { if ($connection instanceof MiddlewareConnection) { $baseConnection = $connection->getBaseConnection(); if (LazyConnectionFactory::isLazyGhost($baseConnection)) { LazyConnectionFactory::initializeLazyGhost($baseConnection); } } elseif (LazyConnectionFactory::isLazyGhost($connection)) { LazyConnectionFactory::initializeLazyGhost($connection); } } private static function createCacheStrategy(array $config): CacheStrategy { // Verwende direktes CacheInterface oder fallback zu ArrayCache $keyPrefix = $config['prefix'] ?? 'db_query:'; $tags = $config['tags'] ?? ['database_query']; // Wenn eine CacheInterface-Instanz direkt übergeben wird if (isset($config['cache_instance']) && $config['cache_instance'] instanceof Cache) { return new CacheAdapterStrategy($config['cache_instance'], $keyPrefix); } // Fallback zu einfacher Array-basierter Implementierung return new SimpleCacheStrategy($keyPrefix); } /** * Erstelle Cache-Strategy mit direkter CacheInterface-Instanz */ public static function createCacheStrategyFromCache( Cache $cache, string $keyPrefix = 'db_query:', array $tags = ['database_query'] ): CacheStrategy { return CacheAdapterStrategy::withTags($cache, $tags, $keyPrefix); } public static function createCachedConnection( array|DriverConfig $config, array $cacheConfig = [] ): ConnectionInterface { return self::createConnection($config, [ 'cache' => array_merge([ 'ttl' => 300, 'enabled' => true, 'prefix' => 'db_query:', 'tags' => ['database_query'], ], $cacheConfig), ]); } public static function createCachedConnectionWithCache( array|DriverConfig $config, Cache $cache, array $additionalConfig = [] ): ConnectionInterface { return self::createCachedConnection($config, array_merge([ 'cache_instance' => $cache, ], $additionalConfig)); } /** * Erstelle Connection mit externem Cache-System */ public static function createConnectionWithCustomCache( array|DriverConfig $config, Cache $cache, string $keyPrefix = 'db_query:', int $ttl = 300 ): ConnectionInterface { return self::createConnection($config, [ 'cache' => [ 'cache_instance' => $cache, 'prefix' => $keyPrefix, 'ttl' => $ttl, 'enabled' => true, ], ]); } public static function createFullFeaturedConnection( array|DriverConfig $config, array $middlewareConfig = [] ): ConnectionInterface { // "Alles dabei" - Connection mit allen Features $defaultConfig = [ 'lazy' => true, 'cache' => [ 'ttl' => 300, 'enabled' => true, 'prefix' => 'db_query:', 'tags' => ['database_query'], ], 'retry' => [ 'max_retries' => 3, 'delay_ms' => 100, ], 'health_check' => [ 'interval' => 30, 'enabled' => true, ], ]; return self::createConnection($config, array_merge($defaultConfig, $middlewareConfig)); } public static function createProductionConnection( array|DriverConfig $config, ?Cache $cache = null, array $middlewareConfig = [] ): ConnectionInterface { // Production-optimierte Konfiguration $defaultConfig = [ 'lazy' => true, 'cache' => [ 'ttl' => 1800, 'enabled' => true, 'prefix' => 'prod_db:', 'tags' => ['database_query', 'production'], ], 'retry' => [ 'max_retries' => 5, 'delay_ms' => 200, ], 'health_check' => [ 'interval' => 60, 'enabled' => true, ], ]; // Wenn externe Cache-Instanz übergeben wird if ($cache !== null) { $defaultConfig['cache']['cache_instance'] = $cache; } return self::createConnection($config, array_merge($defaultConfig, $middlewareConfig)); } private static function createDriver(DriverConfig $config): Driver { return match($config->driverType) { DriverType::MYSQL => new MysqlDriver($config), DriverType::PGSQL => new PostgresDriver($config), DriverType::SQLITE => new SqliteDriver($config), }; } private static function createPdo(Driver $driver): \PDO { try { return match($driver->config->driverType) { DriverType::MYSQL => new Mysql( $driver->dsn, $driver->config->username, $driver->config->password, $driver->options ), DriverType::PGSQL => new Pgsql( $driver->dsn, $driver->config->username, $driver->config->password, $driver->options ), DriverType::SQLITE => new Sqlite( $driver->dsn, $driver->config->username, $driver->config->password, $driver->options ), }; } catch (\PDOException $e) { // Extract SQLSTATE from PDOException $sqlStateCode = self::extractSqlState($e); try { $sqlState = new SqlState($sqlStateCode); } catch (\InvalidArgumentException) { // Fallback to generic exception if SQLSTATE is invalid throw DatabaseException::simple( "Failed to create database connection: {$e->getMessage()}", $e ); } // Map SQLSTATE to specific connection exception throw self::handleConnectionError( $driver->config, $sqlState, $e ); } } /** * Extract SQLSTATE from PDOException */ private static function extractSqlState(\PDOException $e): string { $sqlStateCode = $e->getCode(); // PDO sometimes returns error info array as code if (is_array($sqlStateCode)) { return $sqlStateCode[0] ?? 'HY000'; } // If code is not a valid 5-character SQLSTATE, try errorInfo if (!is_string($sqlStateCode) || strlen($sqlStateCode) !== 5) { // For connection errors, PDO might not have errorInfo available // Fallback to generic driver error return 'HY000'; } return $sqlStateCode; } /** * Handle connection errors with SQLSTATE-aware exceptions */ private static function handleConnectionError( DriverConfig $config, SqlState $sqlState, \PDOException $e ): ConnectionFailedException { $host = $config->host ?? 'unknown'; $database = $config->database ?? 'unknown'; $username = $config->username ?? 'unknown'; return match ($sqlState->code) { '08001' => ConnectionFailedException::cannotConnect( $host, $database, $sqlState, "Could not connect to database server: {$e->getMessage()}", $e ), '08004' => ConnectionFailedException::serverRejectedConnection( $host, $username, $sqlState, $e->getMessage() ), '08006' => ConnectionFailedException::connectionFailedDuringTransaction( $sqlState ), '08007' => ConnectionFailedException::connectionLost( $sqlState, null ), '28000' => ConnectionFailedException::serverRejectedConnection( $host, $username, $sqlState, "Invalid authorization: {$e->getMessage()}" ), default => ConnectionFailedException::cannotConnect( $host, $database, $sqlState, $e->getMessage(), $e ), }; } }