З'єднання для закриття PDO


120

Просто досить просте запитання щодо 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 = null; Там ваш об'єкт PDO назавжди залишиться відкритим (у програмі, що нагадує демон, php). Особливо це стосується, коли посилання PDO подорожує через функції та властивості об'єкта, і ви ніколи не будете впевнені, що зведете їх нанівець.
Габріель

33
На PDO повинен бути метод -> close ().
Габріель

5
Ще одна причина неприязнь PDO.
Хосе Карлос PHP

6
@Gabriel - Я вважаю, що "зберігання кількох копій" - це ще гірший зразок.
Рік Джеймс

4
Це не працює, якщо ви створили об’єкт PDOStatement між цими двома рядками (тобто в кожній практичній ситуації). Щоб закрити з'єднання, потрібно встановити як об'єкт PDO, так і об'єкт PDOStatement на нуль. Дивіться тут: php.net/manual/en/pdo.connections.php#114822
Ілмарі

8

Це більше, ніж просто встановлення з'єднання на null. Про це може сказати документація, але це не правда для 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;

Спасибі за вашу відповідь. Питання було досить давно, але ваше право щодо зв'язку.
Ліам Сорсбі

Я фактично не згоден, що возитися з TCP-з'єднанням через PHP - це гарна ідея. Всі низькорівневі TCP-з'єднання обробляються, тому нам просто доводиться мати справу з класом та об'єктами високого рівня під час виконання. PHP - це мова, що базується на запитах (як ви, мабуть, знаєте), тому вбивство потенційно стійкого з'єднання з dB може призвести до несподіваних помилок / проблем для користувачів. Випадок використання, на який ви посилаєтесь, швидше за все, призведе до того, що драйвер буде підтримувати постійне з'єднання відкритим, щоб використовувати його іншим запитом, тому я би подумав, що це буде очікуваною поведінкою.
Ліам Сорсбі

Якщо ви дійсно подивитесь на список процесів у mysql, він покаже з'єднання все ще там. Я погоджуюся, що ви не повинні возитися з TCP-з'єднанням, як це, і повинен бути спосіб від’єднатись від з'єднання належним чином. Але це не так. Отже, якщо ви дійсно хочете відключитися від сервера, вам доведеться зробити щось подібне. Встановлення з'єднання в нульове значення не відключає протокол з'єднання з тим, що кажуть документи.
Jdahern

Я знайшов це пояснення: stackoverflow.com/a/18277327/1315873
Філ

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);

1
Ви можете зробити CMyPDO :: __ метод construct () приватним і використовувати одинарний візерунок там ..
Aditya Hajare

Так, це можливо. Також потрібно призначити інформацію про з'єднання іншим методом, якщо ви використовуєте більше однієї бази даних одночасно. Різниця мінімальна, просто у вас є трохи довші інструкції викликати методи екземпляра.
Філ

@AdityaHajare Ви не можете зробити публічний метод приватного суперкласу в підкласі ..
nickdnk

@nickdnk, ти маєш рацію. Я мав на увазі створити окремий клас CMyPDO (не змушуючи його розширювати PDO), а потім створити екземпляр бази даних всередині приватного конструктора CMyPDO (новий PDO ($ dsn, $ dbuser, $ dbpass);), переконуючись лише у одному екземпляр доступний у всій програмі (Singleton Design Pattern).
Aditya Hajare

1
@Fil Але код "зовні" closeConnectionне повинен усвідомлювати, що йому потрібно скопіювати посилання на змінну замість призначення об'єкта. Іншими словами, ваш спосіб спробувати кодування близької функції 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
Будь-ласка, видаліть спробу вловлювання зі свого коду або додайте нове кидання всередину, як показано тут . Зараз ваш код зловживає як винятками, так і повідомленнями про помилки взагалі
Ваше здорове глузд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.