From 7a1c2612c01d9bd4d0c2d128fb5b2bf8f0a2e671 Mon Sep 17 00:00:00 2001 From: tekimen Date: Wed, 4 Mar 2026 09:47:26 +0900 Subject: [PATCH] [RFC] Add grapheme_strrev function (#20949) * [RFC] Add grapheme_strrev function Add more tests Arabic for grapheme_strrev function. --- NEWS | 1 + UPGRADING | 4 ++ ext/intl/grapheme/grapheme_string.cpp | 59 ++++++++++++++++++++++++++ ext/intl/php_intl.stub.php | 2 + ext/intl/php_intl_arginfo.h | 8 +++- ext/intl/tests/grapheme_strrev.phpt | Bin 0 -> 866 bytes 6 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 ext/intl/tests/grapheme_strrev.phpt diff --git a/NEWS b/NEWS index 75332b89a1e..4e1196fb094 100644 --- a/NEWS +++ b/NEWS @@ -37,6 +37,7 @@ PHP NEWS (BogdanUngureanu) . Fixed bug GH-20426 (Spoofchecker::setRestrictionLevel() error message suggests missing constants). (DanielEScherzer) + . Added grapheme_strrev (Yuya Hamada) - JSON: . Enriched JSON last error / exception message with error location. diff --git a/UPGRADING b/UPGRADING index 7e47d0ba481..454a7b900f3 100644 --- a/UPGRADING +++ b/UPGRADING @@ -137,6 +137,10 @@ PHP 8.6 UPGRADE NOTES . Added ReflectionProperty::isReadable() and ReflectionProperty::isWritable(). RFC: https://wiki.php.net/rfc/isreadable-iswriteable +- Intl: + . `grapheme_strrev()` returns strrev for grapheme cluster unit. + RFC: https://wiki.php.net/rfc/grapheme_strrev + - Standard: . `clamp()` returns the given value if in range, else return the nearest bound. diff --git a/ext/intl/grapheme/grapheme_string.cpp b/ext/intl/grapheme/grapheme_string.cpp index 6dd5a002a65..36c0cc0f732 100644 --- a/ext/intl/grapheme/grapheme_string.cpp +++ b/ext/intl/grapheme/grapheme_string.cpp @@ -1135,4 +1135,63 @@ out_ustring1: efree(ustring1); } +U_CFUNC PHP_FUNCTION(grapheme_strrev) +{ + zend_string *string; + UText *ut = nullptr; + UErrorCode ustatus = U_ZERO_ERROR; + UBreakIterator *bi; + char *pstr, *end, *p; + zend_string *ret; + int32_t pos = 0, current = 0, end_len = 0; + unsigned char u_break_iterator_buffer[U_BRK_SAFECLONE_BUFFERSIZE]; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(string) + ZEND_PARSE_PARAMETERS_END(); + + if (ZSTR_LEN(string) == 0) { + RETURN_EMPTY_STRING(); + } + + pstr = ZSTR_VAL(string); + ut = utext_openUTF8(ut, pstr, ZSTR_LEN(string), &ustatus); + + if (U_FAILURE(ustatus)) { + intl_error_set_code(nullptr, ustatus); + intl_error_set_custom_msg(nullptr, "Error opening UTF-8 text"); + + RETVAL_FALSE; + goto close; + } + + bi = nullptr; + ustatus = U_ZERO_ERROR; + + bi = grapheme_get_break_iterator((void*)u_break_iterator_buffer, &ustatus ); + ret = zend_string_alloc(ZSTR_LEN(string), 0); + p = ZSTR_VAL(ret); + + ubrk_setUText(bi, ut, &ustatus); + pos = ubrk_last(bi); + if (pos == UBRK_DONE) { + goto ubrk_end; + } + + current = ZSTR_LEN(string); + for (end = pstr; pos != UBRK_DONE; ) { + pos = ubrk_previous(bi); + end_len = current - pos; + for (int32_t j = 0; j < end_len; j++) { + *p++ = *(pstr + pos + j); + } + current = pos; + } +ubrk_end: + RETVAL_NEW_STR(ret); + ubrk_close(bi); +close: + utext_close(ut); +} + /* }}} */ diff --git a/ext/intl/php_intl.stub.php b/ext/intl/php_intl.stub.php index 9a8f036865c..4bcb8587f78 100644 --- a/ext/intl/php_intl.stub.php +++ b/ext/intl/php_intl.stub.php @@ -445,6 +445,8 @@ function grapheme_str_split(string $string, int $length = 1): array|false {} function grapheme_levenshtein(string $string1, string $string2, int $insertion_cost = 1, int $replacement_cost = 1, int $deletion_cost = 1, string $locale = ""): int|false {} +function grapheme_strrev(string $string): string|false {} + /** @param int $next */ function grapheme_extract(string $haystack, int $size, int $type = GRAPHEME_EXTR_COUNT, int $offset = 0, &$next = null): string|false {} diff --git a/ext/intl/php_intl_arginfo.h b/ext/intl/php_intl_arginfo.h index e00e51420d4..81160349980 100644 --- a/ext/intl/php_intl_arginfo.h +++ b/ext/intl/php_intl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit php_intl.stub.php instead. - * Stub hash: d9e331c3a1ae46f8eae07ef0d39cb9990e74a0d1 */ + * Stub hash: c52fd0def2530be628beedbbcdcfecdcb07449a8 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_intlcal_create_instance, 0, 0, IntlCalendar, 1) ZEND_ARG_OBJ_TYPE_MASK(0, timezone, IntlTimeZone|DateTimeZone, MAY_BE_STRING|MAY_BE_NULL, "null") @@ -501,6 +501,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_grapheme_levenshtein, 0, 2, MAY_ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, locale, IS_STRING, 0, "\"\"") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_grapheme_strrev, 0, 1, MAY_BE_STRING|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_grapheme_extract, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, haystack, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, size, IS_LONG, 0) @@ -922,6 +926,7 @@ ZEND_FUNCTION(grapheme_strstr); ZEND_FUNCTION(grapheme_stristr); ZEND_FUNCTION(grapheme_str_split); ZEND_FUNCTION(grapheme_levenshtein); +ZEND_FUNCTION(grapheme_strrev); ZEND_FUNCTION(grapheme_extract); ZEND_FUNCTION(idn_to_ascii); ZEND_FUNCTION(idn_to_utf8); @@ -1113,6 +1118,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(grapheme_stristr, arginfo_grapheme_stristr) ZEND_FE(grapheme_str_split, arginfo_grapheme_str_split) ZEND_FE(grapheme_levenshtein, arginfo_grapheme_levenshtein) + ZEND_FE(grapheme_strrev, arginfo_grapheme_strrev) ZEND_FE(grapheme_extract, arginfo_grapheme_extract) ZEND_FE(idn_to_ascii, arginfo_idn_to_ascii) ZEND_FE(idn_to_utf8, arginfo_idn_to_utf8) diff --git a/ext/intl/tests/grapheme_strrev.phpt b/ext/intl/tests/grapheme_strrev.phpt new file mode 100644 index 0000000000000000000000000000000000000000..dff84fbba8e97cdee5af5c0c3ce346df018afd14 GIT binary patch literal 866 zcma))F>As=6vsQ?r+5Ux1dO<+)}f>z6kE`tP>Wo`*$6R)%cWFAkb-oOF5LvdF)kh4 zN&oh2N>~~ zhg4O+Y3Yq-{i@OA-93ldT)U`W>byFwxt5DEhtE2F+;e0FjS%psK#3<}p$f&Kh7v7~ zpL{;O3I~7vYP>*2{tVm3dszxmwbT&$-Mr|tXu_Y#|BK>_ZXqTBOv9Zf{Q8c!zfO>t=x@4(z1DF zqvtWZl$5Z%Gbm67sqLh$aqOjp`P9kCX=scPF|7e)Y&BFni?`xhKi}V