155

Just a rather simple question with regards to PDO compared to MySQLi.

With MySQLi, to close the connection you could do:

$this->connection->close();

However with PDO it states you open the connection using:

$this->connection = new PDO();

but to close the connection you set it to null.

$this->connection = null;

Is this correct and will this actually free the PDO connection? (I know it does as it is set to null.) I mean with MySQLi you have to call a function (close) to close the connection. Is PDO as easy as = null to disconnect? Or is there a function to close the connection?

Nisse Engström
  • 4,738
  • 23
  • 27
  • 42
Liam Sorsby
  • 2,912
  • 3
  • 28
  • 51
  • 11
    the reason i am asking is i'm not sure if i was closing the connection properly. but no not really just intrigued – Liam Sorsby Aug 16 '13 at 15:45
  • Do you really need to close your connection? – Your Common Sense Aug 16 '13 at 15:46
  • How long is that "longer than necessary"? – Your Common Sense Aug 16 '13 at 15:48
  • 3
    The database connection is automatically closed when your PHP script stops execution. – Martin Bean Aug 16 '13 at 15:48
  • i suppose your right anyway as it would be at the start and the end of the script but the script will destroy the database connection at the end of the script anyway. sorry about that. common sence finally came back – Liam Sorsby Aug 16 '13 at 15:49
  • Internal `destructor` – Flosculus Aug 16 '13 at 15:49
  • in that case then what is the best way? to set to null, use destructor or let the script finish executing and let the script end the connection? – Liam Sorsby Aug 16 '13 at 15:50
  • 4
    If you're done using it then why not go ahead and terminate it, especially if there is some time consuming code once you've finished interacting with the database. Though, I don't really see the issue with waiting for the script to finish either though (other than reducing connections to the DB server.) – Kieran Aug 16 '13 at 15:57
  • 3
    https://github.com/php/php-src/blob/master/ext/pdo/pdo_dbh.c Find out for yourself how it works :P – Flosculus Aug 16 '13 at 15:58
  • 34
    Not all php scripts are short lived. There are php daemons out there. I think this is a great thing to clarify personally. – datUser Oct 17 '14 at 21:12

6 Answers6

173

According to documentation you're correct (http://php.net/manual/en/pdo.connections.php):

The connection remains active for the lifetime of that PDO object. To close the connection, you need to destroy the object by ensuring that all remaining references to it are deleted--you do this by assigning NULL to the variable that holds the object. If you don't do this explicitly, PHP will automatically close the connection when your script ends.

Note that if you initialise the PDO object as a persistent connection it will not automatically close the connection.

