From c0853907a19cead153eea93b81220570d18c055b Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Mon, 24 Aug 2026 12:34:00 -0400 Subject: [PATCH] [PDO] Reset stale attributes when reusing a persistent connection Reusing a cached persistent handle only re-applied auto_commit and error_mode from the new $options, so desired_case, oracle_nulls, stringify and default_fetch_type leaked from whatever the previous request had set on that connection. Reset them to their defaults on reuse before applying the new options. Sibling audit found no other persistent reuse path; drivers do not cache these PDO-level flags in their own state except pdo_mysql's stringify, which mirrors the same dbh field and is reset along with it. --- NEWS | 6 ++++ ext/pdo/pdo_dbh.c | 5 ++++ .../persistent_handle_attribute_reset.phpt | 30 +++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 ext/pdo/tests/persistent_handle_attribute_reset.phpt diff --git a/NEWS b/NEWS index 3346d38ea898..9e31753f2d75 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,12 @@ PHP NEWS registrations are freed while still reachable from the cycle collector. (Ilia Alshanetsky) +- PDO: + . Reused persistent connections no longer keep stale fetch-related + attributes (case conversion, null handling, stringify, default fetch + mode) from the previous request. (Ilia Alshanetsky) + + 24 Sep 2026, PHP 8.4.26 diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 9f3d20745df9..115610490275 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -424,6 +424,11 @@ PDO_API void php_pdo_internal_construct_driver(INTERNAL_FUNCTION_PARAMETERS, zen if (pdbh) { call_factory = 0; + + pdbh->oracle_nulls = PDO_NULL_NATURAL; + pdbh->stringify = false; + pdbh->desired_case = PDO_CASE_NATURAL; + pdbh->default_fetch_type = PDO_FETCH_BOTH; } else { /* need a brand new pdbh */ pdbh = pecalloc(1, sizeof(*pdbh), 1); diff --git a/ext/pdo/tests/persistent_handle_attribute_reset.phpt b/ext/pdo/tests/persistent_handle_attribute_reset.phpt new file mode 100644 index 000000000000..ea5f05a40fa6 --- /dev/null +++ b/ext/pdo/tests/persistent_handle_attribute_reset.phpt @@ -0,0 +1,30 @@ +--TEST-- +Persistent connection reuse resets PDO attributes to their defaults +--EXTENSIONS-- +pdo +pdo_sqlite +--FILE-- + true]; + +$p1 = new PDO($dsn, null, null, $options); +$p1->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); +$p1->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_EMPTY_STRING); +$p1->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_NUM); +$p1->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER); + +unset($p1); + +$p2 = new PDO($dsn, null, null, $options); + +var_dump($p2->getAttribute(PDO::ATTR_STRINGIFY_FETCHES)); +var_dump($p2->getAttribute(PDO::ATTR_ORACLE_NULLS) === PDO::NULL_NATURAL); +var_dump($p2->getAttribute(PDO::ATTR_DEFAULT_FETCH_MODE) === PDO::FETCH_BOTH); +var_dump($p2->getAttribute(PDO::ATTR_CASE) === PDO::CASE_NATURAL); +?> +--EXPECT-- +bool(false) +bool(true) +bool(true) +bool(true)