PDO (PHP Data Objects) 是 PHP 官方提供的資料庫存取抽象層,支援 12+ 資料庫驅動,提供預備語句、交易控制、統一錯誤處理,是現代 PHP 專案的標準選擇。
<?php
class Database {
private static ?PDO $instance = null;
public static function getConnection(array $config): PDO {
if (self::$instance === null) {
$dsn = self::buildDsn($config);
self::$instance = new PDO(
$dsn,
$config['username'],
$config['password'],
[
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 self::$instance;
}
private static function buildDsn(array $config): string {
$driver = $config['driver'] ?? 'mysql';
$host = $config['host'] ?? '127.0.0.1';
$port = $config['port'] ?? self::getDefaultPort($driver);
$dbname = $config['dbname'] ?? '';
return match($driver) {
'mysql' => "mysql:host=$host;port=$port;dbname=$dbname;charset=utf8mb4",
'pgsql' => "pgsql:host=$host;port=$port;dbname=$dbname",
'sqlsrv' => "sqlsrv:Server=$host,$port;Database=$dbname",
'oci' => "oci:dbname=//$host:$port/$dbname",
'sqlite' => "sqlite:" . ($dbname ?: ':memory:'),
default => throw new InvalidArgumentException("Unsupported driver: $driver")
};
}
private static function getDefaultPort(string $driver): int {
return match($driver) {
'mysql' => 3306,
'pgsql' => 5432,
'sqlsrv' => 1433,
'oci' => 1521,
default => 3306
};
}
}
?>
<?php
$config = [
'driver' => 'mysql',
'host' => '127.0.0.1',
'port' => 3306,
'dbname' => 'myapp',
'username' => 'app_user',
'password' => 'secure_password',
'charset' => 'utf8mb4' // 支援 emoji
];
$pdo = Database::getConnection($config);
// 建表範例 (InnoDB + utf8mb4)
$pdo->exec("
CREATE TABLE IF NOT EXISTS users (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(191) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
name VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
");
?>
pdo_mysql 擴展utf8mb4 而非 utf8 完整支援 UnicodePDO::MYSQL_ATTR_SSL_CA 等選項<?php
$config = [
'driver' => 'pgsql',
'host' => '127.0.0.1',
'port' => 5432,
'dbname' => 'myapp',
'username' => 'app_user',
'password' => 'secure_password'
];
$pdo = Database::getConnection($config);
// PostgreSQL 專屬功能範例
$pdo->exec("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"");
$pdo->exec("
CREATE TABLE IF NOT EXISTS events (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
payload JSONB NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
)
");
// JSONB 查詢
$stmt = $pdo->prepare("SELECT * FROM events WHERE payload @> ?");
$stmt->execute([json_encode(['type' => 'order_created'])]);
?>
pdo_pgsql 擴展<?php
// Linux 環境需安裝 Microsoft ODBC Driver + pdo_sqlsrv
$config = [
'driver' => 'sqlsrv',
'host' => '192.168.1.100',
'port' => 1433,
'dbname' => 'MyDatabase',
'username' => 'sa',
'password' => 'StrongPassword123'
];
$pdo = Database::getConnection($config);
// SQL Server 專屬:分頁查詢 (2012+)
$stmt = $pdo->prepare("
SELECT * FROM Products
ORDER BY CreatedDate DESC
OFFSET ? ROWS FETCH NEXT ? ROWS ONLY
");
$stmt->execute([($page - 1) * $pageSize, $pageSize]);
?>
pecl install pdo_sqlsrvVARCHAR(MAX)、NVARCHAR(MAX)DATETIME2、DATETIMEOFFSET<?php
// 需安裝 Oracle Instant Client + pdo_oci
$config = [
'driver' => 'oci',
'host' => '192.168.1.200',
'port' => 1521,
'dbname' => 'ORCLPDB1', // Service Name 或 SID
'username' => 'app_user',
'password' => 'secure_password'
];
$pdo = Database::getConnection($config);
// Oracle 專屬:序列 + 觸發器自動編號
$pdo->exec("CREATE SEQUENCE users_seq START WITH 1 INCREMENT BY 1");
$pdo->exec("
CREATE OR REPLACE TRIGGER users_bir
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
SELECT users_seq.NEXTVAL INTO :NEW.id FROM dual;
END;
");
?>
pdo_oci 編譯較複雜,建議使用 Docker 映像預裝OFFSET ... ROWS FETCH NEXT ... ROWS ONLY (12c+)<?php
$config = [
'driver' => 'sqlite',
'dbname' => __DIR__ . '/data/app.db' // 檔案路徑
];
$pdo = Database::getConnection($config);
// SQLite 專屬:記憶體資料庫 (測試極快)
$testPdo = new PDO('sqlite::memory:');
$testPdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 啟用外鍵約束 (預設關閉)
$pdo->exec('PRAGMA foreign_keys = ON');
$pdo->exec('PRAGMA journal_mode = WAL'); // 更好併發效能
?>
pdo_sqlite,無需額外安裝| 資料庫 | PDO 驅動 | Ubuntu/Debian 安裝 | Windows |
|---|---|---|---|
| MySQL | pdo_mysql | apt install php-pdo-mysql | php.ini 取消註解 |
| PostgreSQL | pdo_pgsql | apt install php-pdo-pgsql | php.ini 取消註解 |
| SQL Server | pdo_sqlsrv | ODBC Driver + pecl install pdo_sqlsrv | 下載 DLL 放入 ext |
| Oracle | pdo_oci | Instant Client + 重編譯 PHP | 較複雜,建議 Docker |
| SQLite | pdo_sqlite | 內建 | 內建 |
PDO::ATTR_EMULATE_PREPARES => false 使用真實預備語句charset=utf8mb4,PostgreSQL 預設 UTF-8$pdo->beginTransaction()、commit()、rollBack()PDO::ATTR_PERSISTENT) 需謹慎評估掌握 PDO 統一介面,就能在專案中靈活切換資料庫、編寫可移植的資料存取層,是 PHP 後端工程師必備核心技能。