Połączenie zamykające PDO

120

Po prostu dość proste pytanie dotyczące PDO w porównaniu z MySQLi.

Z MySQLi, aby zamknąć połączenie, możesz zrobić:

$this->connection->close();

Jednak w przypadku PDO stwierdza, że ​​otwierasz połączenie za pomocą:

$this->connection = new PDO();

ale aby zamknąć połączenie, które ustawiłeś null.

$this->connection = null;

Czy to prawda i czy to faktycznie zwolni połączenie PDO? (Wiem, że działa tak, jak jest ustawione null.) Mam na myśli, że w przypadku MySQLi musisz wywołać funkcję ( close), aby zamknąć połączenie. Czy PDO jest tak łatwe, jak = nullodłączenie? Czy jest funkcja zamykania połączenia?

Liam Sorsby
źródło
11
pytam o to, że nie jestem pewien, czy prawidłowo zamykałem połączenie. ale nie, nie tylko zaintrygowany
Liam Sorsby
2
Połączenie z bazą danych jest automatycznie zamykane, gdy skrypt PHP przestaje wykonywać.
Martin Bean
3
Jeśli skończyłeś go używać, dlaczego nie kontynuować i zakończyć go, zwłaszcza jeśli po zakończeniu interakcji z bazą danych jest jakiś czasochłonny kod. Chociaż tak naprawdę nie widzę problemu z czekaniem na zakończenie skryptu (poza ograniczeniem połączeń z serwerem DB).
Kieran
3
github.com/php/php-src/blob/master/ext/pdo/pdo_dbh.c Przekonaj się, jak to działa: P
Flosculus
23
Nie wszystkie skrypty php są krótkotrwałe. Istnieją demony php. Myślę, że to świetna rzecz do wyjaśnienia osobiście.
datUser

Odpowiedzi:

146

Zgodnie z dokumentacją masz rację ( http://php.net/manual/en/pdo.connections.php ):

Połączenie pozostaje aktywne przez cały okres istnienia tego obiektu PDO . Aby zamknąć połączenie, musisz zniszczyć obiekt , upewniając się, że wszystkie pozostałe odniesienia do niego zostały usunięte - robisz to, przypisując NULL do zmiennej, która przechowuje obiekt. Jeśli nie zrobisz tego jawnie, PHP automatycznie zamknie połączenie po zakończeniu skryptu .

Zwróć uwagę, że jeśli zainicjujesz obiekt PDO jako trwałe połączenie, połączenie nie zostanie automatycznie zamknięte.

Kieran
źródło
4
A jeśli proces się nie kończy? np. websocket. Czy istnieje sposób, aby nie używać trwałego połączenia?
Rafael Moni
1
W przypadku trwałych połączeń w skrypcie, który działa przez długi czas, możesz celowo (lub przypadkowo) zabić połączenia z przekroczeniem limitu czasu (np. W my.ini) lub z wielu innych powodów. Podczas łączenia lub uruchamiania zapytania wyłap jakikolwiek błąd, a jeśli jest to „MySQL zniknął”, spróbuj połączyć się ponownie lub uruchom zapytanie po raz drugi.
Frank Forte
1
Note that if you initialise the PDO object as a persistent connection it will not automatically close the connectionAle jeśli połączenie jest trwałe i jawnie wywołam na nim NULL przed zakończeniem skryptu, zostanie zamknięte, nawet jeśli jest trwałe, prawda?
tonix
1
@tonix Nie, powinno zostać wydane (udostępnione innemu skryptowi), ale nie zamknięte.
Benjamin,
2
@tonix Myślę, że tak. Cytuj z podręcznika PHP na temat trwałych połączeń : " Ostrzeżenie Istnieje kilka dodatkowych zastrzeżeń, o których należy pamiętać podczas korzystania z trwałych połączeń. Jednym z nich jest to, że podczas korzystania z blokowania tabeli na trwałym połączeniu, jeśli skrypt z jakiegokolwiek powodu nie może zwolnić blokady, wtedy kolejne skrypty korzystające z tego samego połączenia będą blokowane na czas nieokreślony i mogą wymagać ponownego uruchomienia serwera httpd lub serwera bazy danych. "
Benjamin,
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.
himanshu dholakia
źródło
11
IMHO Myślę, że to bardzo zły wzorzec, zwłaszcza gdy programista może przechowywać kilka kopii referencji pdo. $ a = nowe PDO (...); $ b = $ a; $ a = null; Tam twój obiekt PDO pozostanie otwarty na zawsze (w programie php podobnym do demona). Jest to szczególnie ważne, gdy odwołanie PDO przechodzi przez funkcje i właściwości obiektu i nigdy nie jesteś pewien, czy wszystkie z nich zostaną wyzerowane.
Gabriel
33
Na PDO powinna istnieć metoda -> close ().
Gabriel
5
Kolejny powód, by nie lubić PDO.
José Carlos PHP
6
@Gabriel - Sugeruję, że „przechowywanie kilku kopii” to jeszcze gorszy wzorzec.
Rick James
4
To nie działa, jeśli utworzyłeś obiekt PDOStatement pomiędzy tymi dwoma wierszami (to znaczy w każdej praktycznej sytuacji). Aby zamknąć połączenie, musisz ustawić zarówno obiekt PDO, jak i obiekt PDOStatement na wartość null. Zobacz tutaj: php.net/manual/en/pdo.connections.php#114822
Ilmari
8

To więcej niż tylko ustawienie połączenia na null. Może tak jest w dokumentacji, ale to nie jest prawda w przypadku mysql. Połączenie pozostanie trochę dłużej (słyszałem lata 60-te, ale nigdy tego nie testowałem)

Jeśli chcesz tutaj znaleźć pełne wyjaśnienie, zobacz ten komentarz dotyczący połączeń https://www.php.net/manual/en/pdo.connections.php#114822

Aby wymusić zamknięcie połączenia, musisz zrobić coś takiego

$this->connection = new PDO();
$this->connection->query('KILL CONNECTION_ID()');
$this->connection = null;
Jdahern
źródło
Dziękuję za Twoją odpowiedź. Pytanie było od jakiegoś czasu, ale masz rację co do połączenia.
Liam Sorsby
Właściwie nie zgadzam się, że majstrowanie przy połączeniu TCP przez PHP jest dobrym pomysłem. Cała obsługa połączeń TCP niskiego poziomu jest wyodrębniona, więc w czasie wykonywania musimy po prostu zajmować się klasą wysokiego poziomu i obiektami. PHP jest językiem opartym na żądaniach (jak zapewne wiesz), więc zabicie potencjalnie trwałego połączenia z dB prawdopodobnie spowoduje nieoczekiwane błędy / problemy dla użytkowników. Przypadek użycia, z którym łączysz się, prawdopodobnie powoduje, że sterownik utrzymuje otwarte połączenie trwałe w celu wykorzystania go przez inne żądanie, więc pomyślałem, że będzie to oczekiwane zachowanie.
Liam Sorsby
Jeśli faktycznie spojrzysz na listę procesów w mysql, pokaże ona, że ​​połączenie nadal istnieje. Zgadzam się, że nie powinieneś w ten sposób bawić się połączeniem TCP i powinien istnieć sposób na prawidłowe rozłączenie się z połączeniem. Ale tak nie jest. Tak więc, jeśli naprawdę chcesz odłączyć się od serwera, będziesz musiał zrobić coś takiego. Ustawienie połączenia na wartość null nie rozłącza połączenia wbrew temu, co mówią dokumenty.
Jdahern
Znalazłem to wyjaśnienie: stackoverflow.com/a/18277327/1315873
Fil
7

Utworzyłem klasę pochodną, ​​aby mieć bardziej samodokumentującą się instrukcję zamiast „$ 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;
    }
}

