Commit 7a3dd1b952e for php.net

commit 7a3dd1b952e38450420369bf43c796f1b6768e6e
Merge: 9820f01cd16 4efca409534
Author: Weilin Du <weilindu@php.net>
Date:   Sat Jun 20 13:08:51 2026 +0800

    Merge branch 'PHP-8.4' into PHP-8.5

    * PHP-8.4:

      ext/intl: Fix Locale::lookup() fallback on invalid language tags (#22306)

diff --cc NEWS
index a1a2776e037,df93cb5b77b..7319f666bbb
--- a/NEWS
+++ b/NEWS
@@@ -41,13 -46,22 +41,15 @@@ PH
      (Weilin Du)
    . Fixed IntlTimeZone::getDisplayName() to synchronize object error state
      for invalid display types. (Weilin Du)
 -  . Fixed Spoofchecker restriction-level APIs to only be exposed with ICU 53
 -    and later. (Graham Campbell)
+   . Fixed Locale::lookup() and locale_lookup() to return NULL instead of the
+     fallback locale when a language tag cannot be canonicalized. (Weilin Du)

 -- mysqli:
 -  . Fix stmt->query leak in mysqli_execute_query() validation errors.
 -    (David Carlier)
 -
  - Opcache:
 +  . Fixed bug GH-22265 (Another tailcall vm_interrupt bug). (Levi Morrison)
    . Fixed bug GH-20469 (Unsafe inheritance cache replay with reentrant
      autoloading). (Levi Morrison)
 -
 -- OpenSSL:
 -  . Fixed bug GH-22187 (Memory corruption (zend_mm_heap corrupted) in
 -    openssl_encrypt with AES-WRAP-PAD). (David Carlier)
 +  . Fixed bug GH-21972 (Corrupted variable type when a typed by-value return
 +    contains a reference wrapper). (Weilin Du)

  - Phar:
    . Fixed a bypass of the magic ".phar" directory protection in
diff --cc ext/intl/locale/locale_methods.cpp
index 57c3fd396f2,b5d48257338..f98ea55a185
--- a/ext/intl/locale/locale_methods.cpp
+++ b/ext/intl/locale/locale_methods.cpp
@@@ -1431,18 -1435,15 +1431,19 @@@ static zend_string* lookup_loc_range(co
  			zend_argument_type_error(2, "must only contain string values");
  			LOOKUP_CLEAN_RETURN(NULL);
  		}
 +		if (zend_str_has_nul_byte(Z_STR_P(ele_value))) {
 +			zend_argument_value_error(2, "must not contain any null bytes");
 +			LOOKUP_CLEAN_RETURN(NULL);
 +		}
- 		cur_arr[cur_arr_len*2] = estrndup(Z_STRVAL_P(ele_value), Z_STRLEN_P(ele_value));
- 		result = strToMatch(Z_STRVAL_P(ele_value), cur_arr[cur_arr_len*2]);
+ 		i = cur_arr_len*2;
+ 		cur_arr[i] = estrndup(Z_STRVAL_P(ele_value), Z_STRLEN_P(ele_value));
+ 		cur_arr_len++;
+ 		result = strToMatch(Z_STRVAL_P(ele_value), cur_arr[i]);
  		if(result == 0) {
 -			intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag", 0);
 +			intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "unable to canonicalize lang_tag");
  			LOOKUP_CLEAN_RETURN(NULL);
  		}
- 		cur_arr[cur_arr_len*2+1] = Z_STRVAL_P(ele_value);
- 		cur_arr_len++ ;
+ 		cur_arr[i+1] = Z_STRVAL_P(ele_value);
  	} ZEND_HASH_FOREACH_END(); /* end of for */

  	/* Canonicalize array elements */
diff --cc ext/intl/tests/locale_lookup_invalid_language_tag.phpt
index 00000000000,6f7c7517f95..ea4e3ec1cd0
mode 000000,100644..100644
--- a/ext/intl/tests/locale_lookup_invalid_language_tag.phpt
+++ b/ext/intl/tests/locale_lookup_invalid_language_tag.phpt
@@@ -1,0 -1,35 +1,35 @@@
+ --TEST--
+ Locale::lookup() returns null for invalid language tags
+ --EXTENSIONS--
+ intl
+ --FILE--
+ <?php
+
+ var_dump(Locale::lookup([''], 'de-DE', false, 'en-US'));
+ var_dump(intl_get_error_message());
+
+ var_dump(locale_lookup([''], 'de-DE', false, 'en-US'));
+ var_dump(intl_get_error_message());
+
+ ini_set('intl.use_exceptions', '1');
+
+ try {
+     Locale::lookup([''], 'de-DE', false, 'en-US');
+ } catch (IntlException $e) {
+     echo $e->getMessage(), PHP_EOL;
+ }
+
+ try {
+     locale_lookup([''], 'de-DE', false, 'en-US');
+ } catch (IntlException $e) {
+     echo $e->getMessage(), PHP_EOL;
+ }
+
+ ?>
+ --EXPECT--
+ NULL
 -string(75) "lookup_loc_range: unable to canonicalize lang_tag: U_ILLEGAL_ARGUMENT_ERROR"
++string(57) "unable to canonicalize lang_tag: U_ILLEGAL_ARGUMENT_ERROR"
+ NULL
 -string(75) "lookup_loc_range: unable to canonicalize lang_tag: U_ILLEGAL_ARGUMENT_ERROR"
 -lookup_loc_range: unable to canonicalize lang_tag
 -lookup_loc_range: unable to canonicalize lang_tag
++string(57) "unable to canonicalize lang_tag: U_ILLEGAL_ARGUMENT_ERROR"
++unable to canonicalize lang_tag
++unable to canonicalize lang_tag