Commit 305a60b0d2d for php.net
commit 305a60b0d2d3bbf2b2091e68199950c4d42b3739
Author: Weilin Du <weilindu@php.net>
Date: Sun Sep 20 00:02:27 2026 +0800
ext/intl: Optimize callback handling in IntlChar::enumCharTypes() (#23748)
Just pass the FCC rather than a FCI/FCC pair is safe here.
Co-authored-by: Gina Peter Banyard <girgias@php.net>
diff --git a/UPGRADING b/UPGRADING
index 5e019b44cd5..f2a2ef8f5e7 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -1111,6 +1111,7 @@ PHP 8.6 UPGRADE NOTES
their returned arrays.
. Improved performance of transliterator_list_ids() and
resourcebundle_locales() by pre-allocating their returned arrays.
+ . Optimized callback invocation in IntlChar::enumCharTypes().
- JSON:
. Improve performance of encoding arrays and objects.
diff --git a/ext/intl/uchar/tests/enumCharTypes_trampoline.phpt b/ext/intl/uchar/tests/enumCharTypes_trampoline.phpt
new file mode 100644
index 00000000000..014493cb6b3
--- /dev/null
+++ b/ext/intl/uchar/tests/enumCharTypes_trampoline.phpt
@@ -0,0 +1,58 @@
+--TEST--
+IntlChar::enumCharTypes() with trampoline callbacks
+--EXTENSIONS--
+intl
+--FILE--
+<?php
+function testTrampoline(string $name, array $arguments): bool {
+ // Only print the first two ranges to avoid ICU version dependencies.
+ if ($arguments[0] < 33) {
+ echo $name, ': ', implode(', ', $arguments), "\n";
+ }
+ if ($name === 'trampolineThrow' && $arguments[0] === 32) {
+ throw new Exception('Stop enumeration');
+ }
+ return false;
+}
+
+class TrampolineTest {
+ public function __call(string $name, array $arguments) {
+ return testTrampoline($name, $arguments);
+ }
+ public static function __callStatic(string $name, array $arguments) {
+ return testTrampoline($name, $arguments);
+ }
+}
+
+foreach ([new TrampolineTest(), TrampolineTest::class] as $target) {
+ echo is_object($target) ? "__call\n" : "__callStatic\n";
+ foreach (['trampoline', 'trampolineThrow', 'trampoline'] as $method) {
+ try {
+ var_dump(IntlChar::enumCharTypes([$target, $method]));
+ } catch (Exception $e) {
+ echo $e->getMessage(), "\n";
+ }
+ }
+}
+?>
+--EXPECT--
+__call
+trampoline: 0, 32, 15
+trampoline: 32, 33, 12
+NULL
+trampolineThrow: 0, 32, 15
+trampolineThrow: 32, 33, 12
+Stop enumeration
+trampoline: 0, 32, 15
+trampoline: 32, 33, 12
+NULL
+__callStatic
+trampoline: 0, 32, 15
+trampoline: 32, 33, 12
+NULL
+trampolineThrow: 0, 32, 15
+trampolineThrow: 32, 33, 12
+Stop enumeration
+trampoline: 0, 32, 15
+trampoline: 32, 33, 12
+NULL
diff --git a/ext/intl/uchar/uchar.cpp b/ext/intl/uchar/uchar.cpp
index 89bc0a31733..1319ec67908 100644
--- a/ext/intl/uchar/uchar.cpp
+++ b/ext/intl/uchar/uchar.cpp
@@ -188,16 +188,11 @@ IC_METHOD(getNumericValue) {
/* }}} */
/* {{{ */
-typedef struct _enumCharType_data {
- zend_fcall_info fci;
- zend_fcall_info_cache fci_cache;
-} enumCharType_data;
-static UBool enumCharType_callback(enumCharType_data *context,
+static UBool enumCharType_callback(const void *context,
UChar32 start, UChar32 limit, UCharCategory type) {
- zval retval;
+ const zend_fcall_info_cache *fcc = static_cast<const zend_fcall_info_cache *>(context);
zval args[3];
- ZVAL_NULL(&retval);
/* Note that $start is INclusive, while $limit is EXclusive
* Therefore (0, 32, 15) means CPs 0..31 are of type 15
*/
@@ -205,27 +200,19 @@ static UBool enumCharType_callback(enumCharType_data *context,
ZVAL_LONG(&args[1], limit);
ZVAL_LONG(&args[2], type);
- context->fci.retval = &retval;
- context->fci.param_count = 3;
- context->fci.params = args;
-
- if (zend_call_function(&context->fci, &context->fci_cache) == FAILURE) {
- intl_error_set_code(NULL, U_INTERNAL_PROGRAM_ERROR);
- intl_errors_set_custom_msg(NULL, "enumCharTypes callback failed");
- zval_ptr_dtor(&retval);
- return 0;
- }
- zval_ptr_dtor(&retval);
- return 1;
+ zend_call_known_fcc(fcc, NULL, 3, args, NULL);
+ return !EG(exception);
}
IC_METHOD(enumCharTypes) {
- enumCharType_data context;
+ zend_fcall_info fci;
+ zend_fcall_info_cache fcc;
ZEND_PARSE_PARAMETERS_START(1, 1)
- Z_PARAM_FUNC(context.fci, context.fci_cache)
+ Z_PARAM_FUNC_NO_TRAMPOLINE_FREE(fci, fcc)
ZEND_PARSE_PARAMETERS_END();
- u_enumCharTypes((UCharEnumTypeRange*)enumCharType_callback, &context);
+ u_enumCharTypes(enumCharType_callback, &fcc);
+ zend_release_fcall_info_cache(&fcc);
}
/* }}} */