From c4b74d407334df05341adcc7b361ad0551f6fd6b Mon Sep 17 00:00:00 2001 From: Arshid Date: Fri, 26 Jun 2026 13:53:30 +0530 Subject: [PATCH 1/9] ext/phar: Avoid redundant manifest hash lookup in offsetUnset() (#22437) Replace `zend_hash_exists()` + `zend_hash_find_ptr()` with a single `zend_hash_find_ptr()` call and a `NULL` check, avoiding an unnecessary HashTable lookup. --- ext/phar/phar_object.c | 43 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index e2ba71c501e7..7eb8392e6c69 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -3741,33 +3741,32 @@ PHP_METHOD(Phar, offsetUnset) RETURN_THROWS(); } - if (zend_hash_exists(&phar_obj->archive->manifest, file_name)) { - phar_entry_info *entry = zend_hash_find_ptr(&phar_obj->archive->manifest, file_name); - if (entry) { - if (entry->is_deleted) { - /* entry is deleted, but has not been flushed to disk yet */ - return; - } + phar_entry_info *entry = zend_hash_find_ptr(&phar_obj->archive->manifest, file_name); + if (entry) { + if (entry->is_deleted) { + /* entry is deleted, but has not been flushed to disk yet */ + return; + } - if (phar_obj->archive->is_persistent) { - if (FAILURE == phar_copy_on_write(&(phar_obj->archive))) { - zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", ZSTR_VAL(phar_obj->archive->fname)); - RETURN_THROWS(); - } - /* re-populate entry after copy on write */ - entry = zend_hash_find_ptr(&phar_obj->archive->manifest, file_name); + if (phar_obj->archive->is_persistent) { + if (FAILURE == phar_copy_on_write(&(phar_obj->archive))) { + zend_throw_exception_ex(phar_ce_PharException, 0, "phar \"%s\" is persistent, unable to copy on write", ZSTR_VAL(phar_obj->archive->fname)); + RETURN_THROWS(); } - entry->is_modified = 0; - entry->is_deleted = 1; - /* we need to "flush" the stream to save the newly deleted file on disk */ - phar_flush(phar_obj->archive, &error); + /* re-populate entry after copy on write */ + entry = zend_hash_find_ptr(&phar_obj->archive->manifest, file_name); + } + entry->is_modified = 0; + entry->is_deleted = 1; + /* we need to "flush" the stream to save the newly deleted file on disk */ + phar_flush(phar_obj->archive, &error); - if (error) { - zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error); - efree(error); - } + if (error) { + zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error); + efree(error); } } + } /* }}} */ From 9313e8f6c05a040fea7d8cdf1d9949c16896ae83 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 26 Jun 2026 16:24:23 +0800 Subject: [PATCH 2/9] Fix GH-17384: Reject too large number_format decimals (#22435) number_format($number, 9876543210); is now silently equals to number_format($number, 2147483647); and generates 2147483647 decimal places and eat up 2 GB memory (and exhaust almost half of them which cause a fatal error). I only reject very large positive numbers here (as every input larger than 2147483647 is silently turned into 2147483647). Because negative ones is always returning 0 anyways and only very large positive numbers can cause to such problems. Fixes #17384 --- NEWS | 2 ++ ext/standard/math.c | 11 ++++---- ext/standard/tests/math/gh17384.phpt | 25 +++++++++++++++++++ .../math/number_format_basiclong_64bit.phpt | 18 ------------- .../tests/math/number_format_decimals.phpt | 22 +--------------- 5 files changed, 34 insertions(+), 44 deletions(-) create mode 100644 ext/standard/tests/math/gh17384.phpt diff --git a/NEWS b/NEWS index ce6e46a4c36b..1fe33f473af6 100644 --- a/NEWS +++ b/NEWS @@ -283,6 +283,8 @@ PHP NEWS (sebastian) . Fixed bug GH-22171 (Invalid auth header generation in http(s) stream wrapper). (David Carlier) + . Fixed bug GH-17384 (number_format() may exhaust memory with decimals + outside the range from -2147483648 to 2147483647). (Weilin Du) - Streams: . Added new stream errors API including new StreamException, StreamError diff --git a/ext/standard/math.c b/ext/standard/math.c index 6550e965d177..1498a1efc0b2 100644 --- a/ext/standard/math.c +++ b/ext/standard/math.c @@ -1415,6 +1415,11 @@ PHP_FUNCTION(number_format) thousand_sep_len = 1; } + if (UNEXPECTED(dec > INT_MAX || dec < INT_MIN)) { + zend_argument_value_error(2, "must be between %d and %d", INT_MIN, INT_MAX); + RETURN_THROWS(); + } + switch (Z_TYPE_P(num)) { case IS_LONG: RETURN_STR(_php_math_number_format_long(Z_LVAL_P(num), dec, dec_point, dec_point_len, thousand_sep, thousand_sep_len)); @@ -1429,11 +1434,7 @@ PHP_FUNCTION(number_format) RETURN_STR(_php_math_number_format_long((zend_long)Z_DVAL_P(num), dec, dec_point, dec_point_len, thousand_sep, thousand_sep_len)); } - if (dec >= 0) { - dec_int = ZEND_LONG_INT_OVFL(dec) ? INT_MAX : (int)dec; - } else { - dec_int = ZEND_LONG_INT_UDFL(dec) ? INT_MIN : (int)dec; - } + dec_int = (int) dec; RETURN_STR(_php_math_number_format_ex(Z_DVAL_P(num), dec_int, dec_point, dec_point_len, thousand_sep, thousand_sep_len)); default: ZEND_UNREACHABLE(); diff --git a/ext/standard/tests/math/gh17384.phpt b/ext/standard/tests/math/gh17384.phpt new file mode 100644 index 000000000000..eff207a906a6 --- /dev/null +++ b/ext/standard/tests/math/gh17384.phpt @@ -0,0 +1,25 @@ +--TEST-- +GH-17384: number_format() must reject decimals outside the int range +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; + } + } +} + +?> +--EXPECT-- +number_format(): Argument #2 ($decimals) must be between -2147483648 and 2147483647 +number_format(): Argument #2 ($decimals) must be between -2147483648 and 2147483647 +number_format(): Argument #2 ($decimals) must be between -2147483648 and 2147483647 +number_format(): Argument #2 ($decimals) must be between -2147483648 and 2147483647 diff --git a/ext/standard/tests/math/number_format_basiclong_64bit.phpt b/ext/standard/tests/math/number_format_basiclong_64bit.phpt index fd709fc648f9..7a986efc1fae 100644 --- a/ext/standard/tests/math/number_format_basiclong_64bit.phpt +++ b/ext/standard/tests/math/number_format_basiclong_64bit.phpt @@ -30,7 +30,6 @@ $precisions = array( -17, -19, -20, - PHP_INT_MIN, ); foreach ($numbers as $number) { @@ -54,7 +53,6 @@ foreach ($numbers as $number) { ... with precision -17: string(25) "9,200,000,000,000,000,000" ... with precision -19: string(26) "10,000,000,000,000,000,000" ... with precision -20: string(1) "0" -... with precision -9223372036854775808: string(1) "0" --- testing: int(-9223372036854775808) ... with precision 5: string(32) "-9,223,372,036,854,775,808.00000" ... with precision 0: string(26) "-9,223,372,036,854,775,808" @@ -65,7 +63,6 @@ foreach ($numbers as $number) { ... with precision -17: string(26) "-9,200,000,000,000,000,000" ... with precision -19: string(27) "-10,000,000,000,000,000,000" ... with precision -20: string(1) "0" -... with precision -9223372036854775808: string(1) "0" --- testing: int(2147483647) ... with precision 5: string(19) "2,147,483,647.00000" ... with precision 0: string(13) "2,147,483,647" @@ -76,7 +73,6 @@ foreach ($numbers as $number) { ... with precision -17: string(1) "0" ... with precision -19: string(1) "0" ... with precision -20: string(1) "0" -... with precision -9223372036854775808: string(1) "0" --- testing: int(-2147483648) ... with precision 5: string(20) "-2,147,483,648.00000" ... with precision 0: string(14) "-2,147,483,648" @@ -87,7 +83,6 @@ foreach ($numbers as $number) { ... with precision -17: string(1) "0" ... with precision -19: string(1) "0" ... with precision -20: string(1) "0" -... with precision -9223372036854775808: string(1) "0" --- testing: int(9223372034707292160) ... with precision 5: string(31) "9,223,372,034,707,292,160.00000" ... with precision 0: string(25) "9,223,372,034,707,292,160" @@ -98,7 +93,6 @@ foreach ($numbers as $number) { ... with precision -17: string(25) "9,200,000,000,000,000,000" ... with precision -19: string(26) "10,000,000,000,000,000,000" ... with precision -20: string(1) "0" -... with precision -9223372036854775808: string(1) "0" --- testing: int(-9223372034707292160) ... with precision 5: string(32) "-9,223,372,034,707,292,160.00000" ... with precision 0: string(26) "-9,223,372,034,707,292,160" @@ -109,7 +103,6 @@ foreach ($numbers as $number) { ... with precision -17: string(26) "-9,200,000,000,000,000,000" ... with precision -19: string(27) "-10,000,000,000,000,000,000" ... with precision -20: string(1) "0" -... with precision -9223372036854775808: string(1) "0" --- testing: int(2147483648) ... with precision 5: string(19) "2,147,483,648.00000" ... with precision 0: string(13) "2,147,483,648" @@ -120,7 +113,6 @@ foreach ($numbers as $number) { ... with precision -17: string(1) "0" ... with precision -19: string(1) "0" ... with precision -20: string(1) "0" -... with precision -9223372036854775808: string(1) "0" --- testing: int(-2147483649) ... with precision 5: string(20) "-2,147,483,649.00000" ... with precision 0: string(14) "-2,147,483,649" @@ -131,7 +123,6 @@ foreach ($numbers as $number) { ... with precision -17: string(1) "0" ... with precision -19: string(1) "0" ... with precision -20: string(1) "0" -... with precision -9223372036854775808: string(1) "0" --- testing: int(4294967294) ... with precision 5: string(19) "4,294,967,294.00000" ... with precision 0: string(13) "4,294,967,294" @@ -142,7 +133,6 @@ foreach ($numbers as $number) { ... with precision -17: string(1) "0" ... with precision -19: string(1) "0" ... with precision -20: string(1) "0" -... with precision -9223372036854775808: string(1) "0" --- testing: int(4294967295) ... with precision 5: string(19) "4,294,967,295.00000" ... with precision 0: string(13) "4,294,967,295" @@ -153,7 +143,6 @@ foreach ($numbers as $number) { ... with precision -17: string(1) "0" ... with precision -19: string(1) "0" ... with precision -20: string(1) "0" -... with precision -9223372036854775808: string(1) "0" --- testing: int(4294967293) ... with precision 5: string(19) "4,294,967,293.00000" ... with precision 0: string(13) "4,294,967,293" @@ -164,7 +153,6 @@ foreach ($numbers as $number) { ... with precision -17: string(1) "0" ... with precision -19: string(1) "0" ... with precision -20: string(1) "0" -... with precision -9223372036854775808: string(1) "0" --- testing: int(9223372036854775806) ... with precision 5: string(31) "9,223,372,036,854,775,806.00000" ... with precision 0: string(25) "9,223,372,036,854,775,806" @@ -175,7 +163,6 @@ foreach ($numbers as $number) { ... with precision -17: string(25) "9,200,000,000,000,000,000" ... with precision -19: string(26) "10,000,000,000,000,000,000" ... with precision -20: string(1) "0" -... with precision -9223372036854775808: string(1) "0" --- testing: float(9.223372036854776E+18) ... with precision 5: string(31) "9,223,372,036,854,775,808.00000" ... with precision 0: string(25) "9,223,372,036,854,775,808" @@ -186,7 +173,6 @@ foreach ($numbers as $number) { ... with precision -17: string(25) "9,200,000,000,000,000,000" ... with precision -19: string(26) "10,000,000,000,000,000,000" ... with precision -20: string(1) "0" -... with precision -9223372036854775808: string(1) "0" --- testing: int(-9223372036854775807) ... with precision 5: string(32) "-9,223,372,036,854,775,807.00000" ... with precision 0: string(26) "-9,223,372,036,854,775,807" @@ -197,7 +183,6 @@ foreach ($numbers as $number) { ... with precision -17: string(26) "-9,200,000,000,000,000,000" ... with precision -19: string(27) "-10,000,000,000,000,000,000" ... with precision -20: string(1) "0" -... with precision -9223372036854775808: string(1) "0" --- testing: float(-9.223372036854776E+18) ... with precision 5: string(32) "-9,223,372,036,854,775,808.00000" ... with precision 0: string(26) "-9,223,372,036,854,775,808" @@ -208,7 +193,6 @@ foreach ($numbers as $number) { ... with precision -17: string(26) "-9,200,000,000,000,000,000" ... with precision -19: string(27) "-10,000,000,000,000,000,000" ... with precision -20: string(1) "0" -... with precision -9223372036854775808: string(1) "0" --- testing: float(9.223372036854775E+18) ... with precision 5: string(31) "9,223,372,036,854,774,784.00000" ... with precision 0: string(25) "9,223,372,036,854,774,784" @@ -219,7 +203,6 @@ foreach ($numbers as $number) { ... with precision -17: string(25) "9,200,000,000,000,000,000" ... with precision -19: string(26) "10,000,000,000,000,000,000" ... with precision -20: string(1) "0" -... with precision -9223372036854775808: string(1) "0" --- testing: float(-9.223372036854775E+18) ... with precision 5: string(32) "-9,223,372,036,854,774,784.00000" ... with precision 0: string(26) "-9,223,372,036,854,774,784" @@ -230,4 +213,3 @@ foreach ($numbers as $number) { ... with precision -17: string(26) "-9,200,000,000,000,000,000" ... with precision -19: string(27) "-10,000,000,000,000,000,000" ... with precision -20: string(1) "0" -... with precision -9223372036854775808: string(1) "0" diff --git a/ext/standard/tests/math/number_format_decimals.phpt b/ext/standard/tests/math/number_format_decimals.phpt index 5fa45fddce2c..b589f0aefeba 100644 --- a/ext/standard/tests/math/number_format_decimals.phpt +++ b/ext/standard/tests/math/number_format_decimals.phpt @@ -29,7 +29,7 @@ $values = array( MIN_INT32, ); -$decimals = array(0, 1, 2, 3, 4, 5, -1, -2, -3, -4, -5, PHP_INT_MIN); +$decimals = array(0, 1, 2, 3, 4, 5, -1, -2, -3, -4, -5); foreach ($values as $value) { echo 'testing '; @@ -55,7 +55,6 @@ testing float(1.5151) ... with decimal places of -3: string(1) "0" ... with decimal places of -4: string(1) "0" ... with decimal places of -5: string(1) "0" -... with decimal places of %i: string(1) "0" testing float(15.151) ... with decimal places of 0: string(2) "15" ... with decimal places of 1: string(4) "15.2" @@ -68,7 +67,6 @@ testing float(15.151) ... with decimal places of -3: string(1) "0" ... with decimal places of -4: string(1) "0" ... with decimal places of -5: string(1) "0" -... with decimal places of %i: string(1) "0" testing float(151.51) ... with decimal places of 0: string(3) "152" ... with decimal places of 1: string(5) "151.5" @@ -81,7 +79,6 @@ testing float(151.51) ... with decimal places of -3: string(1) "0" ... with decimal places of -4: string(1) "0" ... with decimal places of -5: string(1) "0" -... with decimal places of %i: string(1) "0" testing float(1515.1) ... with decimal places of 0: string(5) "1,515" ... with decimal places of 1: string(7) "1,515.1" @@ -94,7 +91,6 @@ testing float(1515.1) ... with decimal places of -3: string(5) "2,000" ... with decimal places of -4: string(1) "0" ... with decimal places of -5: string(1) "0" -... with decimal places of %i: string(1) "0" testing int(15151) ... with decimal places of 0: string(6) "15,151" ... with decimal places of 1: string(8) "15,151.0" @@ -107,7 +103,6 @@ testing int(15151) ... with decimal places of -3: string(6) "15,000" ... with decimal places of -4: string(6) "20,000" ... with decimal places of -5: string(1) "0" -... with decimal places of %i: string(1) "0" testing float(-1.5151) ... with decimal places of 0: string(2) "-2" ... with decimal places of 1: string(4) "-1.5" @@ -120,7 +115,6 @@ testing float(-1.5151) ... with decimal places of -3: string(1) "0" ... with decimal places of -4: string(1) "0" ... with decimal places of -5: string(1) "0" -... with decimal places of %i: string(1) "0" testing float(-15.151) ... with decimal places of 0: string(3) "-15" ... with decimal places of 1: string(5) "-15.2" @@ -133,7 +127,6 @@ testing float(-15.151) ... with decimal places of -3: string(1) "0" ... with decimal places of -4: string(1) "0" ... with decimal places of -5: string(1) "0" -... with decimal places of %i: string(1) "0" testing float(-151.51) ... with decimal places of 0: string(4) "-152" ... with decimal places of 1: string(6) "-151.5" @@ -146,7 +139,6 @@ testing float(-151.51) ... with decimal places of -3: string(1) "0" ... with decimal places of -4: string(1) "0" ... with decimal places of -5: string(1) "0" -... with decimal places of %i: string(1) "0" testing float(-1515.1) ... with decimal places of 0: string(6) "-1,515" ... with decimal places of 1: string(8) "-1,515.1" @@ -159,7 +151,6 @@ testing float(-1515.1) ... with decimal places of -3: string(6) "-2,000" ... with decimal places of -4: string(1) "0" ... with decimal places of -5: string(1) "0" -... with decimal places of %i: string(1) "0" testing int(-15151) ... with decimal places of 0: string(7) "-15,151" ... with decimal places of 1: string(9) "-15,151.0" @@ -172,7 +163,6 @@ testing int(-15151) ... with decimal places of -3: string(7) "-15,000" ... with decimal places of -4: string(7) "-20,000" ... with decimal places of -5: string(1) "0" -... with decimal places of %i: string(1) "0" testing int(999) ... with decimal places of 0: string(3) "999" ... with decimal places of 1: string(5) "999.0" @@ -185,7 +175,6 @@ testing int(999) ... with decimal places of -3: string(5) "1,000" ... with decimal places of -4: string(1) "0" ... with decimal places of -5: string(1) "0" -... with decimal places of %i: string(1) "0" testing int(-999) ... with decimal places of 0: string(4) "-999" ... with decimal places of 1: string(6) "-999.0" @@ -198,7 +187,6 @@ testing int(-999) ... with decimal places of -3: string(6) "-1,000" ... with decimal places of -4: string(1) "0" ... with decimal places of -5: string(1) "0" -... with decimal places of %i: string(1) "0" testing float(999) ... with decimal places of 0: string(3) "999" ... with decimal places of 1: string(5) "999.0" @@ -211,7 +199,6 @@ testing float(999) ... with decimal places of -3: string(5) "1,000" ... with decimal places of -4: string(1) "0" ... with decimal places of -5: string(1) "0" -... with decimal places of %i: string(1) "0" testing float(-999) ... with decimal places of 0: string(4) "-999" ... with decimal places of 1: string(6) "-999.0" @@ -224,7 +211,6 @@ testing float(-999) ... with decimal places of -3: string(6) "-1,000" ... with decimal places of -4: string(1) "0" ... with decimal places of -5: string(1) "0" -... with decimal places of %i: string(1) "0" testing int(999999) ... with decimal places of 0: string(7) "999,999" ... with decimal places of 1: string(9) "999,999.0" @@ -237,7 +223,6 @@ testing int(999999) ... with decimal places of -3: string(9) "1,000,000" ... with decimal places of -4: string(9) "1,000,000" ... with decimal places of -5: string(9) "1,000,000" -... with decimal places of %i: string(1) "0" testing int(-999999) ... with decimal places of 0: string(8) "-999,999" ... with decimal places of 1: string(10) "-999,999.0" @@ -250,7 +235,6 @@ testing int(-999999) ... with decimal places of -3: string(10) "-1,000,000" ... with decimal places of -4: string(10) "-1,000,000" ... with decimal places of -5: string(10) "-1,000,000" -... with decimal places of %i: string(1) "0" testing float(999999) ... with decimal places of 0: string(7) "999,999" ... with decimal places of 1: string(9) "999,999.0" @@ -263,7 +247,6 @@ testing float(999999) ... with decimal places of -3: string(9) "1,000,000" ... with decimal places of -4: string(9) "1,000,000" ... with decimal places of -5: string(9) "1,000,000" -... with decimal places of %i: string(1) "0" testing float(-999999) ... with decimal places of 0: string(8) "-999,999" ... with decimal places of 1: string(10) "-999,999.0" @@ -276,7 +259,6 @@ testing float(-999999) ... with decimal places of -3: string(10) "-1,000,000" ... with decimal places of -4: string(10) "-1,000,000" ... with decimal places of -5: string(10) "-1,000,000" -... with decimal places of %i: string(1) "0" testing int(2147483647) ... with decimal places of 0: string(13) "2,147,483,647" ... with decimal places of 1: string(15) "2,147,483,647.0" @@ -289,7 +271,6 @@ testing int(2147483647) ... with decimal places of -3: string(13) "2,147,484,000" ... with decimal places of -4: string(13) "2,147,480,000" ... with decimal places of -5: string(13) "2,147,500,000" -... with decimal places of %i: string(1) "0" testing int(-2147483648) ... with decimal places of 0: string(14) "-2,147,483,648" ... with decimal places of 1: string(16) "-2,147,483,648.0" @@ -302,4 +283,3 @@ testing int(-2147483648) ... with decimal places of -3: string(14) "-2,147,484,000" ... with decimal places of -4: string(14) "-2,147,480,000" ... with decimal places of -5: string(14) "-2,147,500,000" -... with decimal places of %i: string(1) "0" From adc5f8da339c6a5ef80afd64c77735456f5e307d Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Fri, 26 Jun 2026 06:31:16 -0400 Subject: [PATCH 3/9] Fix GH-21682: reject ZipArchive serialization via __serialize() (#21708) ZipArchive wraps a libzip handle that cannot survive serialization: serialize() produced a string that unserialized into an empty object with numFiles 0, and that unserialize path was the bug72434 use-after-free vector. Add __serialize() and __unserialize() that throw, so the base class rejects (un)serialization and the UAF is closed by construction, while a subclass can still override both to round-trip through closeString()/openString(). Move the bug72434 test to ext/zip/tests since it now requires the zip extension. Fixes GH-21682 --- NEWS | 3 ++ ext/standard/tests/strings/bug72434.phpt | 29 ----------------- ext/zip/php_zip.c | 25 +++++++++++++++ ext/zip/php_zip.stub.php | 4 +++ ext/zip/php_zip_arginfo.h | 13 +++++++- ext/zip/tests/bug72434.phpt | 17 ++++++++++ ext/zip/tests/gh21682.phpt | 16 ++++++++++ ext/zip/tests/gh21682_subclass.phpt | 32 +++++++++++++++++++ .../tests/gh21682_subclass_no_overrides.phpt | 26 +++++++++++++++ 9 files changed, 135 insertions(+), 30 deletions(-) delete mode 100644 ext/standard/tests/strings/bug72434.phpt create mode 100644 ext/zip/tests/bug72434.phpt create mode 100644 ext/zip/tests/gh21682.phpt create mode 100644 ext/zip/tests/gh21682_subclass.phpt create mode 100644 ext/zip/tests/gh21682_subclass_no_overrides.phpt diff --git a/NEWS b/NEWS index 1fe33f473af6..f13f2e00ca4d 100644 --- a/NEWS +++ b/NEWS @@ -313,6 +313,9 @@ PHP NEWS . Added Uri\Rfc3986\UriBuilder. (kocsismate) - Zip: + . Fixed bug GH-21682 (ZipArchive instances should not be serializable). + serialize()/unserialize() now throw unless a subclass overrides + __serialize()/__unserialize(). (iliaal) . Fixed ZipArchive callback being called after executor has shut down. (ilutov) . Support minimum version for libzip dependency updated to 1.0.0. diff --git a/ext/standard/tests/strings/bug72434.phpt b/ext/standard/tests/strings/bug72434.phpt deleted file mode 100644 index 6d64baa26fa7..000000000000 --- a/ext/standard/tests/strings/bug72434.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -Bug #72434: ZipArchive class Use After Free Vulnerability in PHP's GC algorithm and unserialize ---FILE-- - rc is 0 -$a = $unserialized_payload[1]; -// Increment the reference counter by 1 again -> rc is 1 -$b = $a; -// Trigger free of $free_me (referenced by $m[1]). -unset($b); -$fill_freed_space_1 = "filler_zval_1"; -$fill_freed_space_2 = "filler_zval_2"; -$fill_freed_space_3 = "filler_zval_3"; -$fill_freed_space_4 = "filler_zval_4"; -debug_zval_dump($unserialized_payload[1]); -?> ---EXPECTF-- -array(1) refcount(3){ - [0]=> - object(stdClass)#%d (0) refcount(1){ - } -} diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 12344450678b..f231b3ad9ef2 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -25,6 +25,7 @@ #include "ext/standard/php_filestat.h" #include "zend_attributes.h" #include "zend_interfaces.h" +#include "zend_exceptions.h" #include "php_zip.h" #include "php_zip_arginfo.h" @@ -1645,6 +1646,30 @@ PHP_METHOD(ZipArchive, count) } /* }}} */ +PHP_METHOD(ZipArchive, __serialize) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zend_throw_exception_ex(NULL, 0, + "Serialization of '%s' is not allowed, override __serialize() and __unserialize() to implement it", + ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name)); +} + +PHP_METHOD(ZipArchive, __unserialize) +{ + zval *data; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(data) + ZEND_PARSE_PARAMETERS_END(); + + (void) data; + + zend_throw_exception_ex(NULL, 0, + "Unserialization of '%s' is not allowed, override __serialize() and __unserialize() to implement it", + ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name)); +} + /* {{{ clear the internal status */ PHP_METHOD(ZipArchive, clearError) { diff --git a/ext/zip/php_zip.stub.php b/ext/zip/php_zip.stub.php index 49dd19e53553..2cece2791d1b 100644 --- a/ext/zip/php_zip.stub.php +++ b/ext/zip/php_zip.stub.php @@ -661,6 +661,10 @@ public function closeString(): string|false {} /** @tentative-return-type */ public function count(): int {} + public function __serialize(): array {} + + public function __unserialize(array $data): void {} + /** @tentative-return-type */ public function getStatusString(): string {} diff --git a/ext/zip/php_zip_arginfo.h b/ext/zip/php_zip_arginfo.h index faa6feb1cb1e..4f8366dc1512 100644 --- a/ext/zip/php_zip_arginfo.h +++ b/ext/zip/php_zip_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit php_zip.stub.php instead. - * Stub hash: d623efdfe5ac46f07aebf8fb120050c818f3d793 */ + * Stub hash: 206d9be6640ee7e94d68d7e075ab61bf3188cab3 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_open, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) @@ -63,6 +63,13 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ZipArchive_count, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ZipArchive___serialize, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ZipArchive___unserialize, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, data, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ZipArchive_getStatusString, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -326,6 +333,8 @@ ZEND_METHOD(ZipArchive, setPassword); ZEND_METHOD(ZipArchive, close); ZEND_METHOD(ZipArchive, closeString); ZEND_METHOD(ZipArchive, count); +ZEND_METHOD(ZipArchive, __serialize); +ZEND_METHOD(ZipArchive, __unserialize); ZEND_METHOD(ZipArchive, getStatusString); ZEND_METHOD(ZipArchive, clearError); ZEND_METHOD(ZipArchive, addEmptyDir); @@ -406,6 +415,8 @@ static const zend_function_entry class_ZipArchive_methods[] = { ZEND_ME(ZipArchive, close, arginfo_class_ZipArchive_close, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, closeString, arginfo_class_ZipArchive_closeString, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, count, arginfo_class_ZipArchive_count, ZEND_ACC_PUBLIC) + ZEND_ME(ZipArchive, __serialize, arginfo_class_ZipArchive___serialize, ZEND_ACC_PUBLIC) + ZEND_ME(ZipArchive, __unserialize, arginfo_class_ZipArchive___unserialize, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, getStatusString, arginfo_class_ZipArchive_getStatusString, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, clearError, arginfo_class_ZipArchive_clearError, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, addEmptyDir, arginfo_class_ZipArchive_addEmptyDir, ZEND_ACC_PUBLIC) diff --git a/ext/zip/tests/bug72434.phpt b/ext/zip/tests/bug72434.phpt new file mode 100644 index 000000000000..e2ccebad3754 --- /dev/null +++ b/ext/zip/tests/bug72434.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #72434: ZipArchive class Use After Free Vulnerability in PHP's GC algorithm and unserialize +--EXTENSIONS-- +zip +--FILE-- +getMessage() . "\n"; +} +?> +--EXPECT-- +Unserialization of 'ZipArchive' is not allowed, override __serialize() and __unserialize() to implement it diff --git a/ext/zip/tests/gh21682.phpt b/ext/zip/tests/gh21682.phpt new file mode 100644 index 000000000000..ee09c73e5c4d --- /dev/null +++ b/ext/zip/tests/gh21682.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-21682 (ZipArchive serialization is rejected) +--EXTENSIONS-- +zip +--FILE-- +getMessage() . "\n"; +} +?> +--EXPECT-- +Serialization of 'ZipArchive' is not allowed, override __serialize() and __unserialize() to implement it diff --git a/ext/zip/tests/gh21682_subclass.phpt b/ext/zip/tests/gh21682_subclass.phpt new file mode 100644 index 000000000000..8c50dd4f3a24 --- /dev/null +++ b/ext/zip/tests/gh21682_subclass.phpt @@ -0,0 +1,32 @@ +--TEST-- +GH-21682 (ZipArchive subclass implements serialization via __serialize()/__unserialize()) +--EXTENSIONS-- +zip +--FILE-- + $this->closeString()]; + } + + public function __unserialize(array $data): void + { + $this->openString($data['data']); + } +} + +$zip = new MyArchive(); +$zip->openString(); +$zip->addFromString('test1', 'abc123'); + +$roundtrip = unserialize(serialize($zip)); +var_dump($roundtrip instanceof MyArchive); +var_dump($roundtrip->numFiles); +var_dump($roundtrip->getFromName('test1')); +?> +--EXPECT-- +bool(true) +int(1) +string(6) "abc123" diff --git a/ext/zip/tests/gh21682_subclass_no_overrides.phpt b/ext/zip/tests/gh21682_subclass_no_overrides.phpt new file mode 100644 index 000000000000..04fb49d9848e --- /dev/null +++ b/ext/zip/tests/gh21682_subclass_no_overrides.phpt @@ -0,0 +1,26 @@ +--TEST-- +GH-21682 (ZipArchive subclass without overrides inherits the serialization throw) +--EXTENSIONS-- +zip +--FILE-- +getMessage() . "\n"; +} + +try { + unserialize('O:5:"MyZip":0:{}'); + echo "ERROR: should have thrown\n"; +} catch (\Exception $e) { + echo $e->getMessage() . "\n"; +} +?> +--EXPECT-- +Serialization of 'MyZip' is not allowed, override __serialize() and __unserialize() to implement it +Unserialization of 'MyZip' is not allowed, override __serialize() and __unserialize() to implement it From 0fb3d658dc3264d3288353a641cce2c6adeba1bc Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Fri, 26 Jun 2026 06:42:53 -0400 Subject: [PATCH 4/9] ext/sodium: throw ValueError for pwhash argument errors (#22388) The four password-hashing functions reported out-of-range arguments (a non-positive or below-minimum opslimit or memlimit, an oversized hash length or password, a wrong-length salt) as a SodiumException. These are argument-value errors, so throw ValueError via zend_argument_value_error() instead, matching the rest of the engine. SodiumException is still used for internal libsodium failures. SodiumException's create_object empties the whole backtrace, which also protects caller frames holding the password; a plain ValueError does not, so each converted site keeps an explicit sodium_remove_param_values_from_backtrace(EG(exception)), mirroring the ZPP-failure paths. --- NEWS | 2 + UPGRADING | 9 +++ ext/sodium/libsodium.c | 69 ++++++++++++------- .../tests/pwhash_memlimit_below_min.phpt | 4 +- ext/sodium/tests/pwhash_valueerror_scrub.phpt | 25 +++++++ 5 files changed, 84 insertions(+), 25 deletions(-) create mode 100644 ext/sodium/tests/pwhash_valueerror_scrub.phpt diff --git a/NEWS b/NEWS index f13f2e00ca4d..9d38664136af 100644 --- a/NEWS +++ b/NEWS @@ -230,6 +230,8 @@ PHP NEWS - Sodium: . Added support for libsodium 1.0.21 IPcrypt and XOF APIs. (jedisct1) + . pwhash argument-validation errors now throw ValueError instead of + SodiumException. (iliaal) - SPL: . DirectoryIterator key can now work better with filesystem supporting larger diff --git a/UPGRADING b/UPGRADING index ae0e3afc97ad..58bf1f9c9a16 100644 --- a/UPGRADING +++ b/UPGRADING @@ -127,6 +127,15 @@ PHP 8.6 UPGRADE NOTES occurrence constraints and integer restriction facets. Negative minOccurs and maxOccurs values are rejected as well. +- Sodium: + . The password-hashing functions sodium_crypto_pwhash(), + sodium_crypto_pwhash_str(), + sodium_crypto_pwhash_scryptsalsa208sha256() and + sodium_crypto_pwhash_scryptsalsa208sha256_str() now throw ValueError + instead of SodiumException when an argument is out of range, such as an + opslimit or memlimit below the documented minimum. SodiumException is + still thrown for internal libsodium failures. + - SPL: . SplObjectStorage::getHash() implementations may no longer mutate any SplObjectStorage instance. Attempting to do so now throws an Error. diff --git a/ext/sodium/libsodium.c b/ext/sodium/libsodium.c index bd246abb53a5..8c85991150b5 100644 --- a/ext/sodium/libsodium.c +++ b/ext/sodium/libsodium.c @@ -1429,23 +1429,28 @@ PHP_FUNCTION(sodium_crypto_pwhash) RETURN_THROWS(); } if (hash_len <= 0) { - zend_argument_error(sodium_exception_ce, 1, "must be greater than 0"); + zend_argument_value_error(1, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (hash_len >= 0xffffffff) { - zend_argument_error(sodium_exception_ce, 1, "is too large"); + zend_argument_value_error(1, "must be less than 4294967295 bytes"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (passwd_len >= 0xffffffff) { - zend_argument_error(sodium_exception_ce, 2, "is too long"); + zend_argument_value_error(2, "must be less than 4294967295 bytes"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (opslimit <= 0) { - zend_argument_error(sodium_exception_ce, 4, "must be greater than 0"); + zend_argument_value_error(4, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (memlimit <= 0 || memlimit > SIZE_MAX) { - zend_argument_error(sodium_exception_ce, 5, "must be greater than 0"); + zend_argument_value_error(5, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (alg != crypto_pwhash_ALG_ARGON2I13 @@ -1460,15 +1465,18 @@ PHP_FUNCTION(sodium_crypto_pwhash) zend_error(E_WARNING, "empty password"); } if (salt_len != crypto_pwhash_SALTBYTES) { - zend_argument_error(sodium_exception_ce, 3, "must be SODIUM_CRYPTO_PWHASH_SALTBYTES bytes long"); + zend_argument_value_error(3, "must be SODIUM_CRYPTO_PWHASH_SALTBYTES bytes long"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (opslimit < crypto_pwhash_OPSLIMIT_MIN) { - zend_argument_error(sodium_exception_ce, 4, "must be greater than or equal to %d", crypto_pwhash_OPSLIMIT_MIN); + zend_argument_value_error(4, "must be greater than or equal to %d", crypto_pwhash_OPSLIMIT_MIN); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (memlimit < crypto_pwhash_MEMLIMIT_MIN) { - zend_argument_error(sodium_exception_ce, 5, "must be greater than or equal to %d", crypto_pwhash_MEMLIMIT_MIN); + zend_argument_value_error(5, "must be greater than or equal to %d", crypto_pwhash_MEMLIMIT_MIN); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } hash = zend_string_alloc((size_t) hash_len, 0); @@ -1513,26 +1521,31 @@ PHP_FUNCTION(sodium_crypto_pwhash_str) RETURN_THROWS(); } if (opslimit <= 0) { - zend_argument_error(sodium_exception_ce, 2, "must be greater than 0"); + zend_argument_value_error(2, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (memlimit <= 0 || memlimit > SIZE_MAX) { - zend_argument_error(sodium_exception_ce, 3, "must be greater than 0"); + zend_argument_value_error(3, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (passwd_len >= 0xffffffff) { - zend_argument_error(sodium_exception_ce, 1, "is too long"); + zend_argument_value_error(1, "must be less than 4294967295 bytes"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (passwd_len <= 0) { zend_error(E_WARNING, "empty password"); } if (opslimit < crypto_pwhash_OPSLIMIT_MIN) { - zend_argument_error(sodium_exception_ce, 2, "must be greater than or equal to %d", crypto_pwhash_OPSLIMIT_MIN); + zend_argument_value_error(2, "must be greater than or equal to %d", crypto_pwhash_OPSLIMIT_MIN); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (memlimit < crypto_pwhash_MEMLIMIT_MIN) { - zend_argument_error(sodium_exception_ce, 3, "must be greater than or equal to %d", crypto_pwhash_MEMLIMIT_MIN); + zend_argument_value_error(3, "must be greater than or equal to %d", crypto_pwhash_MEMLIMIT_MIN); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } hash_str = zend_string_alloc(crypto_pwhash_STRBYTES - 1, 0); @@ -1619,30 +1632,36 @@ PHP_FUNCTION(sodium_crypto_pwhash_scryptsalsa208sha256) RETURN_THROWS(); } if (hash_len <= 0 || hash_len >= ZSTR_MAX_LEN || hash_len > 0x1fffffffe0ULL) { - zend_argument_error(sodium_exception_ce, 1, "must be greater than 0"); + zend_argument_value_error(1, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (opslimit <= 0) { - zend_argument_error(sodium_exception_ce, 4, "must be greater than 0"); + zend_argument_value_error(4, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (memlimit <= 0 || memlimit > SIZE_MAX) { - zend_argument_error(sodium_exception_ce, 5, "must be greater than 0"); + zend_argument_value_error(5, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (passwd_len <= 0) { zend_error(E_WARNING, "empty password"); } if (salt_len != crypto_pwhash_scryptsalsa208sha256_SALTBYTES) { - zend_argument_error(sodium_exception_ce, 3, "must be SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_SALTBYTES bytes long"); + zend_argument_value_error(3, "must be SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_SALTBYTES bytes long"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (opslimit < crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE) { - zend_argument_error(sodium_exception_ce, 4, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE); + zend_argument_value_error(4, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (memlimit < crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) { - zend_argument_error(sodium_exception_ce, 5, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE); + zend_argument_value_error(5, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } hash = zend_string_alloc((size_t) hash_len, 0); @@ -1674,22 +1693,26 @@ PHP_FUNCTION(sodium_crypto_pwhash_scryptsalsa208sha256_str) RETURN_THROWS(); } if (opslimit <= 0) { - zend_argument_error(sodium_exception_ce, 2, "must be greater than 0"); + zend_argument_value_error(2, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (memlimit <= 0 || memlimit > SIZE_MAX) { - zend_argument_error(sodium_exception_ce, 3, "must be greater than 0"); + zend_argument_value_error(3, "must be greater than 0"); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (passwd_len <= 0) { zend_error(E_WARNING, "empty password"); } if (opslimit < crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE) { - zend_argument_error(sodium_exception_ce, 2, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE); + zend_argument_value_error(2, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } if (memlimit < crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) { - zend_argument_error(sodium_exception_ce, 3, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE); + zend_argument_value_error(3, "must be greater than or equal to %d", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE); + sodium_remove_param_values_from_backtrace(EG(exception)); RETURN_THROWS(); } hash_str = zend_string_alloc diff --git a/ext/sodium/tests/pwhash_memlimit_below_min.phpt b/ext/sodium/tests/pwhash_memlimit_below_min.phpt index 63bf4443939b..8913afb382f0 100644 --- a/ext/sodium/tests/pwhash_memlimit_below_min.phpt +++ b/ext/sodium/tests/pwhash_memlimit_below_min.phpt @@ -12,13 +12,13 @@ $salt = str_repeat("a", SODIUM_CRYPTO_PWHASH_SALTBYTES); try { sodium_crypto_pwhash(32, "password", $salt, SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, 1); -} catch (SodiumException $e) { +} catch (\ValueError $e) { echo $e->getMessage(), "\n"; } try { sodium_crypto_pwhash_str("password", SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, 1); -} catch (SodiumException $e) { +} catch (\ValueError $e) { echo $e->getMessage(), "\n"; } ?> diff --git a/ext/sodium/tests/pwhash_valueerror_scrub.phpt b/ext/sodium/tests/pwhash_valueerror_scrub.phpt new file mode 100644 index 000000000000..1cbd776e1839 --- /dev/null +++ b/ext/sodium/tests/pwhash_valueerror_scrub.phpt @@ -0,0 +1,25 @@ +--TEST-- +sodium pwhash argument errors throw ValueError and keep the whole backtrace scrubbed +--EXTENSIONS-- +sodium +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught ValueError: sodium_crypto_pwhash_str(): Argument #3 ($memlimit) must be greater than or equal to %d in %s:%d +Stack trace: +#0 %s(%d): sodium_crypto_pwhash_str() +#1 %s(%d): wrap() +#2 {main} + thrown in %s on line %d From 915318ed9c51e9f3e95566f9bf9e14f01744b74b Mon Sep 17 00:00:00 2001 From: henderkes Date: Thu, 11 Jun 2026 01:35:26 +0000 Subject: [PATCH 5/9] make all static extensions use TSRMG_STATIC Closes GH-22277 --- NEWS | 1 + UPGRADING | 2 +- build/php.m4 | 10 +++++++--- ext/date/config.w32 | 2 +- ext/date/config0.m4 | 2 +- ext/hash/config.m4 | 2 +- ext/hash/config.w32 | 2 +- ext/json/config.m4 | 3 +-- ext/json/config.w32 | 2 +- ext/lexbor/config.m4 | 2 +- ext/lexbor/config.w32 | 2 +- ext/opcache/config.m4 | 2 +- ext/opcache/config.w32 | 2 +- ext/pcre/config.w32 | 2 +- ext/pcre/config0.m4 | 4 +--- ext/random/config.m4 | 3 +-- ext/random/config.w32 | 2 +- ext/reflection/config.m4 | 3 +-- ext/reflection/config.w32 | 2 +- ext/spl/config.m4 | 3 +-- ext/spl/config.w32 | 2 +- ext/standard/config.m4 | 3 +-- ext/standard/config.w32 | 3 +-- ext/uri/config.m4 | 2 +- ext/uri/config.w32 | 2 +- win32/build/confutils.js | 3 +++ 26 files changed, 34 insertions(+), 34 deletions(-) diff --git a/NEWS b/NEWS index 9d38664136af..2462f0a73cc6 100644 --- a/NEWS +++ b/NEWS @@ -33,6 +33,7 @@ PHP NEWS . Fixed GH-22422 (zend_arena layout mismatch leaked memory in separately built extensions under AddressSanitizer). (iliaal) . TSRM: use local-exec TLS in PIE executables. (henderkes) + . perf: make all static extensions use TSRMG_STATIC. (henderkes) - BCMath: . Added NUL-byte validation to BCMath functions. (jorgsowa) diff --git a/UPGRADING b/UPGRADING index 58bf1f9c9a16..15c1aad15db0 100644 --- a/UPGRADING +++ b/UPGRADING @@ -490,7 +490,7 @@ PHP 8.6 UPGRADE NOTES . The performance of the TAILCALL VM has been improved. . The TAILCALL VM is now enabled on Windows when compiling with Clang >= 19 x86_64. - . The performance of ZTS+PIE builds has been improved. + . The performance of ZTS builds has been improved. - DOM: . Made splitText() faster and consume less memory. diff --git a/build/php.m4 b/build/php.m4 index 83375bec5aa2..6d9e3e21387b 100644 --- a/build/php.m4 +++ b/build/php.m4 @@ -938,10 +938,14 @@ AC_DEFUN([PHP_NEW_EXTENSION],[ ifelse($5,,ac_extra=,[ac_extra=$(echo "m4_normalize(m4_expand([$5]))"|$SED s#@ext_srcdir@#$ext_srcdir#g|$SED s#@ext_builddir@#$ext_builddir#g)]) + dnl Statically linked extensions share the engine's _tsrm_ls_cache symbol, + dnl so in ZTS builds they can read the TSRMLS cache directly. + ac_extra_static="$ac_extra -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1" + if test "$3" != "shared" && test "$3" != "yes" && test "$4" != "cli"; then dnl ---------------------------------------------- Static module [PHP_]translit($1,a-z_-,A-Z__)[_SHARED]=no - PHP_ADD_SOURCES($ext_dir,$2,$ac_extra,) + PHP_ADD_SOURCES($ext_dir,$2,$ac_extra_static,) EXT_STATIC="$EXT_STATIC $1;$ext_dir" if test "$3" != "nocli"; then EXT_CLI_STATIC="$EXT_CLI_STATIC $1;$ext_dir" @@ -962,11 +966,11 @@ dnl ---------------------------------------------- CLI static module [PHP_]translit($1,a-z_-,A-Z__)[_SHARED]=no case "$PHP_SAPI" in cgi|embed|phpdbg[)] - PHP_ADD_SOURCES($ext_dir,$2,$ac_extra,) + PHP_ADD_SOURCES($ext_dir,$2,$ac_extra_static,) EXT_STATIC="$EXT_STATIC $1;$ext_dir" ;; *[)] - PHP_ADD_SOURCES($ext_dir,$2,$ac_extra,cli) + PHP_ADD_SOURCES($ext_dir,$2,$ac_extra_static,cli) ;; esac EXT_CLI_STATIC="$EXT_CLI_STATIC $1;$ext_dir" diff --git a/ext/date/config.w32 b/ext/date/config.w32 index b053e27aae35..150fb1498f35 100644 --- a/ext/date/config.w32 +++ b/ext/date/config.w32 @@ -1,6 +1,6 @@ // vim:ft=javascript -EXTENSION("date", "php_date.c", false, "/Iext/date/lib /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 /DHAVE_TIMELIB_CONFIG_H=1"); +EXTENSION("date", "php_date.c", false, "/Iext/date/lib /DHAVE_TIMELIB_CONFIG_H=1"); PHP_DATE = "yes"; ADD_SOURCES("ext/date/lib", "astro.c timelib.c dow.c parse_date.c parse_posix.c parse_tz.c tm2unixtime.c unixtime2tm.c parse_iso_intervals.c interval.c", "date"); diff --git a/ext/date/config0.m4 b/ext/date/config0.m4 index c78fcb78e15e..5018f258ab2a 100644 --- a/ext/date/config0.m4 +++ b/ext/date/config0.m4 @@ -9,7 +9,7 @@ AX_CHECK_COMPILE_FLAG([-Wno-implicit-fallthrough], PHP_DATE_CFLAGS="$PHP_DATE_CFLAGS -DHAVE_TIMELIB_CONFIG_H=1" PHP_TIMELIB_CFLAGS="$PHP_DATE_CFLAGS" -PHP_DATE_CFLAGS="$PHP_DATE_CFLAGS -I@ext_builddir@/lib -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1" +PHP_DATE_CFLAGS="$PHP_DATE_CFLAGS -I@ext_builddir@/lib" AX_CHECK_COMPILE_FLAG([-fwrapv], [PHP_TIMELIB_CFLAGS="$PHP_TIMELIB_CFLAGS -fwrapv"]) diff --git a/ext/hash/config.m4 b/ext/hash/config.m4 index 2da44c503a62..72b7db8f9deb 100644 --- a/ext/hash/config.m4 +++ b/ext/hash/config.m4 @@ -57,7 +57,7 @@ PHP_NEW_EXTENSION([hash], m4_normalize([ murmur/PMurHash128.c ]), [no],, - [$PHP_HASH_CFLAGS -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) + [$PHP_HASH_CFLAGS]) PHP_ADD_BUILD_DIR([$ext_builddir/murmur]) AS_VAR_IF([SHA3_DIR],,, [PHP_ADD_BUILD_DIR([$ext_builddir/$SHA3_DIR])]) PHP_INSTALL_HEADERS([ext/hash], m4_normalize([ diff --git a/ext/hash/config.w32 b/ext/hash/config.w32 index e63efcfd84cb..347dbddea6b4 100644 --- a/ext/hash/config.w32 +++ b/ext/hash/config.w32 @@ -27,7 +27,7 @@ if (!CHECK_HEADER('KeccakHash.h', 'CFLAGS_HASH', hash_sha3_dir)) { ERROR('Unable to locate SHA3 headers'); } -ADD_FLAG('CFLAGS_HASH', '/DKeccakP200_excluded /DKeccakP400_excluded /DKeccakP800_excluded /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); +ADD_FLAG('CFLAGS_HASH', '/DKeccakP200_excluded /DKeccakP400_excluded /DKeccakP800_excluded'); ADD_SOURCES('ext/hash/murmur', 'PMurHash.c PMurHash128.c', 'hash'); diff --git a/ext/json/config.m4 b/ext/json/config.m4 index 5697dbff8d2f..a3ae54a7ac23 100644 --- a/ext/json/config.m4 +++ b/ext/json/config.m4 @@ -4,8 +4,7 @@ PHP_NEW_EXTENSION([json], m4_normalize([ json_scanner.c json.c ]), - [no],, - [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) + [no]) PHP_INSTALL_HEADERS([ext/json], m4_normalize([ php_json_parser.h php_json_scanner.h diff --git a/ext/json/config.w32 b/ext/json/config.w32 index 84f77b6f8c9c..9d9a37df08a8 100644 --- a/ext/json/config.w32 +++ b/ext/json/config.w32 @@ -1,6 +1,6 @@ // vim:ft=javascript -EXTENSION('json', 'json.c', false /* never shared */, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); +EXTENSION('json', 'json.c', false /* never shared */); PHP_JSON="yes"; ADD_SOURCES(configure_module_dirname, "json_encoder.c json_parser.tab.c json_scanner.c", "json"); diff --git a/ext/lexbor/config.m4 b/ext/lexbor/config.m4 index a75f490e77cc..43123578a727 100644 --- a/ext/lexbor/config.m4 +++ b/ext/lexbor/config.m4 @@ -192,7 +192,7 @@ PHP_NEW_EXTENSION([lexbor], m4_normalize([ $LEXBOR_DIR/url/url.c ]), [no],, - [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 $PHP_LEXBOR_CFLAGS]) + [$PHP_LEXBOR_CFLAGS]) PHP_ADD_BUILD_DIR([ $ext_builddir/ diff --git a/ext/lexbor/config.w32 b/ext/lexbor/config.w32 index e75798e06187..403c3b98afb5 100644 --- a/ext/lexbor/config.w32 +++ b/ext/lexbor/config.w32 @@ -1,6 +1,6 @@ // vim:ft=javascript -EXTENSION("lexbor", "php_lexbor.c", false, "/I " + configure_module_dirname + " /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); +EXTENSION("lexbor", "php_lexbor.c", false, "/I " + configure_module_dirname); PHP_LEXBOR="yes"; ADD_SOURCES("ext/lexbor/lexbor/ports/windows_nt/lexbor/core", "memory.c", "lexbor"); ADD_SOURCES("ext/lexbor/lexbor/core", "array_obj.c array.c avl.c bst.c diyfp.c conv.c dobject.c dtoa.c hash.c mem.c mraw.c plog.c print.c serialize.c shs.c str.c strtod.c", "lexbor"); diff --git a/ext/opcache/config.m4 b/ext/opcache/config.m4 index 70138726c56e..3798499a4511 100644 --- a/ext/opcache/config.m4 +++ b/ext/opcache/config.m4 @@ -339,7 +339,7 @@ PHP_NEW_EXTENSION([opcache], m4_normalize([ $ZEND_JIT_SRC ]), [no],, - [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 $JIT_CFLAGS],, + [$JIT_CFLAGS],, [yes]) PHP_ADD_EXTENSION_DEP(opcache, date) diff --git a/ext/opcache/config.w32 b/ext/opcache/config.w32 index 397fa1bdd87d..1ad346b4da31 100644 --- a/ext/opcache/config.w32 +++ b/ext/opcache/config.w32 @@ -14,7 +14,7 @@ ZEND_EXTENSION('opcache', "\ zend_persist_calc.c \ zend_file_cache.c \ zend_shared_alloc.c \ - shared_alloc_win32.c", false, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); + shared_alloc_win32.c", false); ADD_EXTENSION_DEP('opcache', 'date'); ADD_EXTENSION_DEP('opcache', 'hash'); diff --git a/ext/pcre/config.w32 b/ext/pcre/config.w32 index 7c09456b8961..93d9df201d51 100644 --- a/ext/pcre/config.w32 +++ b/ext/pcre/config.w32 @@ -1,7 +1,7 @@ // vim:ft=javascript EXTENSION("pcre", "php_pcre.c", false /* never shared */, - "-Iext/pcre/pcre2lib -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); + "-Iext/pcre/pcre2lib"); ADD_SOURCES("ext/pcre/pcre2lib", "pcre2_auto_possess.c pcre2_chartables.c pcre2_compile.c pcre2_config.c pcre2_context.c pcre2_chkdint.c pcre2_dfa_match.c pcre2_error.c pcre2_jit_compile.c pcre2_maketables.c pcre2_match.c pcre2_match_data.c pcre2_newline.c pcre2_ord2utf.c pcre2_pattern_info.c pcre2_serialize.c pcre2_string_utils.c pcre2_study.c pcre2_substitute.c pcre2_substring.c pcre2_tables.c pcre2_ucd.c pcre2_valid_utf.c pcre2_xclass.c pcre2_find_bracket.c pcre2_convert.c pcre2_extuni.c pcre2_script_run.c", "pcre"); ADD_DEF_FILE("ext\\pcre\\php_pcre.def"); diff --git a/ext/pcre/config0.m4 b/ext/pcre/config0.m4 index e71d9a795574..025b54eb7805 100644 --- a/ext/pcre/config0.m4 +++ b/ext/pcre/config0.m4 @@ -57,8 +57,7 @@ if test "$PHP_EXTERNAL_PCRE" != "no"; then PHP_NEW_EXTENSION([pcre], [php_pcre.c], - [no],, - [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) + [no]) PHP_INSTALL_HEADERS([ext/pcre], [php_pcre.h]) else AC_MSG_CHECKING([for PCRE library to use]) @@ -101,7 +100,6 @@ else $PHP_PCRE_CFLAGS -DHAVE_CONFIG_H -DHAVE_MEMMOVE - -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -I@ext_srcdir@/pcre2lib "]) diff --git a/ext/random/config.m4 b/ext/random/config.m4 index 484f0549e019..3561d502e708 100644 --- a/ext/random/config.m4 +++ b/ext/random/config.m4 @@ -32,8 +32,7 @@ PHP_NEW_EXTENSION([random], m4_normalize([ randomizer.c zend_utils.c ]), - [no],, - [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) + [no]) PHP_INSTALL_HEADERS([ext/random], m4_normalize([ php_random_csprng.h php_random_uint128.h diff --git a/ext/random/config.w32 b/ext/random/config.w32 index bb0badbd183d..fa6788803f0b 100644 --- a/ext/random/config.w32 +++ b/ext/random/config.w32 @@ -1,4 +1,4 @@ -EXTENSION("random", "random.c", false /* never shared */, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); +EXTENSION("random", "random.c", false /* never shared */); PHP_RANDOM="yes"; ADD_SOURCES(configure_module_dirname, "csprng.c engine_mt19937.c engine_pcgoneseq128xslrr64.c engine_xoshiro256starstar.c engine_secure.c engine_user.c gammasection.c randomizer.c zend_utils.c", "random"); PHP_INSTALL_HEADERS("ext/random", "php_random.h php_random_csprng.h php_random_uint128.h random_decl.h"); diff --git a/ext/reflection/config.m4 b/ext/reflection/config.m4 index 10ce256f01a8..cd287c140642 100644 --- a/ext/reflection/config.m4 +++ b/ext/reflection/config.m4 @@ -1,4 +1,3 @@ PHP_NEW_EXTENSION([reflection], [php_reflection.c], - [no],, - [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) + [no]) diff --git a/ext/reflection/config.w32 b/ext/reflection/config.w32 index 7f000b02ab63..f305095405d7 100644 --- a/ext/reflection/config.w32 +++ b/ext/reflection/config.w32 @@ -1,4 +1,4 @@ // vim:ft=javascript -EXTENSION("reflection", "php_reflection.c", false /* never shared */, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); +EXTENSION("reflection", "php_reflection.c", false /* never shared */); PHP_REFLECTION="yes"; diff --git a/ext/spl/config.m4 b/ext/spl/config.m4 index f15e124ba3f5..39e320049c3d 100644 --- a/ext/spl/config.m4 +++ b/ext/spl/config.m4 @@ -10,8 +10,7 @@ PHP_NEW_EXTENSION([spl], m4_normalize([ spl_iterators.c spl_observer.c ]), - [no],, - [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) + [no]) PHP_INSTALL_HEADERS([ext/spl], m4_normalize([ php_spl.h spl_array.h diff --git a/ext/spl/config.w32 b/ext/spl/config.w32 index 06e87c663357..4044e05ecb46 100644 --- a/ext/spl/config.w32 +++ b/ext/spl/config.w32 @@ -1,5 +1,5 @@ // vim:ft=javascript -EXTENSION("spl", "php_spl.c spl_functions.c spl_iterators.c spl_array.c spl_directory.c spl_exceptions.c spl_observer.c spl_dllist.c spl_heap.c spl_fixedarray.c", false /*never shared */, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); +EXTENSION("spl", "php_spl.c spl_functions.c spl_iterators.c spl_array.c spl_directory.c spl_exceptions.c spl_observer.c spl_dllist.c spl_heap.c spl_fixedarray.c", false /*never shared */); PHP_SPL="yes"; PHP_INSTALL_HEADERS("ext/spl", "php_spl.h spl_array.h spl_directory.h spl_exceptions.h spl_functions.h spl_iterators.h spl_observer.h spl_dllist.h spl_heap.h spl_fixedarray.h"); diff --git a/ext/standard/config.m4 b/ext/standard/config.m4 index 67c36b93ba34..7edfcf1da1f5 100644 --- a/ext/standard/config.m4 +++ b/ext/standard/config.m4 @@ -453,8 +453,7 @@ PHP_NEW_EXTENSION([standard], m4_normalize([ versioning.c $php_ext_standard_sources ]), - [no],, - [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) + [no]) PHP_ADD_BUILD_DIR([$ext_builddir/libavifinfo]) diff --git a/ext/standard/config.w32 b/ext/standard/config.w32 index 50031ac3b8ad..56096097620d 100644 --- a/ext/standard/config.w32 +++ b/ext/standard/config.w32 @@ -36,8 +36,7 @@ EXTENSION("standard", "array.c base64.c basic_functions.c browscap.c \ php_fopen_wrapper.c credits.c css.c var_unserializer.c ftok.c sha1.c \ user_filters.c uuencode.c filters.c proc_open.c password.c \ streamsfuncs.c http.c flock_compat.c hrtime.c", - false /* never shared */, - '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); + false /* never shared */); ADD_SOURCES("ext/standard/libavifinfo", "avifinfo.c", "standard"); PHP_STANDARD = "yes"; ADD_MAKEFILE_FRAGMENT(); diff --git a/ext/uri/config.m4 b/ext/uri/config.m4 index 31d6c0e10c84..a518cf84b3bd 100644 --- a/ext/uri/config.m4 +++ b/ext/uri/config.m4 @@ -39,7 +39,7 @@ else PHP_EVAL_INCLINE([$LIBURIPARSER_CFLAGS]) fi -PHP_NEW_EXTENSION(uri, [php_uri.c php_uri_common.c uri_parser_rfc3986.c uri_parser_whatwg.c uri_parser_php_parse_url.c $URIPARSER_SOURCES], [no],,[$URI_CFLAGS -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) +PHP_NEW_EXTENSION(uri, [php_uri.c php_uri_common.c uri_parser_rfc3986.c uri_parser_whatwg.c uri_parser_php_parse_url.c $URIPARSER_SOURCES], [no],,[$URI_CFLAGS]) PHP_ADD_EXTENSION_DEP(uri, lexbor) if test "$PHP_EXTERNAL_URIPARSER" = "no"; then diff --git a/ext/uri/config.w32 b/ext/uri/config.w32 index 2bed937a7377..597ffb9fa03a 100644 --- a/ext/uri/config.w32 +++ b/ext/uri/config.w32 @@ -1,4 +1,4 @@ -EXTENSION("uri", "php_uri.c php_uri_common.c uri_parser_rfc3986.c uri_parser_whatwg.c uri_parser_php_parse_url.c", false /* never shared */, "/I ext/lexbor /I ext/uri/uriparser/include /DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); +EXTENSION("uri", "php_uri.c php_uri_common.c uri_parser_rfc3986.c uri_parser_whatwg.c uri_parser_php_parse_url.c", false /* never shared */, "/I ext/lexbor /I ext/uri/uriparser/include"); AC_DEFINE("URI_ENABLE_ANSI", 1, "Define to 1 for enabling ANSI support of uriparser.") AC_DEFINE("URI_NO_UNICODE", 1, "Define to 1 for disabling unicode support of uriparser.") diff --git a/win32/build/confutils.js b/win32/build/confutils.js index b7289bbcb27a..3751101daa4b 100644 --- a/win32/build/confutils.js +++ b/win32/build/confutils.js @@ -1494,6 +1494,9 @@ function EXTENSION(extname, file_list, shared, cflags, dllname, obj_dir) ADD_FLAG("CFLAGS_PHP", "/D COMPILE_DL_" + EXT); } else { STDOUT.WriteLine("Enabling extension " + extname_for_printing); + /* Statically linked extensions share the engine's _tsrm_ls_cache symbol, + * so in ZTS builds they can read the TSRMLS cache directly. */ + cflags = "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 " + cflags; } MFO.WriteBlankLines(1); From 70384ffd4122a6b6ab98b4023389d47e24e6a23a Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 15 Apr 2026 11:41:39 +0200 Subject: [PATCH 6/9] Expose zend_reflection_property_set_raw_value, zend_reflection_property_set_raw_value_without_lazy_initialization Closes GH-21763 --- UPGRADING.INTERNALS | 4 ++ ext/reflection/php_reflection.c | 113 +++++++++++++++++++------------- ext/reflection/php_reflection.h | 21 ++++++ 3 files changed, 93 insertions(+), 45 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 2f0d016a99cd..a467df3b6be5 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -118,6 +118,10 @@ PHP 8.6 INTERNALS UPGRADE NOTES . Added ZEND_CONTAINER_OF(). . The OPENBASEDIR_CHECKPATH() compatibility macro has been removed, instead use php_check_open_basedir() directly. + . Added zend_reflection_property_set_raw_value_without_lazy_initialization(), + zend_reflection_property_set_raw_value() to expose the functionality of + ReflectionProperty::setRawValueWithoutLazyInitialization() and + ReflectionProperty::setRawValue() to C extensions. ======================== 2. Build system changes diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 00085968be79..6c50619a35d8 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6016,10 +6016,10 @@ ZEND_METHOD(ReflectionProperty, setValue) * property is overridden on 'object' and was not private in 'scope'. * The effective prop may add hooks or change flags. */ static zend_property_info *reflection_property_get_effective_prop( - property_reference *ref, zend_class_entry *scope, zend_object *object) { - zend_property_info *prop = ref->prop; + zend_property_info *prop, zend_string *unmangled_name, + zend_class_entry *scope, zend_object *object) { if (scope != object->ce && !(prop && (prop->flags & ZEND_ACC_PRIVATE))) { - prop = zend_hash_find_ptr(&object->ce->properties_info, ref->unmangled_name); + prop = zend_hash_find_ptr(&object->ce->properties_info, unmangled_name); } return prop; } @@ -6052,8 +6052,8 @@ ZEND_METHOD(ReflectionProperty, getRawValue) } } - zend_property_info *prop = reflection_property_get_effective_prop(ref, - intern->ce, Z_OBJ_P(object)); + zend_property_info *prop = reflection_property_get_effective_prop(ref->prop, + ref->unmangled_name, intern->ce, Z_OBJ_P(object)); if (UNEXPECTED(prop && (prop->flags & ZEND_ACC_STATIC))) { zend_throw_exception(reflection_exception_ptr, "May not use getRawValue on static properties", 0); @@ -6083,13 +6083,15 @@ ZEND_METHOD(ReflectionProperty, getRawValue) } } -static void reflection_property_set_raw_value(zend_property_info *prop, - zend_string *unmangled_name, void *cache_slot[3], reflection_object *intern, - zend_object *object, zval *value) +static void zend_reflection_property_set_raw_value_ex(zend_property_info *prop, + zend_string *unmangled_name, void *cache_slot[3], + zend_class_entry *scope, zend_object *object, zval *value) { + ZEND_ASSERT(!prop || !(prop->flags & ZEND_ACC_STATIC)); + if (!prop || !prop->hooks || !prop->hooks[ZEND_PROPERTY_HOOK_SET]) { const zend_class_entry *old_scope = EG(fake_scope); - EG(fake_scope) = intern->ce; + EG(fake_scope) = scope; object->handlers->write_property(object, unmangled_name, value, cache_slot); EG(fake_scope) = old_scope; } else { @@ -6098,6 +6100,22 @@ static void reflection_property_set_raw_value(zend_property_info *prop, } } +PHPAPI void zend_reflection_property_set_raw_value(zend_property_info *prop, + zend_string *unmangled_name, void *cache_slot[3], + zend_class_entry *scope, zend_object *object, zval *value) +{ + prop = reflection_property_get_effective_prop(prop, + unmangled_name, scope, object); + + if (UNEXPECTED(prop && (prop->flags & ZEND_ACC_STATIC))) { + zend_throw_exception(reflection_exception_ptr, "May not use setRawValue on static properties", 0); + return; + } + + zend_reflection_property_set_raw_value_ex(prop, unmangled_name, cache_slot, + scope, object, value); +} + ZEND_METHOD(ReflectionProperty, setRawValue) { reflection_object *intern; @@ -6112,26 +6130,18 @@ ZEND_METHOD(ReflectionProperty, setRawValue) Z_PARAM_ZVAL(value) } ZEND_PARSE_PARAMETERS_END(); - zend_property_info *prop = reflection_property_get_effective_prop(ref, - intern->ce, Z_OBJ_P(object)); - - if (UNEXPECTED(prop && (prop->flags & ZEND_ACC_STATIC))) { - zend_throw_exception(reflection_exception_ptr, "May not use setRawValue on static properties", 0); - RETURN_THROWS(); - } - - reflection_property_set_raw_value(prop, ref->unmangled_name, - ref->cache_slot, intern, Z_OBJ_P(object), value); + zend_reflection_property_set_raw_value(ref->prop, ref->unmangled_name, + ref->cache_slot, intern->ce, Z_OBJ_P(object), value); } static zend_result reflection_property_check_lazy_compatible( zend_property_info *prop, zend_string *unmangled_name, - reflection_object *intern, zend_object *object, const char *method) + zend_class_entry *scope, zend_object *object, const char *method) { if (!prop) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Can not use %s on dynamic property %s::$%s", - method, ZSTR_VAL(intern->ce->name), + method, ZSTR_VAL(scope->name), ZSTR_VAL(unmangled_name)); return FAILURE; } @@ -6166,32 +6176,23 @@ static zend_result reflection_property_check_lazy_compatible( return SUCCESS; } -/* {{{ Set property value without triggering initializer while skipping hooks if any */ -ZEND_METHOD(ReflectionProperty, setRawValueWithoutLazyInitialization) +PHPAPI void zend_reflection_property_set_raw_value_without_lazy_initialization( + zend_property_info *prop, zend_string *unmangled_name, + void *cache_slot[3], zend_class_entry *scope, + zend_object *object, zval *value) { - reflection_object *intern; - property_reference *ref; - zend_object *object; - zval *value; - - GET_REFLECTION_OBJECT_PTR(ref); - - ZEND_PARSE_PARAMETERS_START(2, 2) { - Z_PARAM_OBJ_OF_CLASS(object, intern->ce) - Z_PARAM_ZVAL(value) - } ZEND_PARSE_PARAMETERS_END(); - while (zend_object_is_lazy_proxy(object) && zend_lazy_object_initialized(object)) { object = zend_lazy_object_get_instance(object); } - zend_property_info *prop = reflection_property_get_effective_prop(ref, - intern->ce, object); + prop = reflection_property_get_effective_prop(prop, + unmangled_name, scope, object); - if (reflection_property_check_lazy_compatible(prop, ref->unmangled_name, - intern, object, "setRawValueWithoutLazyInitialization") == FAILURE) { - RETURN_THROWS(); + if (reflection_property_check_lazy_compatible(prop, unmangled_name, + scope, object, "setRawValueWithoutLazyInitialization") == FAILURE) { + ZEND_ASSERT(EG(exception)); + return; } zval *var_ptr = OBJ_PROP(object, prop->offset); @@ -6200,8 +6201,8 @@ ZEND_METHOD(ReflectionProperty, setRawValueWithoutLazyInitialization) /* Do not trigger initialization */ Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_LAZY; - reflection_property_set_raw_value(prop, ref->unmangled_name, - ref->cache_slot, intern, object, value); + zend_reflection_property_set_raw_value_ex(prop, unmangled_name, + cache_slot, scope, object, value); /* Mark property as lazy again if an exception prevented update */ if (EG(exception) && prop_was_lazy && Z_TYPE_P(var_ptr) == IS_UNDEF @@ -6220,6 +6221,26 @@ ZEND_METHOD(ReflectionProperty, setRawValueWithoutLazyInitialization) } } +/* {{{ Set property value without triggering initializer while skipping hooks if any */ +ZEND_METHOD(ReflectionProperty, setRawValueWithoutLazyInitialization) +{ + reflection_object *intern; + property_reference *ref; + zend_object *object; + zval *value; + + GET_REFLECTION_OBJECT_PTR(ref); + + ZEND_PARSE_PARAMETERS_START(2, 2) { + Z_PARAM_OBJ_OF_CLASS(object, intern->ce) + Z_PARAM_ZVAL(value) + } ZEND_PARSE_PARAMETERS_END(); + + zend_reflection_property_set_raw_value_without_lazy_initialization( + ref->prop, ref->unmangled_name, ref->cache_slot, intern->ce, + object, value); +} + /* {{{ Mark property as non-lazy, and initialize to default value */ ZEND_METHOD(ReflectionProperty, skipLazyInitialization) { @@ -6234,7 +6255,7 @@ ZEND_METHOD(ReflectionProperty, skipLazyInitialization) } ZEND_PARSE_PARAMETERS_END(); if (reflection_property_check_lazy_compatible(ref->prop, - ref->unmangled_name, intern, object, + ref->unmangled_name, intern->ce, object, "skipLazyInitialization") == FAILURE) { RETURN_THROWS(); } @@ -6715,7 +6736,8 @@ ZEND_METHOD(ReflectionProperty, isReadable) zend_throw_exception(reflection_exception_ptr, "Given object is not an instance of the class this property was declared in", 0); RETURN_THROWS(); } - prop = reflection_property_get_effective_prop(ref, intern->ce, obj); + prop = reflection_property_get_effective_prop(ref->prop, + ref->unmangled_name, intern->ce, obj); } zend_class_entry *ce = obj ? obj->ce : intern->ce; @@ -6826,7 +6848,8 @@ ZEND_METHOD(ReflectionProperty, isWritable) zend_throw_exception(reflection_exception_ptr, "Given object is not an instance of the class this property was declared in", 0); RETURN_THROWS(); } - prop = reflection_property_get_effective_prop(ref, intern->ce, obj); + prop = reflection_property_get_effective_prop(ref->prop, + ref->unmangled_name, intern->ce, obj); } zend_class_entry *ce = obj ? obj->ce : intern->ce; diff --git a/ext/reflection/php_reflection.h b/ext/reflection/php_reflection.h index ba03f1d9ac2d..51f48b8039cc 100644 --- a/ext/reflection/php_reflection.h +++ b/ext/reflection/php_reflection.h @@ -50,6 +50,27 @@ extern PHPAPI zend_class_entry *reflection_lazy_object_ptr; PHPAPI void zend_reflection_class_factory(zend_class_entry *ce, zval *object); +/* Sets the value of a property, bypassing a set hook if defined. + * 'prop': The property to set + * 'unmangled_name': The name of the property + * 'cache_slot': An opaque pointer used as an internal cache. The same + * cache_slot can be used again with the same 'unmangled_name' and 'scope'. + * Must be zeroed on first use. May be NULL. + * 'scope': The scope from which to set the property + * 'object': The object to set the value on + * 'value': The value to set + */ +PHPAPI void zend_reflection_property_set_raw_value( + zend_property_info *prop, zend_string *unmangled_name, + void *cache_slot[3], zend_class_entry *scope, + zend_object *object, zval *value); + +/* Same as zend_reflection_property_set_raw_value(), but skips lazy object initialization. */ +PHPAPI void zend_reflection_property_set_raw_value_without_lazy_initialization( + zend_property_info *prop, zend_string *unmangled_name, + void *cache_slot[3], zend_class_entry *scope, + zend_object *object, zval *value); + END_EXTERN_C() #endif /* PHP_REFLECTION_H */ From 57096b612d1f5636b89592b0092cc477c94226c7 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 9 Jun 2026 16:02:00 +0100 Subject: [PATCH 7/9] Zend: Fix GH-22257 type confusion in Exception::getTraceAsString(). A crafted, deliberately truncated unserialize() payload can leave Exception::$trace holding a non-array value, since the typed-property check is skipped on the parse failure path. getTraceAsString() then reinterpreted the object as a HashTable, causing an out-of-bounds read. Guard against a non-array trace and return an empty string instead. Fix #22257 close GH-22263 --- NEWS | 2 ++ Zend/tests/gh22257.phpt | 38 ++++++++++++++++++++++++++++++++ ext/standard/var_unserializer.re | 24 +++++++++++++++----- 3 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 Zend/tests/gh22257.phpt diff --git a/NEWS b/NEWS index 2462f0a73cc6..9df461452c1c 100644 --- a/NEWS +++ b/NEWS @@ -34,6 +34,8 @@ PHP NEWS built extensions under AddressSanitizer). (iliaal) . TSRM: use local-exec TLS in PIE executables. (henderkes) . perf: make all static extensions use TSRMG_STATIC. (henderkes) + . Fixed bug GH-22257 (type confusion in Exception::getTraceAsString()). + (David Carlier) - BCMath: . Added NUL-byte validation to BCMath functions. (jorgsowa) diff --git a/Zend/tests/gh22257.phpt b/Zend/tests/gh22257.phpt new file mode 100644 index 000000000000..9bbf3b1f8d35 --- /dev/null +++ b/Zend/tests/gh22257.phpt @@ -0,0 +1,38 @@ +--TEST-- +GH-22257 (Type confusion / OOB read unserializing an Exception with a non-array trace) +--CREDITS-- +Igor Sak-Sakovskiy (Positive Technologies) +--FILE-- +getPrevious()) { + printf("%s: %s\n", $e::class, $e->getMessage()); + } +} + +/* By-ref type violation: the slot is reset to the property default. */ +class Test { public int $i; public array $a; } +try { + var_dump(unserialize('O:4:"Test":2:{s:1:"i";N;s:1:"a";R:2;}')); +} catch (\Throwable $e) { + printf("%s: %s\n", $e::class, $e->getMessage()); +} +echo "OK\n"; +?> +--EXPECTF-- +Warning: unserialize(): Error at offset %d of %d bytes in %s on line %d +TypeError: Cannot assign null to property Test::$i of type int +OK diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 484cb5aa8fc9..4a9b278c116c 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -152,6 +152,17 @@ static zend_never_inline void var_push_dtor_value(php_unserialize_data_t *var_ha } } +static zend_always_inline void var_restore_prop_default(php_unserialize_data_t *var_hash, zend_object *obj, zend_property_info *info, zval *data) +{ + /* A partially/incorrectly unserialized value may violate the property's + * declared type, so restore the default and keep the slot consistent. */ + zval *tmp = &obj->ce->default_properties_table[OBJ_PROP_TO_NUM(info->offset)]; + if (Z_REFCOUNTED_P(data)) { + var_push_dtor_value(var_hash, data); + } + ZVAL_COPY_OR_DUP_PROP(data, tmp); +} + static zend_always_inline zval *tmp_var(php_unserialize_data_t *var_hashx, zend_long num) { var_dtor_entries *var_hash; @@ -677,18 +688,19 @@ second_try: } if (!php_var_unserialize_internal(data, p, max, var_hash)) { - if (info && Z_ISREF_P(data)) { - /* Add type source even if we failed to unserialize. - * The data is still stored in the property. */ - ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(data), info); + if (info) { + if (Z_ISREF_P(data)) { + ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(data), info); + } else { + var_restore_prop_default(var_hash, obj, info, data); + } } goto failure; } if (UNEXPECTED(info)) { if (!zend_verify_prop_assignable_by_ref(info, data, /* strict */ 1)) { - zval_ptr_dtor(data); - ZVAL_UNDEF(data); + var_restore_prop_default(var_hash, obj, info, data); goto failure; } From cff33786f6007b0868db7931b45c7c312275f187 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 26 Jun 2026 20:34:56 +0800 Subject: [PATCH 8/9] ext/intl: add const qualifiers (#22465) Add several const qualifiers. --- .../breakiterator/breakiterator_iterators.cpp | 4 +-- .../breakiterator/breakiterator_methods.cpp | 8 +++--- ext/intl/calendar/calendar_class.cpp | 2 +- ext/intl/calendar/calendar_methods.cpp | 28 +++++++++---------- ext/intl/converter/converter.cpp | 6 ++-- .../dateformat/dateformat_format_object.cpp | 6 ++-- ext/intl/dateformat/dateformat_parse.cpp | 14 +++++----- ext/intl/formatter/formatter_attr.cpp | 2 +- ext/intl/formatter/formatter_parse.cpp | 2 +- ext/intl/intl_error.c | 2 +- .../listformatter/listformatter_class.cpp | 2 +- ext/intl/locale/locale_methods.cpp | 4 +-- ext/intl/msgformat/msgformat_helpers.cpp | 10 +++---- ext/intl/msgformat/msgformat_parse.cpp | 2 +- .../rangeformatter/rangeformatter_class.cpp | 2 +- .../resourcebundle/resourcebundle_class.cpp | 2 +- ext/intl/timezone/timezone_class.cpp | 2 +- ext/intl/timezone/timezone_methods.cpp | 4 +-- .../transliterator/transliterator_methods.cpp | 2 +- ext/intl/uchar/uchar.cpp | 4 +-- 20 files changed, 54 insertions(+), 54 deletions(-) diff --git a/ext/intl/breakiterator/breakiterator_iterators.cpp b/ext/intl/breakiterator/breakiterator_iterators.cpp index 805057489013..d792a6367139 100644 --- a/ext/intl/breakiterator/breakiterator_iterators.cpp +++ b/ext/intl/breakiterator/breakiterator_iterators.cpp @@ -65,7 +65,7 @@ static void _breakiterator_move_forward(zend_object_iterator *iter) return; } - int32_t pos = biter->next(); + const int32_t pos = biter->next(); if (pos != BreakIterator::DONE) { ZVAL_LONG(&zoi_iter->current, (zend_long)pos); } //else we've reached the end of the enum, nothing more is required @@ -76,7 +76,7 @@ static void _breakiterator_rewind(zend_object_iterator *iter) BreakIterator *biter = _breakiter_prolog(iter); zoi_with_current *zoi_iter = (zoi_with_current*)iter; - int32_t pos = biter->first(); + const int32_t pos = biter->first(); ZVAL_LONG(&zoi_iter->current, (zend_long)pos); } diff --git a/ext/intl/breakiterator/breakiterator_methods.cpp b/ext/intl/breakiterator/breakiterator_methods.cpp index 66a35e65ac6a..60659915bf85 100644 --- a/ext/intl/breakiterator/breakiterator_methods.cpp +++ b/ext/intl/breakiterator/breakiterator_methods.cpp @@ -171,7 +171,7 @@ static void _breakiter_no_args_ret_int32( BREAKITER_METHOD_FETCH_OBJECT; - int32_t res = (bio->biter->*func)(); + const int32_t res = (bio->biter->*func)(); RETURN_LONG((zend_long)res); } @@ -195,7 +195,7 @@ static void _breakiter_int32_ret_int32( RETURN_THROWS(); } - int32_t res = (bio->biter->*func)((int32_t)arg); + const int32_t res = (bio->biter->*func)((int32_t)arg); RETURN_LONG((zend_long)res); } @@ -246,7 +246,7 @@ U_CFUNC PHP_METHOD(IntlBreakIterator, current) BREAKITER_METHOD_FETCH_OBJECT; - int32_t res = bio->biter->current(); + const int32_t res = bio->biter->current(); RETURN_LONG((zend_long)res); } @@ -282,7 +282,7 @@ U_CFUNC PHP_METHOD(IntlBreakIterator, isBoundary) BREAKITER_METHOD_FETCH_OBJECT; - UBool res = bio->biter->isBoundary((int32_t)offset); + const UBool res = bio->biter->isBoundary((int32_t)offset); RETURN_BOOL((zend_long)res); } diff --git a/ext/intl/calendar/calendar_class.cpp b/ext/intl/calendar/calendar_class.cpp index 63b203d08d44..c74f0ab9412f 100644 --- a/ext/intl/calendar/calendar_class.cpp +++ b/ext/intl/calendar/calendar_class.cpp @@ -192,7 +192,7 @@ static HashTable *Calendar_get_debug_info(zend_object *object, int *is_temp) i++) { UErrorCode uec = U_ZERO_ERROR; const char *name = debug_info_fields[i].name; - int32_t res = cal->get(debug_info_fields[i].field, uec); + const int32_t res = cal->get(debug_info_fields[i].field, uec); if (U_SUCCESS(uec)) { add_assoc_long(&zfields, name, (zend_long)res); } else { diff --git a/ext/intl/calendar/calendar_methods.cpp b/ext/intl/calendar/calendar_methods.cpp index fe4749d6d62d..fcb2e56bb897 100644 --- a/ext/intl/calendar/calendar_methods.cpp +++ b/ext/intl/calendar/calendar_methods.cpp @@ -226,7 +226,7 @@ static void _php_intlcal_field_uec_ret_in32t_method( CALENDAR_METHOD_FETCH_OBJECT; - int32_t result = (co->ucal->*func)( + const int32_t result = (co->ucal->*func)( (UCalendarDateFields)field, CALENDAR_ERROR_CODE(co)); INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed"); @@ -250,7 +250,7 @@ U_CFUNC PHP_FUNCTION(intlcal_get_time) CALENDAR_METHOD_FETCH_OBJECT; - UDate result = co->ucal->getTime(CALENDAR_ERROR_CODE(co)); + const UDate result = co->ucal->getTime(CALENDAR_ERROR_CODE(co)); INTL_METHOD_CHECK_STATUS(co, "error calling ICU Calendar::getTime"); RETURN_DOUBLE((double)result); @@ -377,7 +377,7 @@ static void _php_intlcal_before_after( RETURN_THROWS(); } - UBool res = (co->ucal->*func)(*when_co->ucal, CALENDAR_ERROR_CODE(co)); + const UBool res = (co->ucal->*func)(*when_co->ucal, CALENDAR_ERROR_CODE(co)); INTL_METHOD_CHECK_STATUS(co, "Error calling ICU method"); RETURN_BOOL((int)res); @@ -401,7 +401,7 @@ U_CFUNC PHP_FUNCTION(intlcal_set) object = getThis(); - int arg_num = ZEND_NUM_ARGS() - (object ? 0 : 1); + const int arg_num = ZEND_NUM_ARGS() - (object ? 0 : 1); if (object && arg_num > 2) { zend_error(E_DEPRECATED, "Calling IntlCalendar::set() with more than 2 arguments is deprecated, " @@ -564,7 +564,7 @@ U_CFUNC PHP_FUNCTION(intlcal_field_difference) CALENDAR_METHOD_FETCH_OBJECT; - int32_t result = co->ucal->fieldDifference((UDate)when, + const int32_t result = co->ucal->fieldDifference((UDate)when, (UCalendarDateFields)field, CALENDAR_ERROR_CODE(co)); INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed"); @@ -597,7 +597,7 @@ U_CFUNC PHP_FUNCTION(intlcal_get_day_of_week_type) CALENDAR_METHOD_FETCH_OBJECT; - int32_t result = co->ucal->getDayOfWeekType( + const int32_t result = co->ucal->getDayOfWeekType( (UCalendarDaysOfWeek)dow, CALENDAR_ERROR_CODE(co)); INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed"); @@ -615,7 +615,7 @@ U_CFUNC PHP_FUNCTION(intlcal_get_first_day_of_week) CALENDAR_METHOD_FETCH_OBJECT; - int32_t result = co->ucal->getFirstDayOfWeek(CALENDAR_ERROR_CODE(co)); + const int32_t result = co->ucal->getFirstDayOfWeek(CALENDAR_ERROR_CODE(co)); INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed"); RETURN_LONG((zend_long)result); @@ -637,7 +637,7 @@ static void _php_intlcal_field_ret_in32t_method( CALENDAR_METHOD_FETCH_OBJECT; - int32_t result = (co->ucal->*func)((UCalendarDateFields)field); + const int32_t result = (co->ucal->*func)((UCalendarDateFields)field); INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed"); RETURN_LONG((zend_long)result); @@ -696,7 +696,7 @@ U_CFUNC PHP_FUNCTION(intlcal_get_minimal_days_in_first_week) CALENDAR_METHOD_FETCH_OBJECT; - uint8_t result = co->ucal->getMinimalDaysInFirstWeek(); + const uint8_t result = co->ucal->getMinimalDaysInFirstWeek(); /* TODO Is it really a failure? */ INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed"); @@ -758,7 +758,7 @@ U_CFUNC PHP_FUNCTION(intlcal_get_weekend_transition) CALENDAR_METHOD_FETCH_OBJECT; - int32_t res = co->ucal->getWeekendTransition((UCalendarDaysOfWeek)dow, + const int32_t res = co->ucal->getWeekendTransition((UCalendarDaysOfWeek)dow, CALENDAR_ERROR_CODE(co)); INTL_METHOD_CHECK_STATUS(co, "Error calling ICU method"); @@ -776,7 +776,7 @@ U_CFUNC PHP_FUNCTION(intlcal_in_daylight_time) CALENDAR_METHOD_FETCH_OBJECT; - UBool ret = co->ucal->inDaylightTime(CALENDAR_ERROR_CODE(co)); + const UBool ret = co->ucal->inDaylightTime(CALENDAR_ERROR_CODE(co)); INTL_METHOD_CHECK_STATUS(co, "Error calling ICU method"); RETURN_BOOL((int)ret); @@ -852,7 +852,7 @@ U_CFUNC PHP_FUNCTION(intlcal_is_weekend) if (date_is_null) { RETURN_BOOL((int)co->ucal->isWeekend()); } else { - UBool ret = co->ucal->isWeekend((UDate)date, CALENDAR_ERROR_CODE(co)); + const UBool ret = co->ucal->isWeekend((UDate)date, CALENDAR_ERROR_CODE(co)); INTL_METHOD_CHECK_STATUS(co, "Error calling ICU method"); RETURN_BOOL((int)ret); } @@ -937,7 +937,7 @@ U_CFUNC PHP_FUNCTION(intlcal_equals) RETURN_THROWS(); } - UBool result = co->ucal->equals(*other_co->ucal, CALENDAR_ERROR_CODE(co)); + const UBool result = co->ucal->equals(*other_co->ucal, CALENDAR_ERROR_CODE(co)); INTL_METHOD_CHECK_STATUS(co, "error calling ICU Calendar::equals"); RETURN_BOOL((int)result); @@ -1116,7 +1116,7 @@ U_CFUNC PHP_FUNCTION(intlcal_to_date_time) /* There are no exported functions in ext/date to this * in a more native fashion */ - double date = co->ucal->getTime(CALENDAR_ERROR_CODE(co)) / 1000.; + const double date = co->ucal->getTime(CALENDAR_ERROR_CODE(co)) / 1000.; int64_t ts; char ts_str[sizeof("@-9223372036854775808")]; int ts_str_len; diff --git a/ext/intl/converter/converter.cpp b/ext/intl/converter/converter.cpp index 88f05136bbbd..30855d1b0c58 100644 --- a/ext/intl/converter/converter.cpp +++ b/ext/intl/converter/converter.cpp @@ -159,7 +159,7 @@ static void php_converter_append_toUnicode_target(zval *val, UConverterToUnicode return; case IS_LONG: { - zend_long lval = Z_LVAL_P(val); + const zend_long lval = Z_LVAL_P(val); if ((lval < 0) || (lval > 0x10FFFF)) { php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR, "Invalid codepoint U+%04lx", lval); return; @@ -265,7 +265,7 @@ static void php_converter_append_fromUnicode_target(zval *val, UConverterFromUni return; case IS_STRING: { - size_t vallen = Z_STRLEN_P(val); + const size_t vallen = Z_STRLEN_P(val); if (TARGET_CHECK(args, vallen)) { args->target = reinterpret_cast(zend_mempcpy(args->target, Z_STRVAL_P(val), vallen)); } @@ -682,7 +682,7 @@ static zend_string* php_converter_do_convert(UConverter *dest_cnv, } /* }}} */ -static void php_converter_set_subst_chars(UConverter *cnv, zend_string *subst, UErrorCode *error) +static void php_converter_set_subst_chars(UConverter *cnv, const zend_string *subst, UErrorCode *error) { if (ZSTR_LEN(subst) > SCHAR_MAX) { *error = U_ILLEGAL_ARGUMENT_ERROR; diff --git a/ext/intl/dateformat/dateformat_format_object.cpp b/ext/intl/dateformat/dateformat_format_object.cpp index 5b0950094d51..52199a305f0f 100644 --- a/ext/intl/dateformat/dateformat_format_object.cpp +++ b/ext/intl/dateformat/dateformat_format_object.cpp @@ -50,9 +50,9 @@ static constexpr DateFormat::EStyle valid_styles[] = { DateFormat::kShortRelative, }; -static bool valid_format(zval *z) { +static bool valid_format(const zval *z) { if (Z_TYPE_P(z) == IS_LONG) { - zend_long lval = Z_LVAL_P(z); + const zend_long lval = Z_LVAL_P(z); for (int i = 0; i < sizeof(valid_styles) / sizeof(*valid_styles); i++) { if ((zend_long)valid_styles[i] == lval) { return true; @@ -149,7 +149,7 @@ U_CFUNC PHP_FUNCTION(datefmt_format_object) timeStyle = (DateFormat::EStyle)(timeStyle & ~DateFormat::kRelative); } - zend_class_entry *instance_ce = object->ce; + const zend_class_entry *instance_ce = object->ce; if (instanceof_function(instance_ce, Calendar_ce_ptr)) { Calendar *obj_cal = calendar_fetch_native_calendar(object); if (obj_cal == NULL) { diff --git a/ext/intl/dateformat/dateformat_parse.cpp b/ext/intl/dateformat/dateformat_parse.cpp index 13cf56ad7d82..d818627439e3 100644 --- a/ext/intl/dateformat/dateformat_parse.cpp +++ b/ext/intl/dateformat/dateformat_parse.cpp @@ -33,7 +33,7 @@ extern "C" { * if set to 1 - store any error encountered in the parameter parse_error * if set to 0 - no need to store any error encountered in the parameter parse_error */ -static void internal_parse_to_timestamp(IntlDateFormatter_object *dfo, char* text_to_parse, size_t text_len, int32_t *parse_pos, bool update_calendar, zval *return_value) +static void internal_parse_to_timestamp(IntlDateFormatter_object *dfo, const char* text_to_parse, size_t text_len, int32_t *parse_pos, bool update_calendar, zval *return_value) { double result = 0; UDate timestamp =0; @@ -70,9 +70,9 @@ static void internal_parse_to_timestamp(IntlDateFormatter_object *dfo, char* tex } /* }}} */ -static void add_to_localtime_arr( IntlDateFormatter_object *dfo, zval* return_value, const UCalendar *parsed_calendar, zend_long calendar_field, char* key_name) +static void add_to_localtime_arr( IntlDateFormatter_object *dfo, zval* return_value, const UCalendar *parsed_calendar, zend_long calendar_field, const char* key_name) { - zend_long calendar_field_val = ucal_get( parsed_calendar, static_cast(calendar_field), &INTL_DATA_ERROR_CODE(dfo)); + const zend_long calendar_field_val = ucal_get( parsed_calendar, static_cast(calendar_field), &INTL_DATA_ERROR_CODE(dfo)); INTL_METHOD_CHECK_STATUS( dfo, "Date parsing - localtime failed : could not get a field from calendar" ); if( strcmp(key_name, CALENDAR_YEAR )==0 ){ @@ -87,7 +87,7 @@ static void add_to_localtime_arr( IntlDateFormatter_object *dfo, zval* return_va } /* {{{ Internal function which calls the udat_parseCalendar */ -static void internal_parse_to_localtime(IntlDateFormatter_object *dfo, char* text_to_parse, size_t text_len, int32_t *parse_pos, zval *return_value) +static void internal_parse_to_localtime(IntlDateFormatter_object *dfo, const char* text_to_parse, size_t text_len, int32_t *parse_pos, zval *return_value) { UCalendar *parsed_calendar = NULL; UChar* text_utf16 = NULL; @@ -149,7 +149,7 @@ U_CFUNC PHP_FUNCTION(datefmt_parse) if (z_parse_pos) { zval *z_parse_pos_tmp = z_parse_pos; ZVAL_DEREF(z_parse_pos_tmp); - zend_long long_parse_pos = zval_get_long(z_parse_pos_tmp); + const zend_long long_parse_pos = zval_get_long(z_parse_pos_tmp); if (ZEND_LONG_INT_OVFL(long_parse_pos)) { intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR); intl_error_set_custom_msg(NULL, "String index is out of valid range."); @@ -188,7 +188,7 @@ U_CFUNC PHP_METHOD(IntlDateFormatter, parseToCalendar) if (z_parse_pos) { bool failed; - zend_long long_parse_pos = zval_try_get_long(z_parse_pos, &failed); + const zend_long long_parse_pos = zval_try_get_long(z_parse_pos, &failed); if (failed) { zend_argument_type_error(2, "must be of type int, %s given", zend_zval_value_name(z_parse_pos)); RETURN_THROWS(); @@ -231,7 +231,7 @@ U_CFUNC PHP_FUNCTION(datefmt_localtime) if (z_parse_pos) { zval *z_parse_pos_tmp = z_parse_pos; ZVAL_DEREF(z_parse_pos_tmp); - zend_long long_parse_pos = zval_get_long(z_parse_pos_tmp); + const zend_long long_parse_pos = zval_get_long(z_parse_pos_tmp); if (ZEND_LONG_INT_OVFL(long_parse_pos)) { intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR); intl_error_set_custom_msg(NULL, "String index is out of valid range."); diff --git a/ext/intl/formatter/formatter_attr.cpp b/ext/intl/formatter/formatter_attr.cpp index d21873ecdabc..4e97ad7f7105 100644 --- a/ext/intl/formatter/formatter_attr.cpp +++ b/ext/intl/formatter/formatter_attr.cpp @@ -74,7 +74,7 @@ U_CFUNC PHP_FUNCTION( numfmt_get_attribute ) break; case UNUM_ROUNDING_INCREMENT: { - double value_double = unum_getDoubleAttribute(FORMATTER_UNUM(nfo), attribute); + const double value_double = unum_getDoubleAttribute(FORMATTER_UNUM(nfo), attribute); if(value_double == -1) { INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR; } else { diff --git a/ext/intl/formatter/formatter_parse.cpp b/ext/intl/formatter/formatter_parse.cpp index 7bbc461516bc..a475960809b8 100644 --- a/ext/intl/formatter/formatter_parse.cpp +++ b/ext/intl/formatter/formatter_parse.cpp @@ -171,7 +171,7 @@ U_CFUNC PHP_FUNCTION( numfmt_parse_currency ) ZEND_TRY_ASSIGN_REF_LONG(zposition, pp.getIndex()); } - double number = currAmt->getNumber().getDouble(INTL_DATA_ERROR_CODE(nfo)); + const double number = currAmt->getNumber().getDouble(INTL_DATA_ERROR_CODE(nfo)); /* Convert parsed currency to UTF-8 and pass it back to caller. */ icu::UnicodeString ucurrency(currAmt->getISOCurrency()); diff --git a/ext/intl/intl_error.c b/ext/intl/intl_error.c index f19a5ec617dc..eab1b7478155 100644 --- a/ext/intl/intl_error.c +++ b/ext/intl/intl_error.c @@ -132,7 +132,7 @@ zend_string * intl_error_get_message( intl_error* err ) return ZSTR_EMPTY_ALLOC(); uErrorName = u_errorName( err->code ); - size_t uErrorLen = strlen(uErrorName); + const size_t uErrorLen = strlen(uErrorName); /* Format output string */ if (err->custom_error_message) { diff --git a/ext/intl/listformatter/listformatter_class.cpp b/ext/intl/listformatter/listformatter_class.cpp index 221dadfcd3b5..0f432b0e6056 100644 --- a/ext/intl/listformatter/listformatter_class.cpp +++ b/ext/intl/listformatter/listformatter_class.cpp @@ -138,7 +138,7 @@ PHP_METHOD(IntlListFormatter, format) intl_errors_reset(LISTFORMATTER_ERROR_P(obj)); - uint32_t count = zend_hash_num_elements(ht); + const uint32_t count = zend_hash_num_elements(ht); if (count == 0) { RETURN_EMPTY_STRING(); } diff --git a/ext/intl/locale/locale_methods.cpp b/ext/intl/locale/locale_methods.cpp index ffcba02d322d..14fa2a8f6bf5 100644 --- a/ext/intl/locale/locale_methods.cpp +++ b/ext/intl/locale/locale_methods.cpp @@ -1660,7 +1660,7 @@ U_CFUNC PHP_FUNCTION(locale_add_likely_subtags) locale = (char *)intl_locale_get_default(); } - int32_t maximized_locale_len = uloc_addLikelySubtags(locale, maximized_locale, sizeof(maximized_locale), &status); + const int32_t maximized_locale_len = uloc_addLikelySubtags(locale, maximized_locale, sizeof(maximized_locale), &status); INTL_CHECK_STATUS(status, "invalid locale"); if (maximized_locale_len < 0) { RETURN_FALSE; @@ -1683,7 +1683,7 @@ U_CFUNC PHP_FUNCTION(locale_minimize_subtags) locale = (char *)intl_locale_get_default(); } - int32_t minimized_locale_len = uloc_minimizeSubtags(locale, minimized_locale, sizeof(minimized_locale), &status); + const int32_t minimized_locale_len = uloc_minimizeSubtags(locale, minimized_locale, sizeof(minimized_locale), &status); INTL_CHECK_STATUS(status, "invalid locale"); if (minimized_locale_len < 0) { RETURN_FALSE; diff --git a/ext/intl/msgformat/msgformat_helpers.cpp b/ext/intl/msgformat/msgformat_helpers.cpp index 12e05af57219..e676a07416c2 100644 --- a/ext/intl/msgformat/msgformat_helpers.cpp +++ b/ext/intl/msgformat/msgformat_helpers.cpp @@ -184,7 +184,7 @@ static HashTable *umsg_parse_format(MessageFormatter_object *mfo, (void*)&bogusType, sizeof(bogusType)); } } else if (name_part.getType() == UMSGPAT_PART_TYPE_ARG_NUMBER) { - int32_t argNumber = name_part.getValue(); + const int32_t argNumber = name_part.getValue(); if (argNumber < 0) { intl_errors_set(&err, U_INVALID_FORMAT_ERROR, "Found part with negative number"); @@ -364,7 +364,7 @@ U_CFUNC void umsg_format_helper(MessageFormatter_object *mfo, UChar **formatted, int32_t *formatted_len) { - int arg_count = zend_hash_num_elements(args); + const int arg_count = zend_hash_num_elements(args); std::vector fargs; std::vector farg_names; MessageFormat *mf = (MessageFormat *)mfo->mf_data.umsgf; @@ -408,7 +408,7 @@ U_CFUNC void umsg_format_helper(MessageFormatter_object *mfo, } UChar temp[16]; - int32_t len = u_sprintf(temp, "%u", (uint32_t)num_index); + const int32_t len = u_sprintf(temp, "%u", (uint32_t)num_index); key.append(temp, len); storedArgType = (Formattable::Type*)zend_hash_index_find_ptr(types, num_index); @@ -467,7 +467,7 @@ U_CFUNC void umsg_format_helper(MessageFormatter_object *mfo, } case Formattable::kDouble: { - double d = zval_get_double(elem); + const double d = zval_get_double(elem); formattable.setDouble(d); break; } @@ -523,7 +523,7 @@ U_CFUNC void umsg_format_helper(MessageFormatter_object *mfo, } case Formattable::kDate: { - double dd = intl_zval_to_millis(elem, &err); + const double dd = intl_zval_to_millis(elem, &err); if (U_FAILURE(err.code)) { char *message; zend_string *u8key; diff --git a/ext/intl/msgformat/msgformat_parse.cpp b/ext/intl/msgformat/msgformat_parse.cpp index b50dc1efed41..e177d3aca7fb 100644 --- a/ext/intl/msgformat/msgformat_parse.cpp +++ b/ext/intl/msgformat/msgformat_parse.cpp @@ -27,7 +27,7 @@ extern "C" { } /* {{{ */ -static void msgfmt_do_parse(MessageFormatter_object *mfo, char *source, size_t src_len, zval *return_value) +static void msgfmt_do_parse(MessageFormatter_object *mfo, const char *source, size_t src_len, zval *return_value) { zval *fargs; int count = 0; diff --git a/ext/intl/rangeformatter/rangeformatter_class.cpp b/ext/intl/rangeformatter/rangeformatter_class.cpp index 2df252d1986b..f933e0c06090 100644 --- a/ext/intl/rangeformatter/rangeformatter_class.cpp +++ b/ext/intl/rangeformatter/rangeformatter_class.cpp @@ -60,7 +60,7 @@ zend_object *IntlNumberRangeFormatter_object_create(zend_class_entry *ce) return &intern->zo; } -static icu::Formattable rangeformatter_create_formattable(zval *number) +static icu::Formattable rangeformatter_create_formattable(const zval *number) { icu::Formattable formattable; diff --git a/ext/intl/resourcebundle/resourcebundle_class.cpp b/ext/intl/resourcebundle/resourcebundle_class.cpp index 2a89b14cc6e5..f796a6ffc8aa 100644 --- a/ext/intl/resourcebundle/resourcebundle_class.cpp +++ b/ext/intl/resourcebundle/resourcebundle_class.cpp @@ -174,7 +174,7 @@ static zval *resource_bundle_array_fetch( { int32_t index = 0; char *key = NULL; - bool is_numeric = offset_str == NULL; + const bool is_numeric = offset_str == NULL; char *pbuf; ResourceBundle_object *rb; diff --git a/ext/intl/timezone/timezone_class.cpp b/ext/intl/timezone/timezone_class.cpp index 319e81c9a77d..23b5511cf361 100644 --- a/ext/intl/timezone/timezone_class.cpp +++ b/ext/intl/timezone/timezone_class.cpp @@ -292,7 +292,7 @@ static HashTable *TimeZone_get_debug_info(zend_object *object, int *is_temp) zend_hash_str_update(debug_info, "id", sizeof("id") - 1, &zv); int32_t rawOffset, dstOffset; - UDate now = Calendar::getNow(); + const UDate now = Calendar::getNow(); tz->getOffset(now, false, rawOffset, dstOffset, uec); if (U_FAILURE(uec)) { return debug_info; diff --git a/ext/intl/timezone/timezone_methods.cpp b/ext/intl/timezone/timezone_methods.cpp index 8f70a87487ac..c2246c5406b1 100644 --- a/ext/intl/timezone/timezone_methods.cpp +++ b/ext/intl/timezone/timezone_methods.cpp @@ -176,7 +176,7 @@ U_CFUNC PHP_FUNCTION(intltz_count_equivalent_ids) RETURN_FALSE; } - int32_t result = TimeZone::countEquivalentIDs(id); + const int32_t result = TimeZone::countEquivalentIDs(id); RETURN_LONG((zend_long)result); } @@ -279,7 +279,7 @@ U_CFUNC PHP_FUNCTION(intltz_get_region) RETURN_FALSE; } - int32_t region_len = TimeZone::getRegion(id, outbuf, sizeof(outbuf), status); + const int32_t region_len = TimeZone::getRegion(id, outbuf, sizeof(outbuf), status); INTL_CHECK_STATUS(status, "error obtaining region"); RETURN_STRINGL(outbuf, region_len); diff --git a/ext/intl/transliterator/transliterator_methods.cpp b/ext/intl/transliterator/transliterator_methods.cpp index 16e5cf8b95d3..45dd00b42bcf 100644 --- a/ext/intl/transliterator/transliterator_methods.cpp +++ b/ext/intl/transliterator/transliterator_methods.cpp @@ -31,7 +31,7 @@ extern "C" { #include -static int create_transliterator( char *str_id, size_t str_id_len, zend_long direction, zval *object ) +static int create_transliterator( const char *str_id, size_t str_id_len, zend_long direction, zval *object ) { Transliterator_object *to; UChar *ustr_id = nullptr; diff --git a/ext/intl/uchar/uchar.cpp b/ext/intl/uchar/uchar.cpp index 4f403cba818e..83d5ab15d341 100644 --- a/ext/intl/uchar/uchar.cpp +++ b/ext/intl/uchar/uchar.cpp @@ -25,10 +25,10 @@ extern "C" { #define IC_METHOD(mname) PHP_METHOD(IntlChar, mname) -static inline int convert_cp(UChar32* pcp, zend_string *string_codepoint, zend_long int_codepoint) { +static inline int convert_cp(UChar32* pcp, const zend_string *string_codepoint, zend_long int_codepoint) { if (string_codepoint != NULL) { int32_t i = 0; - size_t string_codepoint_length = ZSTR_LEN(string_codepoint); + const size_t string_codepoint_length = ZSTR_LEN(string_codepoint); if (ZEND_SIZE_T_INT_OVFL(string_codepoint_length)) { intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR); From 9e5bc9adc905a864dc6fee11ee94d68ccfcd9459 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Fri, 26 Jun 2026 10:28:24 -0300 Subject: [PATCH 9/9] ext/odbc: Fast ZPP conversion (#21893) Uses PATH_OR_NULL for strings to avoid surprise null terminators. --- ext/odbc/php_odbc.c | 261 +++++++++++++++++++++++++++----------------- 1 file changed, 159 insertions(+), 102 deletions(-) diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index a6218cab7276..17120e88af04 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -627,9 +627,10 @@ void php_odbc_fetch_attribs(INTERNAL_FUNCTION_PARAMETERS, int mode) zval *pv_res; zend_long flag; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pv_res, odbc_result_ce, &flag) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce) + Z_PARAM_LONG(flag) + ZEND_PARSE_PARAMETERS_END(); result = Z_ODBC_RESULT_P(pv_res); CHECK_ODBC_RESULT(result); @@ -753,9 +754,9 @@ void odbc_transact(INTERNAL_FUNCTION_PARAMETERS, int type) RETCODE rc; zval *pv_conn; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_conn, odbc_connection_ce) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(pv_conn, odbc_connection_ce) + ZEND_PARSE_PARAMETERS_END(); odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); CHECK_ODBC_CONNECTION(conn); @@ -778,9 +779,10 @@ void odbc_column_lengths(INTERNAL_FUNCTION_PARAMETERS, int type) zval *pv_res; zend_long pv_num; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pv_res, odbc_result_ce, &pv_num) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce) + Z_PARAM_LONG(pv_num) + ZEND_PARSE_PARAMETERS_END(); result = Z_ODBC_RESULT_P(pv_res); CHECK_ODBC_RESULT(result); @@ -854,9 +856,10 @@ PHP_FUNCTION(odbc_prepare) int i; SQLUINTEGER scrollopts; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pv_conn, odbc_connection_ce, &query, &query_len) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(pv_conn, odbc_connection_ce) + Z_PARAM_STRING(query, query_len) + ZEND_PARSE_PARAMETERS_END(); odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); CHECK_ODBC_CONNECTION(conn); @@ -967,9 +970,11 @@ PHP_FUNCTION(odbc_execute) int i; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|h", &pv_res, odbc_result_ce, &pv_param_ht) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT(pv_param_ht) + ZEND_PARSE_PARAMETERS_END(); result = Z_ODBC_RESULT_P(pv_res); CHECK_ODBC_RESULT(result); @@ -1120,9 +1125,9 @@ PHP_FUNCTION(odbc_cursor) odbc_result *result; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce) + ZEND_PARSE_PARAMETERS_END(); result = Z_ODBC_RESULT_P(pv_res); CHECK_ODBC_RESULT(result); @@ -1175,9 +1180,10 @@ PHP_FUNCTION(odbc_data_source) UCHAR server_name[100], desc[200]; SQLSMALLINT len1=0, len2=0, fetch_type; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &zv_conn, odbc_connection_ce, &zv_fetch_type) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(zv_conn, odbc_connection_ce) + Z_PARAM_LONG(zv_fetch_type) + ZEND_PARSE_PARAMETERS_END(); fetch_type = (SQLSMALLINT) zv_fetch_type; @@ -1233,9 +1239,10 @@ PHP_FUNCTION(odbc_exec) RETCODE rc; SQLUINTEGER scrollopts; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pv_conn, odbc_connection_ce, &query, &query_len) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(pv_conn, odbc_connection_ce) + Z_PARAM_STRING(query, query_len) + ZEND_PARSE_PARAMETERS_END(); odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); CHECK_ODBC_CONNECTION(conn); @@ -1722,9 +1729,11 @@ PHP_FUNCTION(odbc_result_all) size_t i, pv_format_len = 0; SQLSMALLINT sql_c_type; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s", &pv_res, odbc_result_ce, &pv_format, &pv_format_len) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce) + Z_PARAM_OPTIONAL + Z_PARAM_STRING(pv_format, pv_format_len) + ZEND_PARSE_PARAMETERS_END(); result = Z_ODBC_RESULT_P(pv_res); CHECK_ODBC_RESULT(result); @@ -1850,9 +1859,9 @@ PHP_FUNCTION(odbc_free_result) zval *pv_res; odbc_result *result; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce) + ZEND_PARSE_PARAMETERS_END(); result = Z_ODBC_RESULT_P(pv_res); CHECK_ODBC_RESULT(result); @@ -2019,8 +2028,8 @@ void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) ZEND_PARSE_PARAMETERS_START(1, 4) Z_PARAM_STRING(db, db_len) Z_PARAM_OPTIONAL - Z_PARAM_STRING_OR_NULL(uid, uid_len) - Z_PARAM_STRING_OR_NULL(pwd, pwd_len) + Z_PARAM_PATH_OR_NULL(uid, uid_len) + Z_PARAM_PATH_OR_NULL(pwd, pwd_len) Z_PARAM_LONG(pv_opt) ZEND_PARSE_PARAMETERS_END(); @@ -2169,9 +2178,9 @@ PHP_FUNCTION(odbc_close) zval *pv_conn; odbc_link *link; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_conn, odbc_connection_ce) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(pv_conn, odbc_connection_ce) + ZEND_PARSE_PARAMETERS_END(); link = Z_ODBC_LINK_P(pv_conn); odbc_connection *connection = Z_ODBC_CONNECTION_P(pv_conn); @@ -2192,9 +2201,9 @@ PHP_FUNCTION(odbc_num_rows) SQLLEN rows; zval *pv_res; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce) + ZEND_PARSE_PARAMETERS_END(); result = Z_ODBC_RESULT_P(pv_res); CHECK_ODBC_RESULT(result); @@ -2211,9 +2220,9 @@ PHP_FUNCTION(odbc_next_result) zval *pv_res; int rc, i; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce) + ZEND_PARSE_PARAMETERS_END(); result = Z_ODBC_RESULT_P(pv_res); CHECK_ODBC_RESULT(result); @@ -2257,9 +2266,9 @@ PHP_FUNCTION(odbc_num_fields) odbc_result *result; zval *pv_res; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce) + ZEND_PARSE_PARAMETERS_END(); result = Z_ODBC_RESULT_P(pv_res); CHECK_ODBC_RESULT(result); @@ -2275,9 +2284,10 @@ PHP_FUNCTION(odbc_field_name) zval *pv_res; zend_long pv_num; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pv_res, odbc_result_ce, &pv_num) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce) + Z_PARAM_LONG(pv_num) + ZEND_PARSE_PARAMETERS_END(); result = Z_ODBC_RESULT_P(pv_res); CHECK_ODBC_RESULT(result); @@ -2310,9 +2320,10 @@ PHP_FUNCTION(odbc_field_type) zval *pv_res; zend_long pv_num; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pv_res, odbc_result_ce, &pv_num) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce) + Z_PARAM_LONG(pv_num) + ZEND_PARSE_PARAMETERS_END(); result = Z_ODBC_RESULT_P(pv_res); CHECK_ODBC_RESULT(result); @@ -2359,9 +2370,10 @@ PHP_FUNCTION(odbc_field_num) odbc_result *result; zval *pv_res; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pv_res, odbc_result_ce, &fname, &fname_len) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce) + Z_PARAM_STRING(fname, fname_len) + ZEND_PARSE_PARAMETERS_END(); result = Z_ODBC_RESULT_P(pv_res); CHECK_ODBC_RESULT(result); @@ -2394,9 +2406,11 @@ PHP_FUNCTION(odbc_autocommit) bool pv_onoff = false; bool pv_onoff_is_null = true; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b!", &pv_conn, odbc_connection_ce, &pv_onoff, &pv_onoff_is_null) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJECT_OF_CLASS(pv_conn, odbc_connection_ce) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL_OR_NULL(pv_onoff, pv_onoff_is_null) + ZEND_PARSE_PARAMETERS_END(); odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); CHECK_ODBC_CONNECTION(conn); @@ -2442,9 +2456,10 @@ static void php_odbc_lasterror(INTERNAL_FUNCTION_PARAMETERS, int mode) zval *pv_handle = NULL; char *ret; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &pv_handle, odbc_connection_ce) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_OBJECT_OF_CLASS(pv_handle, odbc_connection_ce) + ZEND_PARSE_PARAMETERS_END(); if (pv_handle) { conn = Z_ODBC_CONNECTION_P(pv_handle); @@ -2496,9 +2511,12 @@ PHP_FUNCTION(odbc_setoption) zval *pv_handle; zend_long pv_which, pv_opt, pv_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "olll", &pv_handle, &pv_which, &pv_opt, &pv_val) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(4, 4) + Z_PARAM_OBJECT(pv_handle) + Z_PARAM_LONG(pv_which) + Z_PARAM_LONG(pv_opt) + Z_PARAM_LONG(pv_val) + ZEND_PARSE_PARAMETERS_END(); switch (pv_which) { case 1: /* SQLSetConnectOption */ @@ -2556,10 +2574,14 @@ PHP_FUNCTION(odbc_tables) size_t cat_len = 0, schema_len = 0, table_len = 0, type_len = 0; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s!s!s!s!", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, - &table, &table_len, &type, &type_len) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 5) + Z_PARAM_OBJECT_OF_CLASS(pv_conn, odbc_connection_ce) + Z_PARAM_OPTIONAL + Z_PARAM_PATH_OR_NULL(cat, cat_len) + Z_PARAM_PATH_OR_NULL(schema, schema_len) + Z_PARAM_PATH_OR_NULL(table, table_len) + Z_PARAM_PATH_OR_NULL(type, type_len) + ZEND_PARSE_PARAMETERS_END(); odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); CHECK_ODBC_CONNECTION(conn); @@ -2621,10 +2643,14 @@ PHP_FUNCTION(odbc_columns) size_t cat_len = 0, schema_len = 0, table_len = 0, column_len = 0; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s!s!s!s!", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, - &table, &table_len, &column, &column_len) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 5) + Z_PARAM_OBJECT_OF_CLASS(pv_conn, odbc_connection_ce) + Z_PARAM_OPTIONAL + Z_PARAM_PATH_OR_NULL(cat, cat_len) + Z_PARAM_PATH_OR_NULL(schema, schema_len) + Z_PARAM_PATH_OR_NULL(table, table_len) + Z_PARAM_PATH_OR_NULL(column, column_len) + ZEND_PARSE_PARAMETERS_END(); odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); CHECK_ODBC_CONNECTION(conn); @@ -2688,10 +2714,13 @@ PHP_FUNCTION(odbc_columnprivileges) size_t cat_len = 0, schema_len, table_len, column_len; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!sss", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, - &table, &table_len, &column, &column_len) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(5, 5) + Z_PARAM_OBJECT_OF_CLASS(pv_conn, odbc_connection_ce) + Z_PARAM_PATH_OR_NULL(cat, cat_len) + Z_PARAM_STRING(schema, schema_len) + Z_PARAM_STRING(table, table_len) + Z_PARAM_STRING(column, column_len) + ZEND_PARSE_PARAMETERS_END(); odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); CHECK_ODBC_CONNECTION(conn); @@ -2748,10 +2777,15 @@ PHP_FUNCTION(odbc_foreignkeys) size_t pcat_len = 0, pschema_len, ptable_len, fcat_len, fschema_len, ftable_len; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!sssss", &pv_conn, odbc_connection_ce, &pcat, &pcat_len, &pschema, &pschema_len, - &ptable, &ptable_len, &fcat, &fcat_len, &fschema, &fschema_len, &ftable, &ftable_len) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(7, 7) + Z_PARAM_OBJECT_OF_CLASS(pv_conn, odbc_connection_ce) + Z_PARAM_PATH_OR_NULL(pcat, pcat_len) + Z_PARAM_STRING(pschema, pschema_len) + Z_PARAM_STRING(ptable, ptable_len) + Z_PARAM_STRING(fcat, fcat_len) + Z_PARAM_STRING(fschema, fschema_len) + Z_PARAM_STRING(ftable, ftable_len) + ZEND_PARSE_PARAMETERS_END(); #if defined(HAVE_IBMDB2) #define EMPTY_TO_NULL(xstr) \ @@ -2822,9 +2856,11 @@ PHP_FUNCTION(odbc_gettypeinfo) RETCODE rc; SQLSMALLINT data_type; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &pv_conn, odbc_connection_ce, &pv_data_type) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_OBJECT_OF_CLASS(pv_conn, odbc_connection_ce) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(pv_data_type) + ZEND_PARSE_PARAMETERS_END(); data_type = (SQLSMALLINT) pv_data_type; @@ -2879,9 +2915,12 @@ PHP_FUNCTION(odbc_primarykeys) size_t cat_len = 0, schema_len, table_len; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!ss", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &table, &table_len) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(4, 4) + Z_PARAM_OBJECT_OF_CLASS(pv_conn, odbc_connection_ce) + Z_PARAM_PATH_OR_NULL(cat, cat_len) + Z_PARAM_STRING(schema, schema_len) + Z_PARAM_STRING(table, table_len) + ZEND_PARSE_PARAMETERS_END(); odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); CHECK_ODBC_CONNECTION(conn); @@ -2937,10 +2976,13 @@ PHP_FUNCTION(odbc_procedurecolumns) size_t cat_len = 0, schema_len = 0, proc_len = 0, col_len = 0; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s!s!s!s!", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, - &proc, &proc_len, &col, &col_len) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 5) + Z_PARAM_OBJECT_OF_CLASS(pv_conn, odbc_connection_ce) + Z_PARAM_PATH_OR_NULL(cat, cat_len) + Z_PARAM_PATH_OR_NULL(schema, schema_len) + Z_PARAM_PATH_OR_NULL(proc, proc_len) + Z_PARAM_PATH_OR_NULL(col, col_len) + ZEND_PARSE_PARAMETERS_END(); odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); CHECK_ODBC_CONNECTION(conn); @@ -2997,9 +3039,12 @@ PHP_FUNCTION(odbc_procedures) size_t cat_len = 0, schema_len = 0, proc_len = 0; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s!s!s!", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &proc, &proc_len) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 4) + Z_PARAM_OBJECT_OF_CLASS(pv_conn, odbc_connection_ce) + Z_PARAM_PATH_OR_NULL(cat, cat_len) + Z_PARAM_PATH_OR_NULL(schema, schema_len) + Z_PARAM_PATH_OR_NULL(proc, proc_len) + ZEND_PARSE_PARAMETERS_END(); odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); CHECK_ODBC_CONNECTION(conn); @@ -3057,10 +3102,15 @@ PHP_FUNCTION(odbc_specialcolumns) SQLUSMALLINT type, scope, nullable; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ols!ssll", &pv_conn, odbc_connection_ce, &vtype, &cat, &cat_len, &schema, &schema_len, - &name, &name_len, &vscope, &vnullable) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(7, 7) + Z_PARAM_OBJECT_OF_CLASS(pv_conn, odbc_connection_ce) + Z_PARAM_LONG(vtype) + Z_PARAM_PATH_OR_NULL(cat, cat_len) + Z_PARAM_STRING(schema, schema_len) + Z_PARAM_STRING(name, name_len) + Z_PARAM_LONG(vscope) + Z_PARAM_LONG(vnullable) + ZEND_PARSE_PARAMETERS_END(); type = (SQLUSMALLINT) vtype; scope = (SQLUSMALLINT) vscope; @@ -3124,10 +3174,14 @@ PHP_FUNCTION(odbc_statistics) SQLUSMALLINT unique, reserved; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!ssll", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, - &name, &name_len, &vunique, &vreserved) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(6, 6) + Z_PARAM_OBJECT_OF_CLASS(pv_conn, odbc_connection_ce) + Z_PARAM_PATH_OR_NULL(cat, cat_len) + Z_PARAM_STRING(schema, schema_len) + Z_PARAM_STRING(name, name_len) + Z_PARAM_LONG(vunique) + Z_PARAM_LONG(vreserved) /* XXX: Documented as 'accuracy' */ + ZEND_PARSE_PARAMETERS_END(); unique = (SQLUSMALLINT) vunique; reserved = (SQLUSMALLINT) vreserved; @@ -3188,9 +3242,12 @@ PHP_FUNCTION(odbc_tableprivileges) size_t cat_len = 0, schema_len, table_len; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!ss", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &table, &table_len) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(4, 4) + Z_PARAM_OBJECT_OF_CLASS(pv_conn, odbc_connection_ce) + Z_PARAM_PATH_OR_NULL(cat, cat_len) + Z_PARAM_STRING(schema, schema_len) + Z_PARAM_STRING(table, table_len) + ZEND_PARSE_PARAMETERS_END(); odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); CHECK_ODBC_CONNECTION(conn);