PDO закрывающее соединение

121

Просто довольно простой вопрос относительно PDO по сравнению с MySQLi.

С MySQLi, чтобы закрыть соединение, вы можете:

$this->connection->close();

Однако с PDO он заявляет, что вы открываете соединение, используя:

$this->connection = new PDO();

но чтобы закрыть установленное вами соединение null.

$this->connection = null;

Это правильно, и действительно ли это освободит соединение PDO? (Я знаю, что это так, как установлено null.) Я имею в виду, что с MySQLi вам нужно вызвать функцию ( close), чтобы закрыть соединение. PDO так же просто, как = nullотключить? Или есть функция закрытия соединения?

Лиам Сорсби
источник
11
Причина, по которой я спрашиваю, заключается в том, что я не уверен, правильно ли я закрыл соединение. но нет, не просто заинтриговал
Лиам Сорсби
2
Соединение с базой данных автоматически закрывается, когда ваш PHP-скрипт прекращает выполнение.
Мартин Бин
3
Если вы закончили использовать его, почему бы не прекратить его, особенно если после завершения взаимодействия с базой данных есть трудоемкий код. Хотя я действительно не вижу проблемы с ожиданием завершения сценария (кроме уменьшения количества подключений к серверу БД)
Киран
3
github.com/php/php-src/blob/master/ext/pdo/pdo_dbh.c Узнайте для себя, как это работает: P
Flosculus
23
Не все скрипты php недолговечны. Существуют демоны php. Я думаю, что это отличный момент, чтобы уточнить лично.
datUser

Ответы:

146

Согласно документации, вы правы ( http://php.net/manual/en/pdo.connections.php ):

Соединение остается активным в течение всего времени существования этого объекта PDO . Чтобы закрыть соединение, вам нужно уничтожить объект , убедившись, что все оставшиеся ссылки на него удалены - вы делаете это, присваивая NULL переменной, содержащей объект. Если вы не сделаете этого явно, PHP автоматически закроет соединение по завершении вашего скрипта .

Обратите внимание, что если вы инициализируете объект PDO как постоянное соединение, он не будет автоматически закрывать соединение.

Киран
источник
4
Что, если у меня есть процесс, который не заканчивается? например, websocket. Есть ли способ не использовать постоянное соединение?
Рафаэль Мони,
1
Для постоянных соединений в скрипте, который выполняется в течение длительного периода, вы можете намеренно (или случайно) отключить соединения с тайм-аутом (например, в my.ini) или по ряду других причин. При подключении или выполнении запроса перехватите любую ошибку, и если это «MySQL ушел», попробуйте подключиться снова или запустить запрос во второй раз.
Фрэнк Форте,
1
Note that if you initialise the PDO object as a persistent connection it will not automatically close the connectionНо если соединение является постоянным, и я явно вызываю на нем NULL до завершения скрипта, оно будет закрыто, даже если оно постоянное, правильно?
tonix
1
@tonix Нет, его нужно выпустить (сделать доступным для другого скрипта), но не закрывать.
Бенджамин
2
@tonix Думаю, да. Цитата из руководства PHP по постоянным соединениям : « Предупреждение. При использовании постоянных соединений следует помнить о нескольких дополнительных предостережениях. Во-первых, при использовании блокировки таблицы в постоянном соединении, если сценарий по какой-либо причине не может снять блокировку, тогда последующие сценарии, использующие то же соединение, будут блокироваться на неопределенный срок и могут потребовать перезапуска сервера httpd или сервера базы данных ».
Бенджамин
46
$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.
Химаншу Дхолакия
источник
11
IMHO Я думаю, что это очень плохой шаблон, особенно когда разработчик может хранить несколько копий справочника pdo. $ a = новый PDO (...); $ b = $ a; $ a = ноль; Там ваш объект PDO останется открытым навсегда (в программе php, подобной демону). Это особенно верно, когда ссылка PDO перемещается по функциям и свойствам объекта, и вы никогда не уверены, что обнуляете их все.
Габриэль
33
В PDO должен быть метод -> close ().
Габриэль
5
Еще одна причина не любить PDO.
Хосе Карлос PHP,
6
@Gabriel - Я полагаю, что «хранение нескольких копий» - еще худшая модель.
Рик Джеймс
4
Это не сработает, если вы создали объект PDOStatement между этими двумя строками (то есть в любой практической ситуации). Чтобы закрыть соединение, вы должны установить как объект PDO, так и объект PDOStatement в значение null. См. Здесь: php.net/manual/en/pdo.connections.php#114822
Ilmari,
8

Это больше, чем просто установка нулевого соединения. Это может быть то, что говорится в документации, но это не правда для mysql. Соединение будет оставаться немного дольше (я слышал 60-е, но никогда не тестировал)

Если вы хотите, чтобы здесь было полное объяснение, см. Этот комментарий о подключениях https://www.php.net/manual/en/pdo.connections.php#114822

Чтобы принудительно закрыть соединение, вам нужно сделать что-то вроде

$this->connection = new PDO();
$this->connection->query('KILL CONNECTION_ID()');
$this->connection = null;
Jdahern
источник
Спасибо за ваш ответ. Вопрос был давно, но вы правы по поводу связи.
Лиам Сорсби
На самом деле я не согласен с тем, что возиться с TCP-соединением через PHP - хорошая идея. Вся обработка низкоуровневого TCP-соединения абстрагируется, так что нам просто нужно иметь дело с высокоуровневым классом и объектами во время выполнения. PHP - это язык, основанный на запросах (как вы, вероятно, знаете), поэтому отключение потенциально постоянного соединения с dB, вероятно, приведет к неожиданным ошибкам / проблемам для пользователей. Вариант использования, на который вы ссылаетесь, вероятно, является результатом того, что драйвер сохраняет постоянное соединение открытым для использования другим запросом, поэтому я подумал, что это ожидаемое поведение.
Лиам Сорсби
Если вы действительно посмотрите на список процессов в mysql, он покажет, что соединение все еще существует. Я согласен, что вы не должны так возиться с TCP-соединением, и должен быть способ правильно отключиться от соединения. Но это не тот случай. Итак, если вы действительно хотите отключиться от сервера, вам придется сделать что-то вроде этого. Установка соединения на null не отключает соединение, противоречащее тому, что говорится в документации.
Jdahern
Я нашел это объяснение: stackoverflow.com/a/18277327/1315873
Fil
7

Я создал производный класс, чтобы иметь более самодокументирующуюся инструкцию вместо «$ 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;
    }
}

