@@ -313,6 +313,7 @@ PHP_METHOD(PDO, __construct)
313313 int plen = 0 ;
314314 char * hashkey = NULL ;
315315 pdo_dbh_t * pdbh = NULL ;
316+ pdo_dbh_t * * pdbh_ref = NULL ;
316317 zval * v ;
317318
318319 if ((v = zend_hash_index_find_deref (Z_ARRVAL_P (options ), PDO_ATTR_PERSISTENT )) != NULL ) {
@@ -338,9 +339,22 @@ PHP_METHOD(PDO, __construct)
338339 /* is the connection still alive ? */
339340 if (!pdbh || pdbh -> is_closed ||
340341 (pdbh -> methods -> check_liveness && FAILURE == (pdbh -> methods -> check_liveness )(pdbh ))) {
342+ /* clean up prior dbh reference */
343+ if (pdbh && pdbh -> persistent_resource ) {
344+ pdbh_ref = (pdo_dbh_t * * )pdbh -> persistent_resource -> ptr ;
345+ /* clear dbh reference to forestall end-of-request actions in destructor */
346+ * pdbh_ref = NULL ;
347+ zend_list_delete (pdbh -> persistent_resource );
348+ pdbh -> persistent_resource = NULL ;
349+ }
341350 /* need a brand new pdbh */
342351 pdbh = pecalloc (1 , sizeof (* pdbh ), 1 );
343-
352+ pdbh_ref = emalloc (sizeof (* pdbh_ref ));
353+ * pdbh_ref = pdbh ;
354+ pdbh -> persistent_resource = zend_register_resource (pdbh_ref , php_pdo_list_entry ());
355+ if (!pdbh -> persistent_resource ) {
356+ php_error_docref (NULL , E_ERROR , "Failed to register resource entry" );
357+ }
344358 pdbh -> is_persistent = 1 ;
345359 pdbh -> persistent_id = pemalloc (plen + 1 , 1 );
346360 memcpy ((char * )pdbh -> persistent_id , hashkey , plen + 1 );
@@ -394,7 +408,7 @@ PHP_METHOD(PDO, __construct)
394408 /* we should also need to replace the object store entry,
395409 since it was created with emalloc */
396410 /* if a resource is already registered, then it failed a liveness check
397- * and will be replaced, prompting destruct. */
411+ and will be replaced, prompting destruct. */
398412 if ((zend_register_persistent_resource (
399413 (char * )dbh -> persistent_id , dbh -> persistent_id_len , dbh , php_pdo_list_entry ())) == NULL ) {
400414 php_error_docref (NULL , E_ERROR , "Failed to register persistent entry" );
@@ -517,6 +531,8 @@ PHP_METHOD(PDO, prepare)
517531
518532 PDO_DBH_CLEAR_ERR ();
519533
534+ PDO_CLOSE_CHECK ;
535+
520536 if (options && (value = zend_hash_index_find (Z_ARRVAL_P (options ), PDO_ATTR_STATEMENT_CLASS )) != NULL ) {
521537 if (Z_TYPE_P (value ) != IS_ARRAY ) {
522538 zend_type_error ("PDO::ATTR_STATEMENT_CLASS value must be of type array, %s given" ,
@@ -604,6 +620,8 @@ PHP_METHOD(PDO, beginTransaction)
604620
605621 PDO_CONSTRUCT_CHECK ;
606622
623+ PDO_CLOSE_CHECK ;
624+
607625 if (pdo_is_in_transaction (dbh )) {
608626 zend_throw_exception_ex (php_pdo_get_exception (), 0 , "There is already an active transaction" );
609627 RETURN_THROWS ();
@@ -634,6 +652,8 @@ PHP_METHOD(PDO, commit)
634652
635653 PDO_CONSTRUCT_CHECK ;
636654
655+ PDO_CLOSE_CHECK ;
656+
637657 if (!pdo_is_in_transaction (dbh )) {
638658 zend_throw_exception_ex (php_pdo_get_exception (), 0 , "There is no active transaction" );
639659 RETURN_THROWS ();
@@ -658,6 +678,8 @@ PHP_METHOD(PDO, rollBack)
658678
659679 PDO_CONSTRUCT_CHECK ;
660680
681+ PDO_CLOSE_CHECK ;
682+
661683 if (!pdo_is_in_transaction (dbh )) {
662684 zend_throw_exception_ex (php_pdo_get_exception (), 0 , "There is no active transaction" );
663685 RETURN_THROWS ();
@@ -682,6 +704,8 @@ PHP_METHOD(PDO, inTransaction)
682704
683705 PDO_CONSTRUCT_CHECK ;
684706
707+ PDO_CLOSE_CHECK ;
708+
685709 RETURN_BOOL (pdo_is_in_transaction (dbh ));
686710}
687711/* }}} */
@@ -693,6 +717,8 @@ PHP_METHOD(PDO, isConnected)
693717
694718 ZEND_PARSE_PARAMETERS_NONE ();
695719
720+ PDO_CONSTRUCT_CHECK ;
721+
696722 RETURN_BOOL (!dbh -> is_closed );
697723}
698724/* }}} */
@@ -900,6 +926,7 @@ PHP_METHOD(PDO, setAttribute)
900926
901927 PDO_DBH_CLEAR_ERR ();
902928 PDO_CONSTRUCT_CHECK ;
929+ PDO_CLOSE_CHECK ;
903930
904931 RETURN_BOOL (pdo_dbh_attribute_set (dbh , attr , value ));
905932}
@@ -954,6 +981,12 @@ PHP_METHOD(PDO, getAttribute)
954981 break ;
955982 }
956983
984+ if (!dbh -> methods ) {
985+ pdo_raise_impl_error (dbh , NULL , "IM001" ,
986+ "driver attributes not initialized, possibly due to disconnect" );
987+ RETURN_FALSE ;
988+ }
989+
957990 if (!dbh -> methods -> get_attribute ) {
958991 pdo_raise_impl_error (dbh , NULL , "IM001" , "driver does not support getting attributes" );
959992 RETURN_FALSE ;
@@ -994,6 +1027,8 @@ PHP_METHOD(PDO, exec)
9941027
9951028 PDO_DBH_CLEAR_ERR ();
9961029 PDO_CONSTRUCT_CHECK ;
1030+ PDO_CLOSE_CHECK ;
1031+
9971032 ret = dbh -> methods -> doer (dbh , statement );
9981033 if (ret == -1 ) {
9991034 PDO_HANDLE_DBH_ERR ();
@@ -1020,6 +1055,8 @@ PHP_METHOD(PDO, lastInsertId)
10201055
10211056 PDO_DBH_CLEAR_ERR ();
10221057
1058+ PDO_CLOSE_CHECK ;
1059+
10231060 if (!dbh -> methods -> last_id ) {
10241061 pdo_raise_impl_error (dbh , NULL , "IM001" , "driver does not support lastInsertId()" );
10251062 RETURN_FALSE ;
@@ -1040,6 +1077,8 @@ PHP_METHOD(PDO, errorCode)
10401077
10411078 ZEND_PARSE_PARAMETERS_NONE ();
10421079
1080+ PDO_CONSTRUCT_CHECK ;
1081+
10431082 if (dbh -> query_stmt ) {
10441083 RETURN_STRING (dbh -> query_stmt -> error_code );
10451084 }
@@ -1067,6 +1106,8 @@ PHP_METHOD(PDO, errorInfo)
10671106
10681107 ZEND_PARSE_PARAMETERS_NONE ();
10691108
1109+ PDO_CONSTRUCT_CHECK ;
1110+
10701111 array_init (return_value );
10711112
10721113 if (dbh -> query_stmt ) {
@@ -1127,6 +1168,8 @@ PHP_METHOD(PDO, query)
11271168
11281169 PDO_DBH_CLEAR_ERR ();
11291170
1171+ PDO_CLOSE_CHECK ;
1172+
11301173 if (!pdo_stmt_instantiate (dbh , return_value , dbh -> def_stmt_ce , & dbh -> def_stmt_ctor_args )) {
11311174 RETURN_THROWS ();
11321175 }
@@ -1194,6 +1237,9 @@ PHP_METHOD(PDO, quote)
11941237 PDO_CONSTRUCT_CHECK ;
11951238
11961239 PDO_DBH_CLEAR_ERR ();
1240+
1241+ PDO_CLOSE_CHECK ;
1242+
11971243 if (!dbh -> methods -> quoter ) {
11981244 pdo_raise_impl_error (dbh , NULL , "IM001" , "driver does not support quoting" );
11991245 RETURN_FALSE ;
@@ -1379,11 +1425,6 @@ void pdo_dbh_init(int module_number)
13791425/* Disconnect from the database and free associated driver. */
13801426static void dbh_shutdown (pdo_dbh_t * dbh )
13811427{
1382- if (dbh -> driver_data && dbh -> methods && dbh -> methods -> rollback && pdo_is_in_transaction (dbh )) {
1383- dbh -> methods -> rollback (dbh );
1384- dbh -> in_txn = false;
1385- }
1386-
13871428 if (dbh -> methods ) {
13881429 dbh -> methods -> closer (dbh );
13891430 }
@@ -1442,6 +1483,11 @@ static void dbh_free(pdo_dbh_t *dbh)
14421483 pefree ((char * )dbh -> persistent_id , dbh -> is_persistent );
14431484 }
14441485
1486+ if (dbh -> persistent_resource ) {
1487+ dbh -> persistent_resource -> ptr = NULL ;
1488+ dbh -> persistent_resource = NULL ;
1489+ }
1490+
14451491 if (!Z_ISUNDEF (dbh -> def_stmt_ctor_args )) {
14461492 zval_ptr_dtor (& dbh -> def_stmt_ctor_args );
14471493 }
@@ -1475,10 +1521,6 @@ static void pdo_dbh_free_storage(zend_object *std)
14751521
14761522 /* dbh might be null if we OOMed during object initialization. */
14771523 if (dbh ) {
1478- if (dbh -> is_persistent && dbh -> methods && dbh -> methods -> persistent_shutdown ) {
1479- dbh -> methods -> persistent_shutdown (dbh );
1480- }
1481-
14821524 /* stmt is not persistent, even if dbh is, so it must be freed with pdo.
14831525 * Consider copying stmt error code to dbh at this point, seemingly the reason
14841526 * that the stmt is even being held, or even better, to do that at the time of
@@ -1488,12 +1530,14 @@ static void pdo_dbh_free_storage(zend_object *std)
14881530 dbh -> query_stmt = NULL ;
14891531 }
14901532
1491- /* a persisted dbh will be freed when the resource is destructed. */
1492- if (!(-- dbh -> refcount ) && !pdo_is_persisted (dbh )) {
1493- if (!dbh -> is_closed ) {
1494- dbh_shutdown (dbh );
1533+ if (!(-- dbh -> refcount )) {
1534+ /* a persisted dbh will be freed when the resource is destructed. */
1535+ if (!pdo_is_persisted (dbh )) {
1536+ if (!dbh -> is_closed ) {
1537+ dbh_shutdown (dbh );
1538+ }
1539+ dbh_free (dbh );
14951540 }
1496- dbh_free (dbh );
14971541 }
14981542 }
14991543
@@ -1517,6 +1561,27 @@ zend_object *pdo_dbh_new(zend_class_entry *ce)
15171561
15181562/* }}} */
15191563
1564+ ZEND_RSRC_DTOR_FUNC (php_pdo_pdbh_request_dtor ) /* {{{ */
1565+ {
1566+ if (res -> ptr ) {
1567+ pdo_dbh_t * * dbh_ref = (pdo_dbh_t * * )res -> ptr ;
1568+ if (* dbh_ref ) {
1569+ pdo_dbh_t * dbh = (pdo_dbh_t * )* dbh_ref ;
1570+ if (dbh -> methods && dbh -> methods -> persistent_shutdown ) {
1571+ dbh -> methods -> persistent_shutdown (dbh );
1572+ }
1573+ if (dbh -> methods && dbh -> methods -> rollback && pdo_is_in_transaction (dbh )) {
1574+ dbh -> methods -> rollback (dbh );
1575+ dbh -> in_txn = false;
1576+ }
1577+ dbh -> persistent_resource = NULL ;
1578+ }
1579+ efree (dbh_ref );
1580+ res -> ptr = NULL ;
1581+ }
1582+ }
1583+ /* }}} */
1584+
15201585ZEND_RSRC_DTOR_FUNC (php_pdo_pdbh_dtor ) /* {{{ */
15211586{
15221587 if (res -> ptr ) {
0 commit comments