Commit 3913e039656 for php
commit 3913e039656d82d96b4d7062f439289c4dd64268
Merge: 2290177565d 906356642d8
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date: Mon Oct 5 09:01:30 2026 -0400
Merge branch 'PHP-8.5' into PHP-8.6
* PHP-8.5:
ext/intl: Use byte offsets in IntlDateFormatter parsing
diff --cc NEWS
index 30869309af8,4d949ee7420..e4ce7f3f7cf
--- a/NEWS
+++ b/NEWS
@@@ -56,7 -77,25 +56,10 @@@ PH
haystacks. (Weilin Du)
. Fixed grapheme_strrpos() and grapheme_strripos() skipping overlapping matches
when using a negative offset. (Weilin Du)
+ . Fixed IntlDateFormatter::parse(), localtime() and parseToCalendar()
+ treating the offset as UTF-16 code units instead of bytes.
+ (Ilia Alshanetsky)
-- Lexbor:
- . Merge patches lexbor/lexbor@8a14bc0 and lexbor/lexbor@f67ce4b, fixing a
- heap buffer overflow in :lexbor-contains() parsing and buffer overflows
- in malformed decode replay. (alexandre-daubois)
-
-- MBString:
- . Fixed bug GH-23106 (mb_strpos() reads past the end of a haystack ending in
- a truncated UTF-8 sequence). (Lazizbek Ergashev)
- . Fixed mbstring functions emitting surrogates in UTF-8 output and flagging
- it as valid UTF-8. (Nicolas Grekas)
-
-- MySQLi:
- . Fix GH-22854: Fixed failed assertion when accessing mysqli property after
- failed reconnection. (Kamil Tekiela)
-
- MySQLnd:
. Fixed field_count not resetting on OK packet. (Kamil Tekiela)
. Fixed memory leak when closing a prepared statement after its connection
diff --cc ext/intl/formatter/formatter_parse.cpp
index 7a233f012d4,2b53ea33a68..db7463030b7
--- a/ext/intl/formatter/formatter_parse.cpp
+++ b/ext/intl/formatter/formatter_parse.cpp
@@@ -16,65 -16,29 +16,30 @@@
#include <config.h>
#endif
-#include "php_intl.h"
-
+#include <unicode/fmtable.h>
+#include <unicode/curramt.h>
#include <unicode/ustring.h>
-#include <locale.h>
-
+#include "../intl_convertcpp.h"
#include "formatter_class.h"
#include "formatter_format.h"
+
+extern "C" {
+#include "php_intl.h"
+ #include "intl_convert.h"
+}
+
+#include <locale.h>
+#include <memory>
#define ICU_LOCALE_BUG 1
- static bool numfmt_utf8_offset_to_utf16(const char *str, size_t str_len, int32_t *position, UErrorCode *status)
- {
- int32_t utf16_position;
-
- if (*position < 0 || (size_t) *position > str_len) {
- return true;
- }
-
- *status = U_ZERO_ERROR;
- u_strFromUTF8(nullptr, 0, &utf16_position, str, *position, status);
- if (*status != U_BUFFER_OVERFLOW_ERROR && U_FAILURE(*status)) {
- return false;
- }
- *status = U_ZERO_ERROR;
-
- *position = utf16_position;
- return true;
- }
-
- static int32_t numfmt_utf16_offset_to_utf8(const icu::UnicodeString &str, int32_t position)
- {
- int32_t utf8_position;
- UErrorCode status = U_ZERO_ERROR;
-
- if (position < 0 || position > str.length()) {
- return position;
- }
-
- u_strToUTF8(nullptr, 0, &utf8_position, str.getBuffer(), position, &status);
- if (status != U_BUFFER_OVERFLOW_ERROR && U_FAILURE(status)) {
- return position;
- }
-
- return utf8_position;
- }
-
/* {{{ Parse a number. */
-PHP_FUNCTION( numfmt_parse )
+U_CFUNC PHP_FUNCTION( numfmt_parse )
{
zend_long type = FORMAT_TYPE_DOUBLE;
- UChar* sstr = NULL;
- int32_t sstr_len = 0;
char* str = NULL;
size_t str_len;
- int32_t val32, position = 0;
- int64_t val64;
- double val_double;
- int32_t* position_p = NULL;
+ int32_t position = 0;
zval *zposition = NULL;
char *oldlocale;
FORMATTER_METHOD_INIT_VARS;
@@@ -99,10 -59,10 +64,10 @@@
FORMATTER_METHOD_FETCH_OBJECT;
/* Convert given string to UTF-16. */
- intl_convert_utf8_to_utf16(&sstr, &sstr_len, str, str_len, &INTL_DATA_ERROR_CODE(nfo));
+ icu::UnicodeString ustr;
+ intl_stringFromChar(ustr, str, str_len, &INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "String conversion to UTF-16 failed" );
- if (zposition && !numfmt_utf8_offset_to_utf16(str, str_len, &position, &INTL_DATA_ERROR_CODE(nfo))) {
+ if (zposition && !intl_convert_utf8_offset_to_utf16(str, str_len, &position, &INTL_DATA_ERROR_CODE(nfo))) {
- efree(sstr);
INTL_METHOD_CHECK_STATUS(nfo, "Invalid UTF-8 offset");
}
@@@ -162,7 -105,7 +127,7 @@@
}
if (zposition) {
- position = numfmt_utf16_offset_to_utf8(ustr, position);
- position = intl_convert_utf16_offset_to_utf8(sstr, sstr_len, position);
++ position = intl_convert_utf16_offset_to_utf8(ustr.getBuffer(), ustr.length(), position);
ZEND_TRY_ASSIGN_REF_LONG(zposition, position);
}
@@@ -197,43 -150,32 +162,43 @@@ U_CFUNC PHP_FUNCTION( numfmt_parse_curr
FORMATTER_METHOD_FETCH_OBJECT;
/* Convert given string to UTF-16. */
- intl_convert_utf8_to_utf16(&sstr, &sstr_len, str, str_len, &INTL_DATA_ERROR_CODE(nfo));
+ icu::UnicodeString ustr;
+ intl_stringFromChar(ustr, str, str_len, &INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "String conversion to UTF-16 failed" );
- if(zposition) {
- position = (int32_t) zval_get_long(zposition);
+ if (zposition) {
+ zend_long long_position = zval_get_long(zposition);
+ if (UNEXPECTED(long_position < INT32_MIN || long_position > INT32_MAX)) {
+ zend_argument_value_error(hasThis() ? 3 : 4, "must be between %d and %d", INT32_MIN, INT32_MAX);
+ RETURN_THROWS();
+ }
+ position = (int32_t) long_position;
- if (!numfmt_utf8_offset_to_utf16(str, str_len, &position, &INTL_DATA_ERROR_CODE(nfo))) {
+ if (!intl_convert_utf8_offset_to_utf16(str, str_len, &position, &INTL_DATA_ERROR_CODE(nfo))) {
- efree(sstr);
INTL_METHOD_CHECK_STATUS(nfo, "Invalid UTF-8 offset");
}
- position_p = &position;
}
- number = unum_parseDoubleCurrency(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, currency, &INTL_DATA_ERROR_CODE(nfo));
+ icu::ParsePosition pp(position);
+ std::unique_ptr<icu::CurrencyAmount> currAmt(FORMATTER_OBJECT(nfo)->parseCurrency(ustr, pp));
+
+ if (currAmt == nullptr || pp.getErrorIndex() >= 0) {
+ INTL_DATA_ERROR_CODE(nfo) = U_PARSE_ERROR;
+ INTL_METHOD_CHECK_STATUS( nfo, "Number parsing failed" );
+ }
+
if(zposition) {
- position = numfmt_utf16_offset_to_utf8(ustr, pp.getIndex());
- position = intl_convert_utf16_offset_to_utf8(sstr, sstr_len, position);
++ position = intl_convert_utf16_offset_to_utf8(ustr.getBuffer(), ustr.length(), pp.getIndex());
ZEND_TRY_ASSIGN_REF_LONG(zposition, position);
}
- if (sstr) {
- efree(sstr);
- }
- INTL_METHOD_CHECK_STATUS( nfo, "Number parsing failed" );
+
+ const double number = currAmt->getNumber().getDouble(INTL_DATA_ERROR_CODE(nfo));
/* Convert parsed currency to UTF-8 and pass it back to caller. */
- u8str = intl_convert_utf16_to_utf8(currency, u_strlen(currency), &INTL_DATA_ERROR_CODE(nfo));
+ icu::UnicodeString ucurrency(currAmt->getISOCurrency());
+
+ zend_string *u8str = intl_charFromString(ucurrency, &INTL_DATA_ERROR_CODE(nfo));
INTL_METHOD_CHECK_STATUS( nfo, "Currency conversion to UTF-8 failed" );
- ZEND_TRY_ASSIGN_REF_NEW_STR(zcurrency, u8str);
+ ZEND_TRY_ASSIGN_REF_STR(zcurrency, u8str);
RETVAL_DOUBLE( number );
}