Так что я могу вызвать свой код между:

$conn=CMyPDO::getNewConnection();
// my code
CMyPDO::closeConnection($conn);
Fil
источник
1
Вы можете сделать метод CMyPDO :: __ construct () закрытым и использовать там одноэлементный шаблон ..
Адитья Хаджаре,
Да, это возможно. Вам также необходимо назначить информацию о соединении другим методом, если вы одновременно используете несколько баз данных. Разница минимальна, просто у вас есть немного более длинная инструкция для вызова методов экземпляра.
Fil
@AdityaHajare Вы не можете сделать общедоступный метод суперкласса частным в подклассе ..
nickdnk
@nickdnk, ты прав. Я имел в виду создать автономный класс CMyPDO (без расширения PDO), а затем создать экземпляр базы данных внутри частного конструктора CMyPDO (новый класс PDO ($ dsn, $ dbuser, $ dbpass);), убедившись, что только один Экземпляр доступен во всем приложении (Singleton Design Pattern).
Адитья Хаджаре
1
@Fil Но код "снаружи" closeConnectionне должен знать, что ему нужно скопировать ссылку на переменную вместо присвоения объекта. Другими словами, ваш способ попробовать кодировать функцию close PDO имеет плохие побочные эффекты, что делает его ненадежным. Единственный способ сделать это - closeConnectionпроверить, сколько ссылок на объект PDO существует в коде, и бросить в случае, если существует более 1.
Xenos
-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

Полный пример с пользовательским классом PDO2.

йо ло
источник
1
Удалите try catch из своего кода или добавьте внутри новый throw, как показано здесь . Прямо сейчас ваш код злоупотребляет как исключениями, так и сообщением об ошибках в целом
Your Common Sense