1. php
  2. /database
  3. /pdo-mysql

PHP PDO and MySQL

Introduction to PDO and MySQL

PHP Data Objects (PDO) is a database abstraction layer providing a uniform interface for accessing databases in PHP. When combined with MySQL, one of the world's most popular relational database systems, PDO offers a powerful, secure, and flexible solution for database operations in web applications.

Understanding PDO with MySQL is essential for building secure, efficient database-driven applications that protect against SQL injection and provide reliable data management.

Why PDO and MySQL Matter

Security: PDO's prepared statements provide excellent protection against SQL injection attacks through proper parameter binding.

Database Abstraction: PDO allows switching between different database systems with minimal code changes.

Performance: Prepared statements can be reused, improving performance for repeated queries.

Error Handling: Comprehensive error reporting and exception handling for debugging and error management.

Advanced Features: Support for transactions, stored procedures, and complex data types.

MySQL Compatibility: Excellent integration with MySQL's features including stored procedures, views, and advanced data types.

Key PDO Features

Prepared Statements: Pre-compiled SQL statements that separate data from code for security and performance.

Parameter Binding: Safe way to include user data in SQL queries without concatenation.

Transaction Support: ACID-compliant transaction handling for data integrity.

Error Modes: Configurable error reporting including exceptions for robust error handling.

Fetch Modes: Multiple ways to retrieve data including arrays, objects, and custom classes.

PDO Connection and Configuration

Establishing MySQL Connections

<?php
/**
 * PDO MySQL Connection and Configuration
 * 
 * Demonstrates secure database connections, configuration options,
 * and connection management best practices.
 */

/**
 * Database connection manager with connection pooling
 */
class DatabaseManager
{
    private static ?PDO $connection = null;
    private static array $config = [];
    private static array $options = [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES => false,
        PDO::ATTR_STRINGIFY_FETCHES => false,
        PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
    ];
    
    /**
     * Initialize database configuration
     */
    public static function configure(array $config): void
    {
        self::$config = array_merge([
            'host' => 'localhost',
            'port' => 3306,
            'database' => '',
            'username' => '',
            'password' => '',
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'timezone' => '+00:00',
            'strict_mode' => true,
            'engine' => 'InnoDB'
        ], $config);
    }
    
    /**
     * Get database connection
     */
    public static function getConnection(): PDO
    {
        if (self::$connection === null) {
            self::connect();
        }
        
        return self::$connection;
    }
    
    /**
     * Establish database connection
     */
    private static function connect(): void
    {
        if (empty(self::$config)) {
            throw new RuntimeException('Database configuration not set');
        }
        
        $dsn = self::buildDSN();
        
        try {
            self::$connection = new PDO(
                $dsn,
                self::$config['username'],
                self::$config['password'],
                self::$options
            );
            
            // Set MySQL session variables
            self::configureMySQLSession();
            
        } catch (PDOException $e) {
            throw new RuntimeException('Database connection failed: ' . $e->getMessage());
        }
    }
    
    /**
     * Build MySQL DSN string
     */
    private static function buildDSN(): string
    {
        $dsn = "mysql:host={self::$config['host']}";
        $dsn .= ";port={self::$config['port']}";
        $dsn .= ";dbname={self::$config['database']}";
        $dsn .= ";charset={self::$config['charset']}";
        
        return $dsn;
    }
    
    /**
     * Configure MySQL session settings
     */
    private static function configureMySQLSession(): void
    {
        $statements = [
            "SET time_zone = '{self::$config['timezone']}'",
            "SET NAMES {self::$config['charset']} COLLATE {self::$config['collation']}"
        ];
        
        if (self::$config['strict_mode']) {
            $statements[] = "SET sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'";
        }
        
        foreach ($statements as $sql) {
            self::$connection->exec($sql);
        }
    }
    
    /**
     * Test database connection
     */
    public static function testConnection(): bool
    {
        try {
            $pdo = self::getConnection();
            $stmt = $pdo->query('SELECT 1');
            return $stmt !== false;
        } catch (Exception $e) {
            return false;
        }
    }
    
