====== PHP RFC: Add PDO disconnect() and isConnected() ====== * Version: 1.0 * Date: 2025-09-29 * Author: Robert Wolf, rposky@gmail.com * Status: Under Discussion * Implementation: Pending ===== Introduction ===== Adds //PDO::disconnect()// to disconnect from an established database connection and //PDO::isConnected()// to query connection status. isConnected()); $pdo->disconnect(); assert(!$pdo->isConnected()); ?> ===== Proposal ===== In invoking //PDO::disconnect(),// the intended effect is to immediately close connection to the database. //PDO::isConnected()// will reflect whether a connection has been closed via call to //disconnect()// or otherwise, by the remote end. Connection state is tracked through a pre-existing sense variable. Subsequent interactions with a disconnected PDO object that would interact with the database are expected to fail. This failure state is integrated with PDO error mode and the error interfaces //errorCode()// and //errorInfo()// using the standard //SQLSTATE// error code //"01002 - Disconnect error".// Silent or warning error execution modes will prompt the pre-existing //FALSE//-y return value of these same PDO interfaces. Some interfaces may still be successfully invoked on a disconnected PDO object, namely //disconnect(), isConnected(), getAttribute(), setAttribute(), errorCode(), and errorInfo().// Any prior error state will be cleared upon invocation of //disconnect()//, allowing for error reporting of //disconnect()// itself. Due to implementation constraint, the attribute interfaces //getAttribute()// and //setAttribute()// are degraded for disconnected PDO objects, permitting interaction with PDO native attributes only and yielding an implementation error (or warning, depending on error mode) if interacting with driver-specific attributes. The error text for this is as follows: //"driver attributes not initialized, possibly due to disconnect".// Upon persistent PDO object creation, if a connection was previously closed by a call to //disconnect(),// it will be re-established. This is aligned with current behavior, which replaces the persistent database connection if the connection incurs a "liveness" check failure during instantiation. Note that this connection replacement only occurs for the newly-created PDO object and does not extend to other PDO objects referencing the same persistent connection. Persistent PDO objects which share a database connection will all be disconnected when //disconnect()// is invoked on any individual PDO object, albeit maintaining multiple PDO objects which reference the same database connection has long been discouraged. ==== Examples ==== Disconnect error example: setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); $pdo->disconnect(); // produces "Warning: PDO::beginTransaction(): SQLSTATE[01002]: Disconnect error in %s on line %d" $pdo->query('SELECT * FROM DB'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // produces PDOException with message "SQLSTATE[01002]: Disconnect error" $pdo->query('SELECT * FROM DB'); ?> Persistent connect example: true]); // connected $pdo->disconnect(); // Request 2 $pdo = new PDO($dsn, $user, $pass, [PDO::ATTR_PERSISTENT => true]); // connected ?> ===== Motivation ===== It is assumed that a user has reasonable need to close a database connection prior to and possibly outside script termination. Some possible rationale for doing so are given below. * Efficient management of database resources across periods of application idleness, opening and closing connection as needed. * Interoperability with database environment imposing timeout or other session constraints, necessitating multiple connections during script execution. * Abnormal termination procedure in which database closure is highly prioritized, e.g. security violation. * Testing purposes, e.g. simulation of remote connection failure. Currently, a database connection can only be closed by the client through PDO object destruct or, in the case of persistent connections, it is closed at the end of script execution. In order to effectively disconnect and/or replace a database connection, PDO implementation has thus imposed the need to manage PDO object references, as all must be released in order to invoke the object destructor. Furthermore, there can be no verification of database connection closure, given all PDO references have necessarily been released to achieve the closed state. A user must trust in their implementation without means to verify. Given the above constraints, a common integration pattern is to decorate the PDO class in order to better ensure that only a single reference is held by the application. It is the community solution to the pursuant complexity problem of managing PDO object references, as evidenced by this related [[https://marc.info/?l=php-internals&m=145340411014969&w=2|php-internals exchange]]. PHP makes the implementation of a decorator simple enough through magic methods like //%%__call%%// and //%%__get,%%// but this is complicated in PDO by the PDO statement object, which holds reference to a PDO object, and so must also be reference-managed. A leak-proof decorator should thus override //PDO::prepare()// and //PDO::query()// to also prevent the leak of the PDO statement object, likely requiring additional interfaces to otherwise interact with prepared PDO statements, to e.g. bind parameters and execute them, and queried PDO statements, to e.g. fetch one row, N rows, one column, etc. This prescribed implementation pattern is burdensome. For persistent PDO objects, the user has no native capacity to close a connection, given that the database handle is inaccessible and registered as a PHP persistent resource, which remains until the PHP process terminates. To ensure database connectivity across subsequent PHP requests, each new PDO object includes a liveness check on the persistent connection, possibly prompting a new connection. This has meant the burden of defining a viable database connection resides within the database driver's liveness check, and that an outcome differing with actual viability as it concerns a given application results in a non-viable database connection for all subsequent PHP requests. The proposed //disconnect()// thus grants the user the capacity to declare a connection as no-longer viable. ===== Backward Incompatible Changes ===== This proposal aims for backwards compatibility, and likely no changes will be needed upon upgrade. There is no requirement that callers adopt //disconnect()// to close a connection, nor need callers query //isConnected(),// and the current destructor-based disconnect pathway remains supported. Although there is novel introduction of the //SQLSTATE// error //"01002 - Disconnect error"// to various PDO interfaces, and which can be encountered even if //disconnect()// has not been explicitly invoked, i.e. through database driver detection of remote connection closure, the added error conforms to pre-existing PDO error mechanics. PDO implementations presumably already by-and-large include error handling, at least where continued database connectivity has not been guaranteed. The reviewer should bear in mind that this proposal does not introduce the disconnected state to an established PDO connection, which has necessarily always accompanied PDO, but rather the means for the user to achieve it through a held PDO object. ===== Proposed PHP Version(s) ===== next PHP 8.x ===== RFC Impact ===== ==== To the Ecosystem ==== No known impact. ==== To Existing Extensions ==== This proposal targets the common PDO extension and does not require rework to dependent PDO driver extensions. A minor unit test change is necessary within the MySQL PDO extension, due to the additive change to the PHP PDO interface. ==== To SAPIs ==== No known impact. ===== Open Issues ===== None ===== Future Scope ===== The capability to reconnect a disconnected PDO may be desirable to have in the future, but there are several hindrances to providing such functionality: the capture of database credentials and DSN to persist beyond the PDO constructor, the separation of connectivity implementation from the constructor, and the separation of driver implementation for connection closure from the freeing of allocated memory structures, presently combined. Rework along the latter lines would also permit continued interaction with the underlying driver implementation past the point of disconnect(). A wrinkle in PDO implementation referred to above is the PDO statement object and its held reference to the PDO object that created it. When freeing a PDO object, an associated PDO statement object is also necessarily freed, notwithstanding that the underlying database connection may remain open, i.e. is persistent. A worthwhile refactoring effort could seek to remove both the PDO object's held reference to the last-created PDO statement object as well as the PDO statement's held reference to the PDO object which created it. In the case of PDO, the reference to a PDO statement appears to exist for error reporting purposes, such that a path to removal may exist in copying error state from the statement to the database handle at the time of error occurrence. In the case of PDO statement, the reference to PDO appears to exist for GC purposes, preventing the freeing of an associated PDO object and subsequent disconnect of a database handle while the statement remains, the necessity of which is reduced by this proposal separating PDO destruct implementation from database disconnect, though some additional work around PDO and PDO statement lifecycle would be required to ensure proper handling of the database handle. ===== Voting Choices ===== Pending ===== Patches and Tests ===== https://github.com/php/php-src/pull/19933 ===== Implementation ===== Pending ===== References ===== https://bugs.php.net/bug.php?id=40681 https://bugs.php.net/bug.php?id=62065 https://bugs.php.net/bug.php?id=67473 https://marc.info/?l=php-internals&m=145338688709155&w=2 ===== Rejected Features ===== Pending ===== Changelog ===== Pending