需要把 PDO 用起来,第一步就是把 DSN(Data Source Name)写对。本文系统梳理常见数据库的 DSN 语法、易错点与最佳实践,并给出可直接套用的示例代码与校验清单。
DSN:告诉 PDO 要连接哪个驱动(mysql、pgsql、sqlite …)、哪台主机、哪个库,以及其他连接参数。
位置:new PDO($dsn, $username, $password, $options)
结构:<driver>:<key>=<value>;[<key>=<value>;…]
区分大小写:驱动名与键名通常不区分大小写,但值区分(如文件路径)。
基础:
<span><span><span class="hljs-variable">$dsn</span></span><span> = </span><span><span class="hljs-string">'mysql:host=127.0.0.1;port=3306;dbname=app;charset=utf8mb4'</span></span><span>;
</span><span><span class="hljs-variable">$pdo</span></span><span> = </span><span><span class="hljs-keyword">new</span></span><span> </span><span><span class="hljs-title function_ invoke__">PDO</span></span><span>(</span><span><span class="hljs-variable">$dsn</span></span><span>, </span><span><span class="hljs-string">'user'</span></span><span>, </span><span><span class="hljs-string">'pass'</span></span><span>, [
PDO::</span><span><span class="hljs-variable constant_">ATTR_ERRMODE</span></span><span> => PDO::</span><span><span class="hljs-variable constant_">ERRMODE_EXCEPTION</span></span><span>,
]);
</span></span>
要点与可选项:
host:可用域名/IP;若走 UNIX Socket,用 unix_socket=/path/mysql.sock,不要再写 host/port。
dbname:目标数据库名。
charset:强烈建议设为 utf8mb4。
port:可省略,默认 3306。
ssl:新版本 MySQL/驱动可通过 PDO::MYSQL_ATTR_SSL_* 选项配合(见文末“安全与性能选项”)。
UNIX Socket 示例:
<span><span><span class="hljs-variable">$dsn</span></span><span> = </span><span><span class="hljs-string">'mysql:unix_socket=/var/run/mysqld/mysqld.sock;dbname=app;charset=utf8mb4'</span></span><span>;
</span></span>
多参数拼接安全写法(防分号/特殊字符):
<span><span><span class="hljs-variable">$params</span></span><span> = [
</span><span><span class="hljs-string">'host'</span></span><span> => </span><span><span class="hljs-string">'db.internal'</span></span><span>,
</span><span><span class="hljs-string">'port'</span></span><span> => </span><span><span class="hljs-number">3306</span></span><span>,
</span><span><span class="hljs-string">'dbname'</span></span><span> => </span><span><span class="hljs-string">'app'</span></span><span>,
</span><span><span class="hljs-string">'charset'</span></span><span> => </span><span><span class="hljs-string">'utf8mb4'</span></span><span>,
];
</span><span><span class="hljs-variable">$dsn</span></span><span> = </span><span><span class="hljs-string">'mysql:'</span></span><span> . </span><span><span class="hljs-title function_ invoke__">implode</span></span><span>(</span><span><span class="hljs-string">';'</span></span><span>, </span><span><span class="hljs-title function_ invoke__">array_map</span></span><span>(
fn(</span><span><span class="hljs-variable">$k</span></span><span>,</span><span><span class="hljs-variable">$v</span></span><span>) => </span><span><span class="hljs-variable">$k</span></span><span> . </span><span><span class="hljs-string">'='</span></span><span> . </span><span><span class="hljs-title function_ invoke__">str_replace</span></span><span>(</span><span><span class="hljs-string">';'</span></span><span>, </span><span><span class="hljs-string">'\;'</span></span><span>, (</span><span><span class="hljs-keyword">string</span></span><span>)</span><span><span class="hljs-variable">$v</span></span><span>),
</span><span><span class="hljs-title function_ invoke__">array_keys</span></span><span>(</span><span><span class="hljs-variable">$params</span></span><span>),
</span><span><span class="hljs-variable">$params</span></span><span>
));
</span></span>
基础:
<span><span><span class="hljs-variable">$dsn</span></span><span> = </span><span><span class="hljs-string">'pgsql:host=127.0.0.1;port=5432;dbname=app'</span></span><span>;
</span><span><span class="hljs-variable">$pdo</span></span><span> = </span><span><span class="hljs-keyword">new</span></span><span> </span><span><span class="hljs-title function_ invoke__">PDO</span></span><span>(</span><span><span class="hljs-variable">$dsn</span></span><span>, </span><span><span class="hljs-string">'user'</span></span><span>, </span><span><span class="hljs-string">'pass'</span></span><span>);
</span></span>
要点:
host 可为路径(UNIX domain socket 目录,通常是 /var/run/postgresql),此时可省略 port。
字符集建议在 options 或连接后执行 SET NAMES,或用 options='--client_encoding=UTF8'。
某些场景更偏好 host=/var/run/postgresql 写法。
UNIX Socket 示例:
<span><span><span class="hljs-variable">$dsn</span></span><span> = </span><span><span class="hljs-string">"pgsql:host=/var/run/postgresql;dbname=app"</span></span><span>;
</span></span>
文件数据库:
<span><span><span class="hljs-variable">$dsn</span></span><span> = </span><span><span class="hljs-string">'sqlite:/path/to/database.sqlite'</span></span><span>;
</span><span><span class="hljs-variable">$pdo</span></span><span> = </span><span><span class="hljs-keyword">new</span></span><span> </span><span><span class="hljs-title function_ invoke__">PDO</span></span><span>(</span><span><span class="hljs-variable">$dsn</span></span><span>);
</span></span>
内存数据库:
<span><span><span class="hljs-variable">$dsn</span></span><span> = </span><span><span class="hljs-string">'sqlite::memory:'</span></span><span>;
</span><span><span class="hljs-variable">$pdo</span></span><span> = </span><span><span class="hljs-keyword">new</span></span><span> </span><span><span class="hljs-title function_ invoke__">PDO</span></span><span>(</span><span><span class="hljs-variable">$dsn</span></span><span>);
</span></span>
要点:
路径包含空格或分号时,建议真实文件路径中避免特殊字符;Windows 下使用双反斜杠或斜杠路径:C:\\data\\db.sqlite 或 C:/data/db.sqlite。
基础:
<span><span><span class="hljs-variable">$dsn</span></span><span> = </span><span><span class="hljs-string">'sqlsrv:Server=127.0.0.1,1433;Database=app'</span></span><span>;
</span><span><span class="hljs-variable">$pdo</span></span><span> = </span><span><span class="hljs-keyword">new</span></span><span> </span><span><span class="hljs-title function_ invoke__">PDO</span></span><span>(</span><span><span class="hljs-variable">$dsn</span></span><span>, </span><span><span class="hljs-string">'user'</span></span><span>, </span><span><span class="hljs-string">'pass'</span></span><span>);
</span></span>
要点:
Server 可写 host,port 或 hostname\INSTANCE。
建议通过选项设置编码:PDO::SQLSRV_ATTR_ENCODING。
TLS/加密通常通过驱动选项(见文末)。
命名实例:
<span><span><span class="hljs-variable">$dsn</span></span><span> = </span><span><span class="hljs-string">'sqlsrv:Server=WIN-SRV\\SQLEXPRESS;Database=app'</span></span><span>;
</span></span>
基础:
<span><span><span class="hljs-variable">$dsn</span></span><span> = </span><span><span class="hljs-string">'oci:dbname=//127.0.0.1:1521/ORCL;charset=AL32UTF8'</span></span><span>;
</span><span><span class="hljs-variable">$pdo</span></span><span> = </span><span><span class="hljs-keyword">new</span></span><span> </span><span><span class="hljs-title function_ invoke__">PDO</span></span><span>(</span><span><span class="hljs-variable">$dsn</span></span><span>, </span><span><span class="hljs-string">'user'</span></span><span>, </span><span><span class="hljs-string">'pass'</span></span><span>);
</span></span>
要点:
dbname 支持 //host:port/service_name 或本地 tnsnames.ora 中的别名。
推荐 AL32UTF8 编码。
<span><span><span class="hljs-variable">$dsn</span></span><span> = </span><span><span class="hljs-string">'ibm:DRIVER={IBM DB2 ODBC DRIVER};DATABASE=APP;HOSTNAME=127.0.0.1;PORT=50000;PROTOCOL=TCPIP;'</span></span><span>;
</span><span><span class="hljs-variable">$pdo</span></span><span> = </span><span><span class="hljs-keyword">new</span></span><span> </span><span><span class="hljs-title function_ invoke__">PDO</span></span><span>(</span><span><span class="hljs-variable">$dsn</span></span><span>, </span><span><span class="hljs-string">'user'</span></span><span>, </span><span><span class="hljs-string">'pass'</span></span><span>);
</span></span>
<span><span><span class="hljs-variable">$dsn</span></span><span> = </span><span><span class="hljs-string">'firebird:dbname=localhost:/path/to/db.fdb;charset=UTF8'</span></span><span>;
</span><span><span class="hljs-variable">$pdo</span></span><span> = </span><span><span class="hljs-keyword">new</span></span><span> </span><span><span class="hljs-title function_ invoke__">PDO</span></span><span>(</span><span><span class="hljs-variable">$dsn</span></span><span>, </span><span><span class="hljs-string">'user'</span></span><span>, </span><span><span class="hljs-string">'pass'</span></span><span>);
</span></span>
规范:<driver>:key=value;key=value,分号是分隔符。
若值可能包含分号(极少见),建议不要塞进 DSN,而是改走 PDO 第四参 $options 或换成不含分号的配置方式(如环境变量分发,再拼 DSN)。
MySQL:在 DSN 里加 charset=utf8mb4 最直观;连接后不要再 SET NAMES 以免重复。
PostgreSQL:推荐在 options 里设置 --client_encoding=UTF8,或使用连接后 SET client_encoding TO 'UTF8'。
SQL Server:通过选项 PDO::SQLSRV_ATTR_ENCODING 设置(如 SQLSRV_ENCODING_UTF8)。
MySQL 里 unix_socket 与 host/port 不要混用。
PostgreSQL 的 socket 通过将 host 指向目录实现。
SQLite 等文件路径在 Windows 上推荐用双反斜杠或正斜杠:C:\\path\\db.sqlite 或 C:/path/db.sqlite。
不把账号密码硬编码进仓库:
.env:DB_DSN=mysql:host=…;dbname=…;charset=utf8mb4
运行时:$pdo = new PDO(getenv('DB_DSN'), getenv('DB_USER'), getenv('DB_PASS'));
<span><span><span class="hljs-variable">$opts</span></span><span> = [
PDO::</span><span><span class="hljs-variable constant_">ATTR_ERRMODE</span></span><span> => PDO::</span><span><span class="hljs-variable constant_">ERRMODE_EXCEPTION</span></span><span>, </span><span><span class="hljs-comment">// 抛异常,便于定位</span></span><span>
PDO::</span><span><span class="hljs-variable constant_">ATTR_DEFAULT_FETCH_MODE</span></span><span> => PDO::</span><span><span class="hljs-variable constant_">FETCH_ASSOC</span></span><span>, </span><span><span class="hljs-comment">// 默认返回关联数组</span></span><span>
PDO::</span><span><span class="hljs-variable constant_">ATTR_EMULATE_PREPARES</span></span><span> => </span><span><span class="hljs-literal">false</span></span><span>, </span><span><span class="hljs-comment">// 让驱动使用原生预处理(安全/性能更稳)</span></span><span>
];
</span><span><span class="hljs-comment">// MySQL SSL 示例(驱动支持时):</span></span><span>
</span><span><span class="hljs-variable">$opts</span></span><span>[PDO::</span><span><span class="hljs-variable constant_">MYSQL_ATTR_SSL_CA</span></span><span>] = </span><span><span class="hljs-string">'/etc/ssl/certs/ca.pem'</span></span><span>;
</span><span><span class="hljs-comment">// $opts[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = false; // 若需跳过校验(不推荐)</span></span><span>
</span><span><span class="hljs-variable">$pdo</span></span><span> = </span><span><span class="hljs-keyword">new</span></span><span> </span><span><span class="hljs-title function_ invoke__">PDO</span></span><span>(</span><span><span class="hljs-variable">$dsn</span></span><span>, </span><span><span class="hljs-variable">$user</span></span><span>, </span><span><span class="hljs-variable">$pass</span></span><span>, </span><span><span class="hljs-variable">$opts</span></span><span>);
</span></span>
提示:
预处理语句配合 ? 或命名占位符能避免 SQL 注入。
生产环境务必开启 TLS/SSL(需要服务器与驱动共同支持)。
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-function"><span class="hljs-keyword">function</span></span></span><span> </span><span><span class="hljs-title">build_mysql_dsn</span></span><span>(</span><span><span class="hljs-params"><span class="hljs-keyword">array</span></span></span><span> </span><span><span class="hljs-variable">$cfg</span></span><span>): </span><span><span class="hljs-title">string</span></span><span> {
</span><span><span class="hljs-comment">// 支持 host/port 或 unix_socket,自动过滤空值</span></span><span>
</span><span><span class="hljs-variable">$parts</span></span><span> = [];
</span><span><span class="hljs-variable">$parts</span></span><span>[] = </span><span><span class="hljs-keyword">isset</span></span><span>(</span><span><span class="hljs-variable">$cfg</span></span><span>[</span><span><span class="hljs-string">'unix_socket'</span></span><span>])
? </span><span><span class="hljs-string">'unix_socket='</span></span><span> . </span><span><span class="hljs-variable">$cfg</span></span><span>[</span><span><span class="hljs-string">'unix_socket'</span></span><span>]
: </span><span><span class="hljs-string">'host='</span></span><span> . (</span><span><span class="hljs-variable">$cfg</span></span><span>[</span><span><span class="hljs-string">'host'</span></span><span>] ?? </span><span><span class="hljs-string">'127.0.0.1'</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-keyword">empty</span></span><span>(</span><span><span class="hljs-variable">$cfg</span></span><span>[</span><span><span class="hljs-string">'unix_socket'</span></span><span>]) && !</span><span><span class="hljs-keyword">empty</span></span><span>(</span><span><span class="hljs-variable">$cfg</span></span><span>[</span><span><span class="hljs-string">'port'</span></span><span>])) {
</span><span><span class="hljs-variable">$parts</span></span><span>[] = </span><span><span class="hljs-string">'port='</span></span><span> . (</span><span><span class="hljs-keyword">int</span></span><span>)</span><span><span class="hljs-variable">$cfg</span></span><span>[</span><span><span class="hljs-string">'port'</span></span><span>];
}
</span><span><span class="hljs-keyword">if</span></span><span> (!</span><span><span class="hljs-keyword">empty</span></span><span>(</span><span><span class="hljs-variable">$cfg</span></span><span>[</span><span><span class="hljs-string">'dbname'</span></span><span>])) {
</span><span><span class="hljs-variable">$parts</span></span><span>[] = </span><span><span class="hljs-string">'dbname='</span></span><span> . </span><span><span class="hljs-variable">$cfg</span></span><span>[</span><span><span class="hljs-string">'dbname'</span></span><span>];
}
</span><span><span class="hljs-variable">$parts</span></span><span>[] = </span><span><span class="hljs-string">'charset='</span></span><span> . (</span><span><span class="hljs-variable">$cfg</span></span><span>[</span><span><span class="hljs-string">'charset'</span></span><span>] ?? </span><span><span class="hljs-string">'utf8mb4'</span></span><span>);
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-string">'mysql:'</span></span><span> . </span><span><span class="hljs-title function_ invoke__">implode</span></span><span>(</span><span><span class="hljs-string">';'</span></span><span>, </span><span><span class="hljs-variable">$parts</span></span><span>);
}
</span><span><span class="hljs-comment">// 用法</span></span><span>
</span><span><span class="hljs-variable">$dsn</span></span><span> = </span><span><span class="hljs-title function_ invoke__">build_mysql_dsn</span></span><span>([
</span><span><span class="hljs-string">'host'</span></span><span> => </span><span><span class="hljs-string">'db.internal'</span></span><span>,
</span><span><span class="hljs-string">'port'</span></span><span> => </span><span><span class="hljs-number">3306</span></span><span>,
</span><span><span class="hljs-string">'dbname'</span></span><span> => </span><span><span class="hljs-string">'app'</span></span><span>,
</span><span><span class="hljs-string">'charset'</span></span><span> => </span><span><span class="hljs-string">'utf8mb4'</span></span><span>,
]);
</span><span><span class="hljs-comment">// new PDO($dsn, $user, $pass, $opts);</span></span><span>
</span></span>
驱动未安装:could not find driver → 检查 php -m 中是否有 pdo_mysql / pdo_pgsql 等。
主机或端口错误:SQLSTATE[HY000] [2002](MySQL)→ 校验 host/port 或 unix_socket。
数据库不存在:Unknown database 'xxx' → 修正 dbname 或先创建库。
编码问题:出现乱码 → 确认 DSN/选项中字符集一致(MySQL 用 charset=utf8mb4,Pg 设 client_encoding=UTF8)。
TLS/证书:SSL 握手失败 → 检查 CA 路径、服务端是否启用 TLS、证书匹配域名。
权限不足:permission denied 或 Access denied → 校验用户名/密码、用户在目标库的权限。
<span><span>MySQL
mysql:host=HOST;port=3306;dbname=DB;charset=utf8mb4
mysql:unix_socket=/path/mysql.sock;dbname=DB;charset=utf8mb4
PostgreSQL
pgsql:host=HOST;port=5432;dbname=DB
pgsql:host=/var/run/postgresql;dbname=DB
SQLite
sqlite:/absolute/path/to/db.sqlite
sqlite::memory:
SQL Server
sqlsrv:Server=HOST,1433;Database=DB
sqlsrv:Server=HOST\INSTANCE;Database=DB
Oracle
oci:dbname=//HOST:1521/SERVICE;charset=AL32UTF8
Db2
ibm:DRIVER={IBM DB2 ODBC DRIVER};DATABASE=DB;HOSTNAME=HOST;PORT=50000;PROTOCOL=TCPIP;
Firebird
firebird:dbname=HOST:/path/to/db.fdb;charset=UTF8
</span></span>
相关标签:
PDO