    /**
     * Get connection information
     */
    public static function getConnectionInfo(): array
    {
        $pdo = self::getConnection();
        
        return [
            'server_version' => $pdo->getAttribute(PDO::ATTR_SERVER_VERSION),
            'client_version' => $pdo->getAttribute(PDO::ATTR_CLIENT_VERSION),
            'connection_status' => $pdo->getAttribute(PDO::ATTR_CONNECTION_STATUS),
            'server_info' => $pdo->getAttribute(PDO::ATTR_SERVER_INFO),
            'autocommit' => $pdo->getAttribute(PDO::ATTR_AUTOCOMMIT)
        ];
    }
    
    /**
     * Close database connection
     */
    public static function disconnect(): void
    {
        self::$connection = null;
    }
    
    /**
     * Reconnect to database
     */
    public static function reconnect(): void
    {
        self::disconnect();
        self::connect();
    }
}

/**
 * Connection factory for multiple databases
 */
class ConnectionFactory
{
    private static array $connections = [];
    private static array $configs = [];
    
    /**
     * Register database configuration
     */
    public static function register(string $name, array $config): void
    {
        self::$configs[$name] = $config;
    }
    
    /**
     * Get database connection by name
     */
    public static function get(string $name = 'default'): PDO
    {
        if (!isset(self::$connections[$name])) {
            if (!isset(self::$configs[$name])) {
                throw new InvalidArgumentException("Database configuration '$name' not found");
            }
            
            self::$connections[$name] = self::createConnection(self::$configs[$name]);
        }
        
        return self::$connections[$name];
    }
    
    /**
     * Create new database connection
     */
    private static function createConnection(array $config): PDO
    {
        $dsn = "mysql:host={$config['host']};dbname={$config['database']};charset=utf8mb4";
        
        $options = [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false,
            PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"
        ];
        
        return new PDO($dsn, $config['username'], $config['password'], $options);
    }
}

// Usage examples
try {
    // Configure primary database
    DatabaseManager::configure([
        'host' => 'localhost',
        'database' => 'my_app',
        'username' => 'app_user',
        'password' => 'secure_password',
        'timezone' => '+00:00'
    ]);
    
    // Test connection
    if (DatabaseManager::testConnection()) {
        echo "Database connection successful!\n";
        
        // Get connection info
        $info = DatabaseManager::getConnectionInfo();
        echo "MySQL Version: " . $info['server_version'] . "\n";
        echo "Client Version: " . $info['client_version'] . "\n";
    }
    
    // Multiple database connections
    ConnectionFactory::register('main', [
        'host' => 'localhost',
        'database' => 'main_db',
        'username' => 'main_user',
        'password' => 'main_pass'
    ]);
    
    ConnectionFactory::register('analytics', [
        'host' => 'analytics-server',
        'database' => 'analytics_db',
        'username' => 'analytics_user',
        'password' => 'analytics_pass'
    ]);
    
    $mainDB = ConnectionFactory::get('main');
    $analyticsDB = ConnectionFactory::get('analytics');
    
} catch (Exception $e) {
    echo "Database error: " . $e->getMessage() . "\n";
}
?>

Prepared Statements and Parameter Binding

Secure Query Execution

<?php
/**
 * PDO Prepared Statements and Parameter Binding
 * 
 * Demonstrates secure query execution, parameter binding,
 * and various query patterns with MySQL.
 */

/**
 * Database query builder with prepared statements
 */
