PHP Velho Oeste 2024

连接与连接管理

连接是通过创建 PDO 基类的实例而建立的。使用哪种驱动程序并不重要,始终都会用 PDO 类名。构造函数接受用于指定数据库源(也称为 DSN)以及可选的用户名和密码(如果有)的参数。

示例 #1 连接到 MySQL

<?php
$dbh
= new PDO('mysql:host=localhost;dbname=test', $user, $pass);
?>

如果有任何连接错误,将抛出 PDOException 对象。如果想处理错误状态,可以捕获异常,或者选择将其留给 set_exception_handler() 设置的应用程序全局异常处理程序。

示例 #2 处理连接错误

<?php
try {
$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
} catch (
PDOException $e) {
// 对于示例,尝试在超时后重新连接
}

警告

就像其它任一 exception 一样,PDOException 可以通过 catch 语句手动捕获,也可以通过 set_exception_handler() 自动捕获。否则,默认行为是将未捕获的异常转换为 E_FATAL_ERROR。fatal 错误可能会包含泄漏连接详情的 backtrace。因此,生产服务器上的 php.ini 选项 display_errors 应设置为 0

连接数据库成功后,返回 PDO 类的实例给脚本。此连接在 PDO 对象的生存周期中保持有效状态。要关闭连接,需要确保删除它的所有剩余引用来销毁对象——可以通过对对象变量赋值 null 来实现。如果没有明确这么做,PHP 在脚本结束时会自动关闭连接。

注意: 如果还有其它对此 PDO 实例的引用(比如来自 PDOStatement 实例,或来自其它同一 PDO 实例的其它变量),也必须删除这些引用(例如,通过将 null 赋值给引用 PDOStatement 的变量)。

示例 #3 关闭连接

<?php
$dbh
= new PDO('mysql:host=localhost;dbname=test', $user, $pass);
// 在此使用连接
$sth = $dbh->query('SELECT * FROM foo');

// 使用完毕,关闭连接
$sth = null;
$dbh = null;
?>

很多 web 应用程序通过与数据库建立持久连接获得好处。持久连接不会在脚本结束时关闭,而是会缓存,且当另一个脚本使用相同凭证请求连接时重用。持久连接缓存可以避免每次脚本需要与数据库通信时建立新连接的开销,从而让 web 应用程序更快。

示例 #4 持久化连接

<?php
$dbh
= new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(
PDO::ATTR_PERSISTENT => true
));
?>

PDO::ATTR_PERSISTENT 选项的值转换为 bool(启用/禁用持久连接),除非它不是数字 string,在这种情况下允许使用多个持久连接池。如果不同的链接使用不兼容的设置,非常有用,例如 PDO::MYSQL_ATTR_USE_BUFFERED_QUERY 的值不同。

注意:

如果想使用持久连接,必须在传递给 PDO 构造函数的驱动程序选项数组中设置 PDO::ATTR_PERSISTENT。如果在对象实例化后用 PDO::setAttribute() 设置此属性,驱动程序将不会使用持久连接。

注意:

如果使用 PDO ODBC 驱动程序且 ODBC 库支持 ODBC 连接池(有 unixODBC 和 Windows 是其中的两个;可能会有更多),建议不要使用持久 PDO 连接,而是把连接缓存留给 ODBC 连接池层。ODBC 连接池在进程中与其它模块共享;如果 PDO 缓存连接,则此连接永远不会被返回到 ODBC 连接池,从而导致创建额外的连接来服务其它模块。

add a note add a note

User Contributed Notes 13 notes

up
178
cappytoi at yahoo dot com
10 years ago
Using PHP 5.4.26, pdo_pgsql with libpg 9.2.8 (self compiled). As usual PHP never explains some critical stuff in documentation. You shouldn't expect that your connection is closed when you set $dbh = null unless all you do is just instantiating PDO class. Try following:

