Commit 9cce500a504 for php.net

commit 9cce500a5043f2e9827a704a66682089e351a90b
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Tue Sep 1 08:33:05 2026 -0400

    [intl] Record the ICU error code on Spoofchecker failures (#23520)

    Spoofchecker::isSuspicious(), ::areConfusable(), ::areBidiConfusable(),
    ::setChecks(), ::setAllowedLocales() and ::setAllowedChars() warn on
    U_FAILURE but never record the code, so intl_get_error_code() still reads
    U_ZERO_ERROR after a failed call. They now record it, which is what
    SPOOFCHECKER_CHECK_STATUS in spoofchecker_class.h already prescribes for
    this class.

    Closes GH-23520

diff --git a/NEWS b/NEWS
index f3a3914a263..c2735429778 100644
--- a/NEWS
+++ b/NEWS
@@ -24,6 +24,8 @@ PHP                                                                        NEWS
     read. (iliaal)
   . Fixed a use-after-free when IntlRuleBasedBreakIterator is constructed
     from compiled rules. (iliaal)
+  . Fixed Spoofchecker methods not recording the ICU error code when an ICU
+    call fails. (Ilia Alshanetsky)

 - PDO_PGSQL:
   . Added Pdo\Pgsql::ATTR_CHUNK_SIZE to fetch a result set in chunks of the
diff --git a/ext/intl/spoofchecker/spoofchecker_main.cpp b/ext/intl/spoofchecker/spoofchecker_main.cpp
index d7e1b4e0553..7a3ffd9b809 100644
--- a/ext/intl/spoofchecker/spoofchecker_main.cpp
+++ b/ext/intl/spoofchecker/spoofchecker_main.cpp
@@ -46,6 +46,7 @@ U_CFUNC PHP_METHOD(Spoofchecker, isSuspicious)
 	ret = intl_icu_compat_uspoof_check_utf8(co->uspoof, ZSTR_VAL(text), ZSTR_LEN(text), co->uspoofres, SPOOFCHECKER_ERROR_CODE_P(co));

 	if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
+		intl_error_set_code(NULL, SPOOFCHECKER_ERROR_CODE(co));
 		php_error_docref(NULL, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));

 		if (intl_icu_compat_uspoof_check_result_mismatch(co->uspoofres, ret, &errmask, SPOOFCHECKER_ERROR_CODE_P(co))) {
@@ -83,6 +84,7 @@ U_CFUNC PHP_METHOD(Spoofchecker, areConfusable)
 		ret = uspoof_areConfusableUTF8(co->uspoof, ZSTR_VAL(s1), (int32_t)ZSTR_LEN(s1), ZSTR_VAL(s2), (int32_t)ZSTR_LEN(s2), SPOOFCHECKER_ERROR_CODE_P(co));
 	}
 	if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
+		intl_error_set_code(NULL, SPOOFCHECKER_ERROR_CODE(co));
 		php_error_docref(NULL, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));
 		RETURN_TRUE;
 	}
@@ -109,6 +111,7 @@ U_CFUNC PHP_METHOD(Spoofchecker, setAllowedLocales)
 	uspoof_setAllowedLocales(co->uspoof, ZSTR_VAL(locales), SPOOFCHECKER_ERROR_CODE_P(co));

 	if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
+		intl_error_set_code(NULL, SPOOFCHECKER_ERROR_CODE(co));
 		php_error_docref(NULL, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));
 		return;
 	}
@@ -130,6 +133,7 @@ U_CFUNC PHP_METHOD(Spoofchecker, setChecks)
 	uspoof_setChecks(co->uspoof, checks, SPOOFCHECKER_ERROR_CODE_P(co));

 	if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
+		intl_error_set_code(NULL, SPOOFCHECKER_ERROR_CODE(co));
 		php_error_docref(NULL, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));
 	}
 }
@@ -220,6 +224,7 @@ U_CFUNC PHP_METHOD(Spoofchecker, setAllowedChars)
 	efree(upattern);

 	if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
+		intl_error_set_code(NULL, SPOOFCHECKER_ERROR_CODE(co));
 		php_error_docref(NULL, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));
 	}
 }
@@ -355,6 +360,7 @@ U_CFUNC PHP_METHOD(Spoofchecker, areBidiConfusable)
 		ret = uspoof_areBidiConfusableUTF8(co->uspoof, (UBiDiDirection)direction, ZSTR_VAL(s1), (int32_t)ZSTR_LEN(s1), ZSTR_VAL(s2), (int32_t)ZSTR_LEN(s2), SPOOFCHECKER_ERROR_CODE_P(co));
 	}
 	if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
+		intl_error_set_code(NULL, SPOOFCHECKER_ERROR_CODE(co));
 		php_error_docref(NULL, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));
 		RETURN_TRUE;
 	}
diff --git a/ext/intl/tests/spoofchecker_setchecks_error_code.phpt b/ext/intl/tests/spoofchecker_setchecks_error_code.phpt
new file mode 100644
index 00000000000..a9c1eaeab45
--- /dev/null
+++ b/ext/intl/tests/spoofchecker_setchecks_error_code.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Spoofchecker::setChecks() records the ICU error code
+--EXTENSIONS--
+intl
+--SKIPIF--
+<?php if (!class_exists("Spoofchecker")) print 'skip'; ?>
+--FILE--
+<?php
+
+$s = new Spoofchecker();
+$s->setChecks(1 << 20);
+var_dump(intl_get_error_code(), intl_get_error_message());
+
+?>
+--EXPECTF--
+Warning: Spoofchecker::setChecks(): (1) U_ILLEGAL_ARGUMENT_ERROR in %s on line %d
+int(1)
+string(24) "U_ILLEGAL_ARGUMENT_ERROR"