class QueryBuilder
{
    private PDO $pdo;
    private string $table = '';
    private array $conditions = [];
    private array $bindings = [];
    private string $orderBy = '';
    private string $groupBy = '';
    private ?int $limit = null;
    private ?int $offset = null;
    
    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }
    
    /**
     * Set table name
     */
    public function table(string $table): self
    {
        $this->table = $table;
        return $this;
    }
    
    /**
     * Add WHERE condition
     */
    public function where(string $column, string $operator, $value): self
    {
        $placeholder = ':' . $column . '_' . count($this->bindings);
        $this->conditions[] = "$column $operator $placeholder";
        $this->bindings[$placeholder] = $value;
        
        return $this;
    }
    
    /**
     * Add WHERE IN condition
     */
    public function whereIn(string $column, array $values): self
    {
        $placeholders = [];
        foreach ($values as $i => $value) {
            $placeholder = ":{$column}_in_{$i}";
            $placeholders[] = $placeholder;
            $this->bindings[$placeholder] = $value;
        }
        
        $this->conditions[] = "$column IN (" . implode(',', $placeholders) . ")";
        return $this;
    }
    
    /**
     * Add WHERE BETWEEN condition
     */
    public function whereBetween(string $column, $start, $end): self
    {
        $startPlaceholder = ":{$column}_start";
        $endPlaceholder = ":{$column}_end";
        
        $this->conditions[] = "$column BETWEEN $startPlaceholder AND $endPlaceholder";
        $this->bindings[$startPlaceholder] = $start;
        $this->bindings[$endPlaceholder] = $end;
        
        return $this;
    }
    
    /**
     * Add WHERE LIKE condition
     */
    public function whereLike(string $column, string $pattern): self
    {
        $placeholder = ":{$column}_like";
        $this->conditions[] = "$column LIKE $placeholder";
        $this->bindings[$placeholder] = $pattern;
        
        return $this;
    }
    
    /**
     * Add ORDER BY clause
     */
    public function orderBy(string $column, string $direction = 'ASC'): self
    {
        $this->orderBy = "ORDER BY $column $direction";
        return $this;
    }
    
    /**
     * Add GROUP BY clause
     */
    public function groupBy(string $column): self
    {
        $this->groupBy = "GROUP BY $column";
        return $this;
    }
    
    /**
     * Add LIMIT clause
     */
    public function limit(int $limit, ?int $offset = null): self
    {
        $this->limit = $limit;
        $this->offset = $offset;
        return $this;
    }
    
    /**
     * Execute SELECT query
     */
    public function get(array $columns = ['*']): array
    {
        $columnList = implode(', ', $columns);
        $sql = "SELECT $columnList FROM {$this->table}";
        
        if (!empty($this->conditions)) {
            $sql .= ' WHERE ' . implode(' AND ', $this->conditions);
        }
        
        if ($this->groupBy) {
            $sql .= ' ' . $this->groupBy;
        }
        
        if ($this->orderBy) {
            $sql .= ' ' . $this->orderBy;
        }
        
        if ($this->limit) {
            $sql .= " LIMIT {$this->limit}";
            if ($this->offset) {
                $sql .= " OFFSET {$this->offset}";
            }
        }
        
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($this->bindings);
        
        return $stmt->fetchAll();
    }
    
    /**
     * Execute SELECT query and return first result
     */
    public function first(array $columns = ['*']): ?array
    {
        $this->limit(1);
        $results = $this->get($columns);
        return $results[0] ?? null;
    }
    
    /**
     * Get count of records
     */
    public function count(): int
    {
        $sql = "SELECT COUNT(*) FROM {$this->table}";
        
        if (!empty($this->conditions)) {
            $sql .= ' WHERE ' . implode(' AND ', $this->conditions);
        }
        
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($this->bindings);
        
        return (int) $stmt->fetchColumn();
    }
    
    /**
     * Insert record
     */
    public function insert(array $data): int
    {
        $columns = array_keys($data);
        $placeholders = array_map(fn($col) => ":$col", $columns);
        
        $sql = "INSERT INTO {$this->table} (" . implode(', ', $columns) . ") VALUES (" . implode(', ', $placeholders) . ")";
        
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($data);
        
        return (int) $this->pdo->lastInsertId();
    }
    
    /**
     * Update records
     */
    public function update(array $data): int
    {
        $setParts = [];
        foreach ($data as $column => $value) {
            $setParts[] = "$column = :update_$column";
            $this->bindings[":update_$column"] = $value;
        }
        
        $sql = "UPDATE {$this->table} SET " . implode(', ', $setParts);
        
        if (!empty($this->conditions)) {
            $sql .= ' WHERE ' . implode(' AND ', $this->conditions);
        }
        
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($this->bindings);
        
        return $stmt->rowCount();
    }
    
    /**
     * Delete records
     */
    public function delete(): int
    {
        if (empty($this->conditions)) {
            throw new RuntimeException('DELETE queries must have WHERE conditions');
        }
        
        $sql = "DELETE FROM {$this->table} WHERE " . implode(' AND ', $this->conditions);
        
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($this->bindings);
        
        return $stmt->rowCount();
    }
    
    /**
     * Reset query builder
     */
    public function reset(): self
    {
        $this->conditions = [];
        $this->bindings = [];
        $this->orderBy = '';
        $this->groupBy = '';
        $this->limit = null;
        $this->offset = null;
        
        return $this;
    }
}