<?php
$pdo
= new PDO('pgsql:host=192.168.137.1;port=5432;dbname=anydb', 'anyuser', 'pw');
sleep(5);
$stmt = $pdo->prepare('SELECT * FROM sometable');
$stmt->execute();
$pdo = null;
sleep(60);
?>

Now check your database. And what a surprise! Your connection hangs for another 60 seconds. Now that might be expectable because you haven't cleared the resultset.

<?php
$pdo
= new PDO('pgsql:host=192.168.137.160;port=5432;dbname=platin', 'cappytoi', '1111');
sleep(5);
$stmt = $pdo->prepare('SELECT * FROM admin');
$stmt->execute();
$stmt->closeCursor();
$pdo = null;
sleep(60);
?>

What teh heck you say at this point? Still same? Here is what you need to do to close that connection:

<?php
$pdo
= new PDO('pgsql:host=192.168.137.160;port=5432;dbname=platin', 'cappytoi', '1111');
sleep(5);
$stmt = $pdo->prepare('SELECT * FROM admin');
$stmt->execute();
$stmt->closeCursor(); // this is not even required
$stmt = null; // doing this is mandatory for connection to get closed
$pdo = null;
sleep(60);
?>

PDO is just one of a kind because it saves you to depend on 3rd party abstraction layers. But it becomes annoying to see there is no implementation of a "disconnect" method even though there is a request for it for 2 years. Developers underestimate the requirement of such a method. First of all, doing $stmt = null  everywhere is annoying and what is most annoying is you cannot forcibly disconnect even when you set $pdo = null. It might get cleared on script's termination but this is not always possible because script termination may delayed due to slow client connection etc.

Anyway here is how to disconnect forcibly using postgresql:

<?php
$pdo
= new PDO('pgsql:host=192.168.137.160;port=5432;dbname=platin', 'cappytoi', '1111');
sleep(5);
$stmt = $pdo->prepare('SELECT * FROM admin');
$stmt->execute();
$pdo->query('SELECT pg_terminate_backend(pg_backend_pid());');
$pdo = null;
sleep(60);
?>

Following may be used for MYSQL: (not guaranteed)
KILL CONNECTION_ID()
up
9
d dot bergloev at gmail dot com
7 years ago
I would please advice people who talk about database port in reference with socket files to please read up about what a socket file is. TCP/IP uses ports, a socket file however is a direct pipe line to your database. So no, you should not replace localhost with local ip if you use a different port on your database server, because the socket file has nothing to do with your TCP/IP setup. And whenever possible, using the local socket file is much faster than establishing new TCP/IP connections on each request which is only meant for remote database servers.
up
17
jak dot spalding at gmail dot com
13 years ago
Just thought I'd add in and give an explanation as to why you need to use 127.0.0.1 if you have a different port number.

The mysql libraries will automatically use Unix sockets if the host of "localhost" is used. To force TCP/IP you need to set an IP address.
up
11
ogierschelvis at gmail dot com
8 years ago
As http://stackoverflow.com/questions/17630772/pdo-cannot-connect-remote-mysql-server points out; sometimes when you want to connect to an external server like this:

<?php
$conn
= new PDO('mysql:host=123.4.5.6;dbname=test_db;port=3306','username','password');
?>

it will fail no matter what. However if you put a space between mysql: and host like this:

<?php
$conn
= new PDO('mysql: host=123.4.5.6;dbname=test_db;port=3306','username','password');
?>

it will magically work. I'm not sure if this applies in all cases or server setups. But I think it's worth mentioning in the docs.
up
3
Moshe Dolev
5 years ago
Please note that you cannot use persistent connections to create temporary tables in mysql/mariadb.
Tables you create using a statement like "create temporary table TABLE1 ..." are destroyed only when the mysql session ends (not php session !). This never happens if you use a persistent connection.
If you create a temporary table on a persistent connection, the table will live even after the php script ends. The next php script that will try to issue the same create temporary table statement, will receive an error.
IMHO, this fact makes persistent connections quite useless.
up
16
neville at whitespacers dot com
14 years ago
To avoid exposing your connection details should you fail to remember to catch any exception thrown by the PDO constructor you can use the following class to implicitly change the exception handler temporarily.