Kieran
  • 2,200
  • 1
  • 15
  • 17
  • 5
    What if I have a process that doesn't end? e.g. websocket. Is there a way to not use persistent connection? – Rafael Moni Dec 22 '15 at 12:49
  • 1
    For persistent connections in a script that runs for a long period you can purposely (or accidentally) have connections killed with a timeout (e.g. in my.ini), or for a number of other reasons. When connecting or running a query, catch any error, and if it is "MySQL has gone away", attempt to connect again or run the query a second time. – Frank Forte Dec 14 '17 at 20:50
  • 1
    `Note that if you initialise the PDO object as a persistent connection it will not automatically close the connection` But if a connection is persistent and I explicitly call NULL on it before the script ends, it will be closed even if it is persistent, correct? – tonix Nov 23 '18 at 16:48
  • 1
    @tonix No, it should be released (made available to another script), but not closed. – BenMorel Dec 02 '18 at 22:32
  • @Benjamin OK, therefore if there a table was locked and there was an error and the script didn't release the lock, even if the script called `$pdo = NULL` before shutdown, another script which will receive the same PDO connection will not be able to use it properly, right? – tonix Dec 03 '18 at 11:43
  • 2
    @tonix I think so, yes. Quote from the [PHP manual on persistent connections](http://se2.php.net/manual/en/features.persistent-connections.php): *"**Warning** There are a couple of additional caveats to keep in mind when using persistent connections. One is that when using table locking on a persistent connection, if the script for whatever reason cannot release the lock, then subsequent scripts using the same connection will block indefinitely and may require that you either restart the httpd server or the database server."* – BenMorel Dec 03 '18 at 12:46
  • Thank you for the clarification, they should really also write `... restart the httpd server or the database server, EVEN if you unset the PDO object with $pdo = NULL;` – tonix Dec 03 '18 at 18:25
  • The PHP manual is absolute rubbish on this point: "you do this by assigning NULL to the variable that holds the object" - this is nonsense. There is absolutely no difference between explicitly setting a variable or property to `null` as compared to merely letting the variable (or object containing the property) fall out of scope. Note though that garbage-collection in PHP is memory-based, so if you open and close a lot of connections, you may need to explicitly call `gc_collect_cycles()`, e.g. in large test-suites, at the end of every test. (setting vars/props to null won't help, at all.) – mindplay.dk Mar 28 '19 at 10:32
  • From what you write `unset($this->connection);` is enough? – DDS Jan 17 '22 at 13:48
60
$conn=new PDO("mysql:host=$host;dbname=$dbname",$user,$pass);
    // If this is your connection then you have to assign null
    // to your connection variable as follows:
$conn=null;
    // By this way you can close connection in PDO.
Nisse Engström
  • 4,738
  • 23
  • 27
  • 42
  • 13
    IMHO I think it is a very bad pattern, especially when a developer might store several copies of the pdo reference. $a = new PDO(...); $b = $a; $a = null; There, your PDO object will remain open forever (in a daemon-like php program). This is especially true when the PDO reference travels across functions and object properties, and you're never sure to null out all of them. – Gabriel Jan 21 '16 at 09:35
  • 55
    There should be a ->close() method on PDO. – Gabriel Jan 21 '16 at 09:36
  • 6
    Another reason to dislike PDO. – José Carlos PHP Jul 13 '16 at 10:10
  • 12
    @Gabriel - I suggest that the "storing several copies" is an even worse pattern. – Rick James Apr 01 '17 at 20:43
  • @RickJames - Not always possible. Situations of implicit reference reservation like: function() uses ($pdo) {...} can happen. Read my study for more details https://www.phparch.com/magazine/2016-2/july/ – Gabriel Apr 03 '17 at 08:00
  • @Gabriel - Passing a copy to a function is not really a problem -- the function cleans up as it exits. – Rick James Apr 03 '17 at 16:19
  • 1
    @Rick — :-) read my paper. A closure is not a function, it's a structure that holds memory. Declaring this example closure at the beginning of your program, will hold the `use`d variable beyond the end of the closure's execution — until the closure is destroyed. In a dependency injection context, you might not be able to release the resource as early as you wished. – Gabriel Apr 04 '17 at 17:26
  • 5
    This does not work if you have created a PDOStatement object between those two rows (that is, in every practical situation). To close the connection, you have to set both the PDO object AND the PDOStatement object to null. See here: http://php.net/manual/en/pdo.connections.php#114822 – Ilmari May 12 '17 at 11:12
  • 3
    And we are in 2018 and I'm getting trouble in PDO close connection, because a "smart guy" of PHP core dev have a brilliant idea that ->close() method to PDO is a "bad idea" :D – Ragen Dazs Aug 25 '18 at 12:01
23

Its more than just setting the connection to null. That may be what the documentation says, but that is not the truth for mysql. The connection will stay around for a bit longer (Ive heard 60s, but never tested it)

If you want to here the full explanation see this comment on the connections https://www.php.net/manual/en/pdo.connections.php#114822

To force the close the connection you have to do something like

$this->connection = new PDO();
$this->connection->query('KILL CONNECTION_ID()');
$this->connection = null;
Jdahern
  • 1,196
  • 9
  • 14
  • Thank you for your answer. The question was from quite a while ago but your right about the connection. – Liam Sorsby Aug 15 '19 at 18:39
  • I don’t actually agree that messing with The TCP connection via PHP is a good idea. All the low level TCP connection handling are abstracted away so that we just have to deal with the high level class and objects during runtime. PHP is a request based language (as you probably know) so killing a potentially persistent connection to the dB will likely result in unexpected errors/issues for users. The use case you link to is likely the result in the driver keeping the persistent connection open to be utilised by another request so I would have thought this would be expected behaviour. – Liam Sorsby Aug 15 '19 at 18:44
  • 1
    If you actually look at the processes list in mysql, it will show the connection still there. I agree you should not be messing with the TCP connection like this, and there should be a way to disconnect from the connection properly. But that is not the case. So, if you really want to disconnect from the server, your going to have to do something like this. Setting the connection to null does not disconnect the connection contraty to what the docs say. – Jdahern Aug 19 '19 at 16:37
  • I've found this explaination: https://stackoverflow.com/a/18277327/1315873 – Fil Apr 25 '20 at 22:00
  • PHP 8.0 **SQLite 3.32.1** `no such function: CONNECTION_ID` – a55 Feb 15 '21 at 13:26
  • 1
    @a55 This answer is MySQL specific. From my understanding of SQLite, it creates a file level connection. You would have to look at your file descriptors in your OS to see if you would have to run a query. – Jdahern Feb 17 '21 at 05:01
8

I created a derived class to have a more self-documented instruction instead of $conn=null;.

class CMyPDO extends PDO {
    public function __construct($dsn, $username = null, $password = null, array $options = null) {
        parent::__construct($dsn, $username, $password, $options);
    }

    static function getNewConnection() {
        $conn=null;
        try {
            $conn = new CMyPDO("mysql:host=$host;dbname=$dbname",$user,$pass);
        }
        catch (PDOException $exc) {
            echo $exc->getMessage();
        }
        return $conn;
    }

    static function closeConnection(&$conn) {
        $conn=null;
    }
}

So I can call my code between:

$conn=CMyPDO::getNewConnection();
// my code
CMyPDO::closeConnection($conn);
TuralAsgar
  • 1,275
  • 2
  • 13
  • 26
Fil
  • 1,032
  • 13
  • 29
  • 1
    You can make CMyPDO::__construct() method private and use singleton pattern there.. – Aditya Hajare Oct 23 '17 at 04:27
  • Yes, it's possibile. You also need to assign connection information by another method if you use more than one database at a time. The difference is minimal, just you have little longer instruction to call instance methods. – Fil Oct 24 '17 at 08:28
  • @AdityaHajare You cannot make a public method of a superclass private in a subclass.. – nickdnk Nov 09 '17 at 22:54
  • @nickdnk, you are right. What I meant was to create a standalone class CMyPDO (without making it extend PDO) and then creating an instance of database inside a private constructor of CMyPDO (new PDO($dsn, $dbuser, $dbpass);) class making sure only one instance is available throughout the application (Singleton Design Pattern). – Aditya Hajare Nov 10 '17 at 02:35
  • I don't think connection would close if you do `$x = CMyPDO::getnewConnection(); $y = $x; CMyPDO::closeConnection($x);` as your static method has no way (besides reflection maybe and such) to know if other references to `$conn` exist – Xenos Jan 15 '19 at 12:38
  • @Xenos: I think you are right. The same thing happens with `$x=new PDO(...); $y=$x; $x=null;`. The right operator to use is `$y=&$x;` (I've never need to do this anyway). It takes a little discipline to use connection. :P – Fil Jan 15 '19 at 17:25
  • 1
    @Fil But the code "outside" `closeConnection` should not be aware that it needs to copy reference to the variable instead of assigning the object. In other words, your way to try coding a close PDO function have bad side effects, making it unreliable. The only way to do so would be for `closeConnection` to check how many references to the PDO object exist in the code, and throw in case more than 1 exists. – Xenos Jan 15 '19 at 19:50
  • @Xenos: I agree with you. I only know I may use a php extension (not possible in all web hostings) to override the assignment operator and always return a reference (it's possible?) or disable the operator (throwing an exception?). The other alternative a know is to use `ob_start...ob_end_flush` to capture the output of `debug_zval_dump` (of entire class CMyPDO) in `closeConnection` as you suggest. I'd prefer this second options since it runs in every hosting. Thanks for your interesting observations. – Fil Jan 16 '19 at 14:45
  • References for [https://stackoverflow.com/a/15953809/1315873] (php extension for assignment overloading) and for [https://stackoverflow.com/a/3764809/1315873] (reference counting without php extension). In the previous comment I meant `ob_start..ob_get_clean` :P – Fil Jan 16 '19 at 14:50
0

You can also close it by destroying the PDO object with:

unset($this -> connection);
OMA
  • 3,442
  • 2
  • 32
  • 39
-1
<?php if(!class_exists('PDO2')) {
    class PDO2 {
        private static $_instance;
        public static function getInstance() {
            if (!isset(self::$_instance)) {
                try {
                    self::$_instance = new PDO(
                        'mysql:host=***;dbname=***',
                        '***',
                        '***',
                        array(
                            PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_general_ci",
                            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION
                        )
                    );
                } catch (PDOException $e) {
                    throw new PDOException($e->getMessage(), (int) $e->getCode());
                }
            }
            return self::$_instance;
        }
        public static function closeInstance() {
            return self::$_instance = null;
        }
    }
}
$req = PDO2::getInstance()->prepare('SELECT * FROM table');
$req->execute();
$count = $req->rowCount();
$results = $req->fetchAll(PDO::FETCH_ASSOC);
$req->closeCursor();
// Do other requests maybe
// And close connection
PDO2::closeInstance();
// print output

Full example, with custom class PDO2.

yo lo
  • 59
  • 1
  • 3
  • 1
    Please either remove try catch from your code or add a new throw inside as shown [here](https://phpdelusions.net/pdo_examples/connect_to_mysql#example). Right now your code abuses both exceptions and error reporting in general – Your Common Sense Feb 13 '20 at 08:53