/**
 * User repository with prepared statements
 */
class UserRepository
{
    private PDO $pdo;
    private QueryBuilder $query;
    
    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
        $this->query = new QueryBuilder($pdo);
    }
    
    /**
     * Find user by ID
     */
    public function findById(int $id): ?array
    {
        return $this->query->table('users')
            ->where('id', '=', $id)
            ->first();
    }
    
    /**
     * Find user by email
     */
    public function findByEmail(string $email): ?array
    {
        return $this->query->reset()
            ->table('users')
            ->where('email', '=', $email)
            ->first();
    }
    
    /**
     * Find users by role
     */
    public function findByRole(string $role): array
    {
        return $this->query->reset()
            ->table('users')
            ->where('role', '=', $role)
            ->where('active', '=', 1)
            ->orderBy('name')
            ->get();
    }
    
    /**
     * Search users by name
     */
    public function searchByName(string $search): array
    {
        return $this->query->reset()
            ->table('users')
            ->whereLike('name', "%$search%")
            ->orderBy('name')
            ->get();
    }
    
    /**
     * Get paginated users
     */
    public function getPaginated(int $page, int $perPage): array
    {
        $offset = ($page - 1) * $perPage;
        
        $users = $this->query->reset()
            ->table('users')
            ->orderBy('created_at', 'DESC')
            ->limit($perPage, $offset)
            ->get();
        
        $total = $this->query->reset()
            ->table('users')
            ->count();
        
        return [
            'data' => $users,
            'total' => $total,
            'page' => $page,
            'per_page' => $perPage,
            'pages' => ceil($total / $perPage)
        ];
    }
    
    /**
     * Create new user
     */
    public function create(array $userData): int
    {
        $userData['created_at'] = date('Y-m-d H:i:s');
        $userData['updated_at'] = date('Y-m-d H:i:s');
        
        return $this->query->reset()
            ->table('users')
            ->insert($userData);
    }
    
    /**
     * Update user
     */
    public function update(int $id, array $userData): bool
    {
        $userData['updated_at'] = date('Y-m-d H:i:s');
        
        $affected = $this->query->reset()
            ->table('users')
            ->where('id', '=', $id)
            ->update($userData);
        
        return $affected > 0;
    }
    
    /**
     * Delete user
     */
    public function delete(int $id): bool
    {
        $affected = $this->query->reset()
            ->table('users')
            ->where('id', '=', $id)
            ->delete();
        
        return $affected > 0;
    }
    
    /**
     * Soft delete user
     */
    public function softDelete(int $id): bool
    {
        return $this->update($id, [
            'deleted_at' => date('Y-m-d H:i:s'),
            'active' => 0
        ]);
    }
    
    /**
     * Batch insert users
     */
    public function batchInsert(array $users): bool
    {
        if (empty($users)) {
            return false;
        }
        
        $columns = array_keys($users[0]);
        $placeholders = '(' . implode(',', array_map(fn($col) => ":$col", $columns)) . ')';
        
        $sql = "INSERT INTO users (" . implode(', ', $columns) . ") VALUES ";
        $values = [];
        $bindings = [];
        
        foreach ($users as $index => $user) {
            $rowPlaceholders = [];
            foreach ($columns as $column) {
                $placeholder = ":{$column}_{$index}";
                $rowPlaceholders[] = $placeholder;
                $bindings[$placeholder] = $user[$column];
            }
            $values[] = '(' . implode(',', $rowPlaceholders) . ')';
        }
        
        $sql .= implode(',', $values);
        
        $stmt = $this->pdo->prepare($sql);
        return $stmt->execute($bindings);
    }
    
    /**
     * Get user statistics
     */
    public function getStatistics(): array
    {
        $sql = "
            SELECT 
                COUNT(*) as total,
                SUM(CASE WHEN active = 1 THEN 1 ELSE 0 END) as active,
                SUM(CASE WHEN role = 'admin' THEN 1 ELSE 0 END) as admins,
                AVG(TIMESTAMPDIFF(YEAR, created_at, NOW())) as avg_age_years
            FROM users
        ";
        
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute();
        
        return $stmt->fetch();
    }
}