Mogę więc zadzwonić pod mój kod między:

$conn=CMyPDO::getNewConnection();
// my code
CMyPDO::closeConnection($conn);
Fil
źródło
1
Możesz ustawić metodę CMyPDO :: __ construct () jako prywatną i użyć tam wzorca singleton ..
Aditya Hajare
Tak, to możliwe. Jeśli używasz jednocześnie więcej niż jednej bazy danych, musisz także przypisać informacje o połączeniu inną metodą. Różnica jest minimalna, po prostu masz trochę dłuższą instrukcję wywoływania metod instancji.
Fil
@AdityaHajare Nie możesz uczynić publicznej metody superklasy prywatną w podklasie ..
nickdnk
@nickdnk, masz rację. Chodziło mi o stworzenie samodzielnej klasy CMyPDO (bez rozszerzania PDO), a następnie utworzenie instancji bazy danych wewnątrz prywatnego konstruktora CMyPDO (nowe PDO ($ dsn, $ dbuser, $ dbpass);), upewniając się, że tylko jedna wystąpienie jest dostępne w całej aplikacji (Singleton Design Pattern).
Aditya Hajare
1
@Fil Jednak kod „na zewnątrz” closeConnectionnie powinien być świadomy tego, że zamiast przypisywać obiekt musi skopiować odniesienie do zmiennej. Innymi słowy, próba kodowania bliskiej funkcji PDO ma złe skutki uboczne, przez co jest zawodna. Jedynym sposobem na zrobienie tego byłoby closeConnectionsprawdzenie, ile odwołań do obiektu PDO istnieje w kodzie i zgłoszenie, jeśli istnieje więcej niż 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

Pełny przykład z niestandardową klasą PDO2.

yo lo
źródło
1
Usuń try catch ze swojego kodu lub dodaj nowy rzut do środka, jak pokazano tutaj . W tej chwili Twój kod nadużywa zarówno wyjątków, jak i generalnie raportów o błędach
Your Common Sense