From 19deb91002a53958893903aa3315e30e3285e614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=A6=E7=94=B0=20=E6=86=B2=E5=A4=AA=E9=83=8E?= Date: Mon, 24 Nov 2025 12:03:38 +0000 Subject: [PATCH 1/2] pdo_pgsql: Reset persistent session state on disconnect-equivalent processing close GH-20572 --- NEWS | 4 ++ ext/pdo_pgsql/pgsql_driver.c | 16 +++++- ext/pdo_pgsql/tests/session_state_reset.phpt | 55 +++++++++++++++++++ .../session_state_reset_advisory_lock.phpt | 51 +++++++++++++++++ ...session_state_reset_after_interrupted.phpt | 52 ++++++++++++++++++ 5 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 ext/pdo_pgsql/tests/session_state_reset.phpt create mode 100644 ext/pdo_pgsql/tests/session_state_reset_advisory_lock.phpt create mode 100644 ext/pdo_pgsql/tests/session_state_reset_after_interrupted.phpt diff --git a/NEWS b/NEWS index 1ec90015ef21a..af1bb92003d32 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,10 @@ PHP NEWS . Fixed bug GH-20051 (apache2 shutdowns when restart is requested during preloading). (Arnaud, welcomycozyhom) +- PDO_PGSQL: + . Clear session-local state disconnect-equivalent processing. + (KentarouTakeda) + - Phar: . Support reference values in Phar::mungServer(). (ndossche) . Invalid values now throw in Phar::mungServer() instead of being silently diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index be865c1f86838..44f70e00236d2 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -1340,6 +1340,20 @@ static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, i } } +static void pdo_pgsql_request_shutdown(pdo_dbh_t *dbh) +{ + PGresult *res; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + + if(H->server) { + res = PQexec(H->server, "DISCARD ALL"); + + if(res) { + PQclear(res); + } + } +} + static bool pdo_pgsql_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) { bool bval; @@ -1383,7 +1397,7 @@ static const struct pdo_dbh_methods pgsql_methods = { pdo_pgsql_get_attribute, pdo_pgsql_check_liveness, /* check_liveness */ pdo_pgsql_get_driver_methods, /* get_driver_methods */ - NULL, + pdo_pgsql_request_shutdown, pgsql_handle_in_transaction, NULL, /* get_gc */ pdo_pgsql_scanner diff --git a/ext/pdo_pgsql/tests/session_state_reset.phpt b/ext/pdo_pgsql/tests/session_state_reset.phpt new file mode 100644 index 0000000000000..ad892bb0da52e --- /dev/null +++ b/ext/pdo_pgsql/tests/session_state_reset.phpt @@ -0,0 +1,55 @@ +--TEST-- +Persistent connections: session state reset when performing disconnect-equivalent processing (general case) +--EXTENSIONS-- +pdo_pgsql +--SKIPIF-- + +--FILE-- + true])); + +require __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc'; + +$pdo1 = PDOTest::test_factory(__DIR__ . '/common.phpt'); + +$pid1 = (int)$pdo1 + ->query('select pg_backend_pid()::int;') + ->fetchColumn(0); + +$defaultValue = (int)$pdo1 + ->query('show log_min_duration_statement;') + ->fetchColumn(0); + +$setValue = $defaultValue + 1; + +$pdo1->exec("set log_min_duration_statement = {$setValue};"); + +$pdo1 = null; + +$pdo2 = PDOTest::test_factory(__DIR__ . '/common.phpt'); + +$pid2 = (int)$pdo2 + ->query('select pg_backend_pid()::int;') + ->fetchColumn(0); + +assert($pid1 === $pid2); + +$expectedValue = (int)$pdo2 + ->query('show log_min_duration_statement;') + ->fetchColumn(0); + +echo "defaultValue: {$defaultValue}\n"; +echo "setValue: {$setValue}\n"; +echo "expectedValue: {$expectedValue}\n"; +echo "expected value should be reset to default: " . (($expectedValue === $defaultValue) ? 'success' : 'failure') . "\n"; + +?> +--EXPECTF-- +defaultValue: %i +setValue: %d +expectedValue: %i +expected value should be reset to default: success diff --git a/ext/pdo_pgsql/tests/session_state_reset_advisory_lock.phpt b/ext/pdo_pgsql/tests/session_state_reset_advisory_lock.phpt new file mode 100644 index 0000000000000..3435f6b7b402f --- /dev/null +++ b/ext/pdo_pgsql/tests/session_state_reset_advisory_lock.phpt @@ -0,0 +1,51 @@ +--TEST-- +Persistent connections: session state reset when performing disconnect-equivalent processing (advisory lock case) +--EXTENSIONS-- +pdo_pgsql +--SKIPIF-- + +--FILE-- + true])); + +require __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc'; + +$pdo1 = PDOTest::test_factory(__DIR__ . '/common.phpt'); + +$pid1 = (int)$pdo1 + ->query('select pg_backend_pid()::int;') + ->fetchColumn(0); + +$lockResult1 = (bool)$pdo1 + ->query('select pg_try_advisory_lock(42)::int;') + ->fetchColumn(0); + +$pdo1 = null; + +$dsn = getenv('PDO_PGSQL_TEST_DSN'); +$dsn .= ';'; +putenv('PDO_PGSQL_TEST_DSN='.$dsn); + +$pdo2 = PDOTest::test_factory(__DIR__ . '/common.phpt'); + +$pid2 = (int)$pdo2 + ->query('select pg_backend_pid()::int;') + ->fetchColumn(0); + +assert($pid1 !== $pid2); + +$lockResult2 = (bool)$pdo2 + ->query('select pg_try_advisory_lock(42)::int;') + ->fetchColumn(0); + +echo "lock1: " . ($lockResult1 ? 'success' : 'failure') . "\n"; +echo "lock2: " . ($lockResult2 ? 'success' : 'failure') . "\n"; + +?> +--EXPECT-- +lock1: success +lock2: success diff --git a/ext/pdo_pgsql/tests/session_state_reset_after_interrupted.phpt b/ext/pdo_pgsql/tests/session_state_reset_after_interrupted.phpt new file mode 100644 index 0000000000000..fdafe8a00fc39 --- /dev/null +++ b/ext/pdo_pgsql/tests/session_state_reset_after_interrupted.phpt @@ -0,0 +1,52 @@ +--TEST-- +Persistent connections: session state reset after backend termination (interrupted case) +--EXTENSIONS-- +pdo_pgsql +--SKIPIF-- + +--FILE-- + true])); + +require __DIR__ . '/../../../ext/pdo/tests/pdo_test.inc'; + +$pdo1 = PDOTest::test_factory(__DIR__ . '/common.phpt'); + +$pid1 = (int)$pdo1 + ->query('select pg_backend_pid()::int;') + ->fetchColumn(0); + +$pid1 = (int)$pdo1 + ->query('select pg_backend_pid()::int;') + ->fetchColumn(0); + +$dsn = getenv('PDO_PGSQL_TEST_DSN'); +$dsn .= ';'; +putenv('PDO_PGSQL_TEST_DSN='.$dsn); + +$pdo2 = PDOTest::test_factory(__DIR__ . '/common.phpt'); + +$pid2 = (int)$pdo2 + ->query('select pg_backend_pid()::int;') + ->fetchColumn(0); + +assert($pid1 !== $pid2); + +$terminateResult = (bool)$pdo2 + ->query("select pg_terminate_backend({$pid1})::int") + ->fetchColumn(0); + +// Disconnect after being terminated by another connection +$pdo1 = null; + +echo 'pid of pdo1: ' . $pid1 . "\n"; +echo 'terminate result of pdo1 by pdo2: ' . ($terminateResult ? 'success' : 'failure') . "\n"; + +?> +--EXPECTF-- +pid of pdo1: %d +terminate result of pdo1 by pdo2: success From 1f3fe93eff69f994be72ba47e143a0f6d01279e4 Mon Sep 17 00:00:00 2001 From: Heran Yang Date: Fri, 12 Dec 2025 10:58:37 +0800 Subject: [PATCH 2/2] Add GB18030-2022 to default encoding list for zh-CN (#20604) GB18030-2022 is the current official standard, superseding the previous 2005 and 2000 versions. It is essential for modern Chinese text processing for the following reasons: 1. Superset Relationship: GB18030 is a strict superset of CP936 (GBK) and EUC-CN (GB2312). Using GB18030 as the detection target covers all characters in these older encodings while enabling support for a much wider range of characters. 2. Extended Character Coverage: The 2022 standard includes significant updates, covering over 87,000 characters. It adds support for CJK Extensions (C, D, E, F, G) and updates mappings for rare characters that were previously mapped to the Private Use Area (PUA) in the 2005 version. This is critical for correctly handling names containing rare characters (e.g., in banking or government data). 3. Backward Compatibility: It is safe to promote GB18030-2022 as the preferred encoding. Files encoded in EUC-CN or CP936 are valid GB18030 streams. This PR adds GB18030-2022 to the default encoding list for CN. --- NEWS | 1 + UPGRADING.INTERNALS | 3 +++ ext/mbstring/mbstring.c | 3 ++- .../tests/zh_CN_default_encodings.phpt | 24 +++++++++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 ext/mbstring/tests/zh_CN_default_encodings.phpt diff --git a/NEWS b/NEWS index af1bb92003d32..f2e81284bdf89 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,7 @@ PHP NEWS - Mbstring: . ini_set() with mbstring.detect_order changes the order of mb_detect_order as intended, since mbstring.detect_order is an INI_ALL setting. (tobee94) + . Added GB18030-2022 to default encoding list for zh-CN. (HeRaNO) - Opcache: . Fixed bug GH-20051 (apache2 shutdowns when restart is requested during diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index e35557e71f462..71b6e1fd01ecc 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -64,6 +64,9 @@ PHP 8.6 INTERNALS UPGRADE NOTES . Removed the XML_GetCurrentByteCount() libxml compatibility wrapper, as it was unused and could return the wrong result. +- ext/mbstring: + . Added GB18030-2022 to default encoding list for zh-CN. + ======================== 4. OpCode changes ======================== diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 8f94e50ddc861..7422c600284d5 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -116,7 +116,8 @@ static const enum mbfl_no_encoding php_mb_default_identify_list_cn[] = { mbfl_no_encoding_ascii, mbfl_no_encoding_utf8, mbfl_no_encoding_euc_cn, - mbfl_no_encoding_cp936 + mbfl_no_encoding_cp936, + mbfl_no_encoding_gb18030_2022 }; static const enum mbfl_no_encoding php_mb_default_identify_list_tw_hk[] = { diff --git a/ext/mbstring/tests/zh_CN_default_encodings.phpt b/ext/mbstring/tests/zh_CN_default_encodings.phpt new file mode 100644 index 0000000000000..213c304b52c69 --- /dev/null +++ b/ext/mbstring/tests/zh_CN_default_encodings.phpt @@ -0,0 +1,24 @@ +--TEST-- +Default encodings in Simplified Chinese +--EXTENSIONS-- +mbstring +--INI-- +mbstring.language=Simplified Chinese +--FILE-- + +--EXPECT-- +array(5) { + [0]=> + string(5) "ASCII" + [1]=> + string(5) "UTF-8" + [2]=> + string(6) "EUC-CN" + [3]=> + string(5) "CP936" + [4]=> + string(12) "GB18030-2022" +}