// Usage examples
try {
    $pdo = DatabaseManager::getConnection();
    $userRepo = new UserRepository($pdo);
    
    // Find user by ID
    $user = $userRepo->findById(1);
    if ($user) {
        echo "Found user: " . $user['name'] . "\n";
    }
    
    // Search users
    $searchResults = $userRepo->searchByName('john');
    echo "Found " . count($searchResults) . " users matching 'john'\n";
    
    // Get paginated results
    $page = $userRepo->getPaginated(1, 10);
    echo "Page 1 of {$page['pages']} ({$page['total']} total users)\n";
    
    // Create new user
    $newUserId = $userRepo->create([
        'name' => 'Jane Doe',
        'email' => '[email protected]',
        'role' => 'user',
        'active' => 1
    ]);
    echo "Created user with ID: $newUserId\n";
    
    // Update user
    $updated = $userRepo->update($newUserId, [
        'name' => 'Jane Smith',
        'role' => 'admin'
    ]);
    echo "User updated: " . ($updated ? 'Yes' : 'No') . "\n";
    
    // Get statistics
    $stats = $userRepo->getStatistics();
    echo "User Statistics:\n";
    echo "- Total: {$stats['total']}\n";
    echo "- Active: {$stats['active']}\n";
    echo "- Admins: {$stats['admins']}\n";
    
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}
?>

Error Handling and Debugging

Comprehensive Error Management

<?php
/**
 * PDO Error Handling and Debugging
 * 
 * Demonstrates error handling, debugging techniques,
 * and logging for PDO MySQL operations.
 */

/**
 * Database error handler with logging
 */
class DatabaseErrorHandler
{
    private string $logFile;
    private bool $debugMode;
    
    public function __construct(string $logFile = 'database.log', bool $debugMode = false)
    {
        $this->logFile = $logFile;
        $this->debugMode = $debugMode;
    }
    
    /**
     * Handle PDO exceptions
     */
    public function handlePDOException(PDOException $e, string $sql = '', array $bindings = []): void
    {
        $error = [
            'timestamp' => date('Y-m-d H:i:s'),
            'error_code' => $e->getCode(),
            'error_message' => $e->getMessage(),
            'sql_state' => $e->errorInfo[0] ?? 'Unknown',
            'driver_code' => $e->errorInfo[1] ?? 'Unknown',
            'driver_message' => $e->errorInfo[2] ?? 'Unknown',
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'sql' => $sql,
            'bindings' => $bindings
        ];
        
        $this->logError($error);
        
        if ($this->debugMode) {
            $this->displayError($error);
        }
    }
    
    /**
     * Log error to file
     */
    private function logError(array $error): void
    {
        $logEntry = sprintf(
            "[%s] PDO Error %s: %s | SQL: %s | Bindings: %s | File: %s:%d\n",
            $error['timestamp'],
            $error['error_code'],
            $error['error_message'],
            $error['sql'],
            json_encode($error['bindings']),
            $error['file'],
            $error['line']
        );
        
        file_put_contents($this->logFile, $logEntry, FILE_APPEND | LOCK_EX);
    }
    
    /**
     * Display error in debug mode
     */
    private function displayError(array $error): void
    {
        echo "<div style='background: #ffebee; border: 1px solid #f44336; padding: 15px; margin: 10px; border-radius: 4px;'>";
        echo "<h3 style='color: #d32f2f; margin: 0 0 10px 0;'>Database Error</h3>";
        echo "<p><strong>Error:</strong> {$error['error_message']}</p>";
        echo "<p><strong>Code:</strong> {$error['error_code']}</p>";
        echo "<p><strong>SQL State:</strong> {$error['sql_state']}</p>";
        
        if ($error['sql']) {
            echo "<p><strong>SQL:</strong> <code>{$error['sql']}</code></p>";
        }
        
        if (!empty($error['bindings'])) {
            echo "<p><strong>Bindings:</strong> <code>" . json_encode($error['bindings']) . "</code></p>";
        }
        
        echo "<p><strong>Location:</strong> {$error['file']}:{$error['line']}</p>";
        echo "</div>";
    }
    