<?php

Class SafePDO extends PDO {

        public static function
exception_handler($exception) {
           
// Output the exception details
           
die('Uncaught exception: ', $exception->getMessage());
        }

        public function
__construct($dsn, $username='', $password='', $driver_options=array()) {

           
// Temporarily change the PHP exception handler while we . . .
           
set_exception_handler(array(__CLASS__, 'exception_handler'));

           
// . . . create a PDO object
           
parent::__construct($dsn, $username, $password, $driver_options);

           
// Change the exception handler back to whatever it was before
           
restore_exception_handler();
        }

}

// Connect to the database with defined constants
$dbh = new SafePDO(PDO_DSN, PDO_USER, PDO_PASSWORD);

?>
up
9
dan dot franklin at pearson dot com
16 years ago
Note that you can specify a port number with "port=####", but this port number will be ignored if the host is localhost.  If you want to connect to a local port other than the default, use host=127.0.0.1 instead of localhost.
up
5
edsanhu at gmail dot com
8 years ago
For being able to retrieve information from the db in utf-8 the connection assignment has to add to the dsn `charset=utf8`:

<?php
$dbh
= new PDO('mysql:host=localhost;dbname=test;charset=utf8', $user, $pass);
?>
up
0
antony at harrisretail dot co dot uk
4 years ago
It's not possible to use a persistent connection and to extend the PDOStatement class to add methods to the standard class. This means that you cannot do:

<?php
  $dbh
= new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(PDO::ATTR_PERSISTENT => true));
 
$dbh->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('MyPDOStatement', array($this)));
?>

This results in an error:

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error: PDO::ATTR_STATEMENT_CLASS cannot be used with persistent PDO instances
up
1
thz at plista dot com
10 years ago
If you are using PHP 5.4 and later, you can no longer use persistent connections when you have your own database class that derives from the native PDO object. If you do, you will get segmentation faults during the PHP process shutdown.

Please see this bug report for more information: https://bugs.php.net/bug.php?id=63176
up
-3
me+nospam at tati dot pro
7 years ago
If you want to keep connection after fork exit, you can kill with SIGKILL forked process.

<?php
$dbh
= new PDO('pgsql:host=localhost;dbname=test', $user, $pass);
$pid = pcntl_fork();
if(
$pid == 0){
       
// forked process 'll exit immediately
       
exit;
}
sleep(1);
$statement = $dbh->query('select 1');
var_dump($statement);
?>
Result: false

<?php
$dbh
= new PDO('pgsql:host=localhost;dbname=test', $user, $pass);
$pid = pcntl_fork();
if(
$pid == 0){
       
// use sigkill to close process
       
register_shutdown_function(function(){
               
posix_kill(getmypid(), SIGKILL);
        });
       
// forked process 'll exit immediately
       
exit;
}
sleep(1);
$statement = $dbh->query('select 1');
var_dump($statement);
?>
Result: object(PDOStatement)#3 (1) {
  ["queryString"]=>
  string(8) "select 1"
}
up
-5
alvaro at demogracia dot com
12 years ago
On connection errors, the PDO constructor seems to do two things no matter your PDO::ATTR_ERRMODE setting:

1. Trigger a warning
2. Throw a PDOException

If you set the PDO::ATTR_ERRMODE parameter, it will only take effect on further operations.
up
-63
paulo dot sistema at gmail dot com
7 years ago
Hello guys!
Has anyone used the ORACLE WALLET feature in PHP or Java?

https://docs.oracle.com/middleware/1213/wls/JDBCA/oraclewallet.htm#JDBCA596

I would like to know how to implement it because I can not implement. We use PDO + PHP in all applications and now there is this demand of the DBA.

Thank you
To Top