    /**
     * Check MySQL connection health
     */
    public function checkConnectionHealth(PDO $pdo): array
    {
        $health = [
            'connected' => false,
            'server_version' => null,
            'uptime' => null,
            'threads_connected' => null,
            'queries_per_second' => null,
            'errors' => []
        ];
        
        try {
            // Test basic connectivity
            $stmt = $pdo->query('SELECT 1');
            $health['connected'] = true;
            
            // Get server version
            $health['server_version'] = $pdo->getAttribute(PDO::ATTR_SERVER_VERSION);
            
            // Get server status
            $stmt = $pdo->query('SHOW STATUS WHERE Variable_name IN ("Uptime", "Threads_connected", "Queries")');
            $status = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
            
            $health['uptime'] = $status['Uptime'] ?? null;
            $health['threads_connected'] = $status['Threads_connected'] ?? null;
            
            if (isset($status['Queries']) && $health['uptime']) {
                $health['queries_per_second'] = round($status['Queries'] / $health['uptime'], 2);
            }
            
        } catch (PDOException $e) {
            $health['errors'][] = $e->getMessage();
        }
        
        return $health;
    }
}

/**
 * Query profiler for performance monitoring
 */
class QueryProfiler
{
    private array $queries = [];
    private bool $enabled = true;
    
    /**
     * Start query profiling
     */
    public function startQuery(string $sql, array $bindings = []): string
    {
        if (!$this->enabled) {
            return '';
        }
        
        $queryId = uniqid();
        $this->queries[$queryId] = [
            'sql' => $sql,
            'bindings' => $bindings,
            'start_time' => microtime(true),
            'start_memory' => memory_get_usage(true),
            'end_time' => null,
            'end_memory' => null,
            'duration' => null,
            'memory_delta' => null
        ];
        
        return $queryId;
    }
    
    /**
     * End query profiling
     */
    public function endQuery(string $queryId): void
    {
        if (!$this->enabled || !isset($this->queries[$queryId])) {
            return;
        }
        
        $endTime = microtime(true);
        $endMemory = memory_get_usage(true);
        
        $this->queries[$queryId]['end_time'] = $endTime;
        $this->queries[$queryId]['end_memory'] = $endMemory;
        $this->queries[$queryId]['duration'] = $endTime - $this->queries[$queryId]['start_time'];
        $this->queries[$queryId]['memory_delta'] = $endMemory - $this->queries[$queryId]['start_memory'];
    }
    
    /**
     * Get profiling results
     */
    public function getResults(): array
    {
        $totalQueries = count($this->queries);
        $totalTime = array_sum(array_column($this->queries, 'duration'));
        $avgTime = $totalQueries > 0 ? $totalTime / $totalQueries : 0;
        
        $slowQueries = array_filter($this->queries, fn($q) => $q['duration'] > 0.1);
        
        return [
            'total_queries' => $totalQueries,
            'total_time' => round($totalTime, 4),
            'average_time' => round($avgTime, 4),
            'slow_queries' => count($slowQueries),
            'queries' => $this->queries
        ];
    }
    
    /**
     * Reset profiler
     */
    public function reset(): void
    {
        $this->queries = [];
    }
    
    /**
     * Enable/disable profiling
     */
    public function setEnabled(bool $enabled): void
    {
        $this->enabled = $enabled;
    }
}

/**
 * Enhanced PDO wrapper with error handling and profiling
 */
class SafePDO
{
    private PDO $pdo;
    private DatabaseErrorHandler $errorHandler;
    private QueryProfiler $profiler;
    
    public function __construct(PDO $pdo, bool $debugMode = false)
    {
        $this->pdo = $pdo;
        $this->errorHandler = new DatabaseErrorHandler('database.log', $debugMode);
        $this->profiler = new QueryProfiler();
    }
    
    /**
     * Prepare and execute query safely
     */
    public function execute(string $sql, array $bindings = []): PDOStatement
    {
        $queryId = $this->profiler->startQuery($sql, $bindings);
        
        try {
            $stmt = $this->pdo->prepare($sql);
            $stmt->execute($bindings);
            
            $this->profiler->endQuery($queryId);
            return $stmt;
            
        } catch (PDOException $e) {
            $this->profiler->endQuery($queryId);
            $this->errorHandler->handlePDOException($e, $sql, $bindings);
            throw $e;
        }
    }
    
    /**
     * Execute query and fetch all results
     */
    public function fetchAll(string $sql, array $bindings = []): array
    {
        $stmt = $this->execute($sql, $bindings);
        return $stmt->fetchAll();
    }
    
    /**
     * Execute query and fetch single row
     */
    public function fetchOne(string $sql, array $bindings = []): ?array
    {
        $stmt = $this->execute($sql, $bindings);
        $result = $stmt->fetch();
        return $result ?: null;
    }
    
    /**
     * Execute query and fetch single column
     */
    public function fetchColumn(string $sql, array $bindings = []): mixed
    {
        $stmt = $this->execute($sql, $bindings);
        return $stmt->fetchColumn();
    }
    
    /**
     * Begin transaction
     */
    public function beginTransaction(): bool
    {
        try {
            return $this->pdo->beginTransaction();
        } catch (PDOException $e) {
            $this->errorHandler->handlePDOException($e);
            throw $e;
        }
    }
    
    /**
     * Commit transaction
     */
    public function commit(): bool
    {
        try {
            return $this->pdo->commit();
        } catch (PDOException $e) {
            $this->errorHandler->handlePDOException($e);
            throw $e;
        }
    }
    
    /**
     * Rollback transaction
     */
    public function rollback(): bool
    {
        try {
            return $this->pdo->rollback();
        } catch (PDOException $e) {
            $this->errorHandler->handlePDOException($e);
            throw $e;
        }
    }
    
    /**
     * Get profiling results
     */
    public function getProfilingResults(): array
    {
        return $this->profiler->getResults();
    }
    
    /**
     * Check database health
     */
    public function getHealthStatus(): array
    {
        return $this->errorHandler->checkConnectionHealth($this->pdo);
    }
    
    /**
     * Get underlying PDO instance
     */
    public function getPDO(): PDO
    {
        return $this->pdo;
    }
}

// Usage examples
try {
    $pdo = DatabaseManager::getConnection();
    $safePDO = new SafePDO($pdo, true);
    
    // Execute queries with profiling and error handling
    $users = $safePDO->fetchAll('SELECT * FROM users WHERE active = :active', ['active' => 1]);
    echo "Found " . count($users) . " active users\n";
    
    // Test error handling
    try {
        $safePDO->fetchAll('SELECT * FROM non_existent_table');
    } catch (PDOException $e) {
        echo "Caught and handled database error\n";
    }
    
    // Check database health
    $health = $safePDO->getHealthStatus();
    echo "Database connected: " . ($health['connected'] ? 'Yes' : 'No') . "\n";
    echo "MySQL version: " . ($health['server_version'] ?? 'Unknown') . "\n";
    
    // Get profiling results
    $profile = $safePDO->getProfilingResults();
    echo "Executed {$profile['total_queries']} queries in {$profile['total_time']}s\n";
    echo "Average query time: {$profile['average_time']}s\n";
    
} catch (Exception $e) {
    echo "Application error: " . $e->getMessage() . "\n";
}
?>

For more PHP database topics:

Summary

PDO with MySQL provides a robust foundation for PHP database operations:

Secure Connections: Proper configuration and connection management prevent security vulnerabilities and ensure reliable database access.

Prepared Statements: Parameter binding protects against SQL injection while improving performance through statement reuse.

Error Handling: Comprehensive error management with logging and debugging capabilities ensures robust application behavior.

Query Building: Structured query builders provide clean, maintainable database operations with proper parameter binding.

Performance Monitoring: Query profiling and health monitoring enable optimization and troubleshooting of database operations.

Transaction Support: ACID-compliant transaction handling ensures data integrity in complex operations.

Best Practices: Following established patterns for connection management, error handling, and query execution creates maintainable, secure applications.

Mastering PDO with MySQL enables you to build secure, efficient database-driven PHP applications with professional error handling and performance monitoring capabilities.