[php-src] master: Add Locale::getDisplayKeyword() and Locale::getDisplayKeywordValue() (#22264)

Weilin Du via GitHub <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Weilin Du (LamentXU123)
Committer: GitHub (web-flow)
Pusher: LamentXU123
Date: 2026-06-30T14:52:10+08:00

Commit: https://github.com/php/php-src/commit/0c1d809a75d05963511eaee8057f6147051dde30
Raw diff: https://github.com/php/php-src/commit/0c1d809a75d05963511eaee8057f6147051dde30.diff

Add Locale::getDisplayKeyword() and Locale::getDisplayKeywordValue() (#22264)

implement RFC: 
https://wiki.php.net/rfc/getdisplaykeyword_and_getdisplaykeywordvalue

Changed paths:
  A  ext/intl/tests/locale_get_display_keyword.phpt
  A  ext/intl/tests/locale_get_display_keyword_error.phpt
  A  ext/intl/tests/locale_get_display_keyword_null_bytes.phpt
  M  NEWS
  M  UPGRADING
  M  ext/intl/locale/locale.stub.php
  M  ext/intl/locale/locale_arginfo.h
  M  ext/intl/locale/locale_methods.cpp
  M  ext/intl/php_intl.stub.php
  M  ext/intl/php_intl_arginfo.h
  M  ext/intl/tests/locale/bug74993.phpt
  M  ext/intl/tests/ut_common.inc


Diff:

diff --git a/NEWS b/NEWS
index 308ebe4d8f2e..9d326431321c 100644
--- a/NEWS
+++ b/NEWS
@@ -88,6 +88,9 @@ PHP                                                                        NEWS
 - Intl:
   . Fixed malformed ResourceBundle::get() error message when fallback is
     disabled. (Weilin Du)
+  . Added Locale::getDisplayKeyword() and Locale::getDisplayKeywordValue(),
+    with the alias of locale_get_display_keyword() and
+    locale_get_display_keyword_value() respectively. (Weilin Du)
   . Fix incorrect argument positions for invalid start/end arguments in
     transliterator_transliterate(). (Weilin Du)
   . Fixed IntlTimeZone::getDisplayName() to synchronize object error state
diff --git a/UPGRADING b/UPGRADING
index 1c75894512f9..02aa821acecf 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -208,6 +208,10 @@ PHP 8.6 UPGRADE NOTES
   . finfo_file() now works with remote streams.
 
 - Intl:
+  . Added Locale::getDisplayKeyword() and Locale::getDisplayKeywordValue(),
+    with the alias of locale_get_display_keyword() and
+    locale_get_display_keyword_value() respectively.
+    RFC: https://wiki.php.net/rfc/getdisplaykeyword_and_getdisplaykeywordvalue
   . Added IntlNumberRangeFormatter class to format an interval of two numbers
     with a given skeleton, locale, IntlNumberRangeFormatter::COLLAPSE_AUTO,
     IntlNumberRangeFormatter::COLLAPSE_NONE,
diff --git a/ext/intl/locale/locale.stub.php b/ext/intl/locale/locale.stub.php
index 03813ecaf194..fc012768de9f 100644
--- a/ext/intl/locale/locale.stub.php
+++ b/ext/intl/locale/locale.stub.php
@@ -92,6 +92,16 @@ public static function getDisplayLanguage(string $locale, ?string $displayLocale
      */
     public static function getDisplayVariant(string $locale, ?string $displayLocale = null): string|false {}
 
+    /**
+     * @alias locale_get_display_keyword
+     */
+    public static function getDisplayKeyword(string $keyword, ?string $displayLocale = null): string|false {}
+
+    /**
+     * @alias locale_get_display_keyword_value
+     */
+    public static function getDisplayKeywordValue(string $locale, string $keyword, ?string $displayLocale = null): string|false {}
+
     /**
      * @tentative-return-type
      * @alias locale_compose
diff --git a/ext/intl/locale/locale_arginfo.h b/ext/intl/locale/locale_arginfo.h
index 1d6e6683e21a..a01f583b0451 100644
--- a/ext/intl/locale/locale_arginfo.h
+++ b/ext/intl/locale/locale_arginfo.h
@@ -1,5 +1,5 @@
 /* This is a generated file, edit locale.stub.php instead.
- * Stub hash: ff1f75bd34a52f57210734e2f5e29efb87566137 */
+ * Stub hash: 43dbc3aa294044e9cef923a5f39249dae86b67f0 */
 
 ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Locale_getDefault, 0, 0, IS_STRING, 0)
 ZEND_END_ARG_INFO()
@@ -33,6 +33,17 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_Locale_getDisplayVariant arginfo_class_Locale_getDisplayScript
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Locale_getDisplayKeyword, 0, 1, MAY_BE_STRING|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, keyword, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, displayLocale, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Locale_getDisplayKeywordValue, 0, 2, MAY_BE_STRING|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, locale, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, keyword, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, displayLocale, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_Locale_composeLocale, 0, 1, MAY_BE_STRING|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, subtags, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
@@ -83,6 +94,8 @@ ZEND_FUNCTION(locale_get_display_region);
 ZEND_FUNCTION(locale_get_display_name);
 ZEND_FUNCTION(locale_get_display_language);
 ZEND_FUNCTION(locale_get_display_variant);
+ZEND_FUNCTION(locale_get_display_keyword);
+ZEND_FUNCTION(locale_get_display_keyword_value);
 ZEND_FUNCTION(locale_compose);
 ZEND_FUNCTION(locale_parse);
 ZEND_FUNCTION(locale_get_all_variants);
@@ -106,6 +119,8 @@ static const zend_function_entry class_Locale_methods[] = {
 	ZEND_RAW_FENTRY("getDisplayName", zif_locale_get_display_name, arginfo_class_Locale_getDisplayName, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, NULL)
 	ZEND_RAW_FENTRY("getDisplayLanguage", zif_locale_get_display_language, arginfo_class_Locale_getDisplayLanguage, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, NULL)
 	ZEND_RAW_FENTRY("getDisplayVariant", zif_locale_get_display_variant, arginfo_class_Locale_getDisplayVariant, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, NULL)
+	ZEND_RAW_FENTRY("getDisplayKeyword", zif_locale_get_display_keyword, arginfo_class_Locale_getDisplayKeyword, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, NULL)
+	ZEND_RAW_FENTRY("getDisplayKeywordValue", zif_locale_get_display_keyword_value, arginfo_class_Locale_getDisplayKeywordValue, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, NULL)
 	ZEND_RAW_FENTRY("composeLocale", zif_locale_compose, arginfo_class_Locale_composeLocale, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, NULL)
 	ZEND_RAW_FENTRY("parseLocale", zif_locale_parse, arginfo_class_Locale_parseLocale, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, NULL)
 	ZEND_RAW_FENTRY("getAllVariants", zif_locale_get_all_variants, arginfo_class_Locale_getAllVariants, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, NULL)
diff --git a/ext/intl/locale/locale_methods.cpp b/ext/intl/locale/locale_methods.cpp
index 14fa2a8f6bf5..0733ffc8abb7 100644
--- a/ext/intl/locale/locale_methods.cpp
+++ b/ext/intl/locale/locale_methods.cpp
@@ -44,6 +44,8 @@ ZEND_EXTERN_MODULE_GLOBALS( intl )
 #define EXTLANG_PREFIX "a"
 #define PRIVATE_PREFIX "x"
 #define DISP_NAME "name"
+#define DISP_KEYWORD "keyword"
+#define DISP_KEYWORD_VALUE "keyword_value"
 
 #define MAX_NO_VARIANT  15
 #define MAX_NO_EXTLANG  3
@@ -671,6 +673,107 @@ static void get_icu_disp_value_src_php( const char* tag_name, INTERNAL_FUNCTION_
 }
 /* }}} */
 
+/* {{{
+ * common code shared by display keyword functions to get the value from ICU
+ }}} */
+static void get_icu_disp_keyword_value_src_php(const char* tag_name, INTERNAL_FUNCTION_PARAMETERS)
+{
+	char*       loc_name            = NULL;
+	size_t      loc_name_len        = 0;
+	char*       keyword_name        = NULL;
+	size_t      keyword_name_len    = 0;
+	char*       disp_loc_name       = NULL;
+	size_t      disp_loc_name_len   = 0;
+	int         free_loc_name       = 0;
+
+	UChar*      disp_name           = NULL;
+	int32_t     disp_name_len       = 0;
+	int32_t     buflen              = 512;
+	UErrorCode  status              = U_ZERO_ERROR;
+
+	zend_string* u8str;
+	char*       msg                 = NULL;
+
+	intl_error_reset( NULL );
+
+	if (strcmp(tag_name, DISP_KEYWORD) == 0) {
+		ZEND_PARSE_PARAMETERS_START(1, 2)
+			Z_PARAM_PATH(keyword_name, keyword_name_len)
+			Z_PARAM_OPTIONAL
+			Z_PARAM_PATH_OR_NULL(disp_loc_name, disp_loc_name_len)
+		ZEND_PARSE_PARAMETERS_END();
+	} else {
+		ZEND_PARSE_PARAMETERS_START(2, 3)
+			Z_PARAM_PATH(loc_name, loc_name_len)
+			Z_PARAM_PATH(keyword_name, keyword_name_len)
+			Z_PARAM_OPTIONAL
+			Z_PARAM_PATH_OR_NULL(disp_loc_name, disp_loc_name_len)
+		ZEND_PARSE_PARAMETERS_END();
+
+		if (loc_name_len > ULOC_FULLNAME_CAPACITY) {
+			intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "name too long");
+			RETURN_FALSE;
+		}
+
+		if (loc_name_len == 0) {
+			loc_name = (char *)intl_locale_get_default();
+		}
+	}
+
+	if (!disp_loc_name) {
+		disp_loc_name = estrdup(intl_locale_get_default());
+		free_loc_name = 1;
+	}
+
+	do {
+		disp_name = reinterpret_cast<UChar *>(erealloc(disp_name, buflen * sizeof(UChar)));
+		disp_name_len = buflen;
+
+		if (strcmp(tag_name, DISP_KEYWORD) == 0) {
+			buflen = uloc_getDisplayKeyword(keyword_name, disp_loc_name, disp_name, disp_name_len, &status);
+		} else {
+			buflen = uloc_getDisplayKeywordValue(loc_name, keyword_name, disp_loc_name, disp_name, disp_name_len, &status);
+		}
+
+		/* U_STRING_NOT_TERMINATED_WARNING is admissible here; don't look for it */
+		if (U_FAILURE(status)) {
+			if (status == U_BUFFER_OVERFLOW_ERROR) {
+				status = U_ZERO_ERROR;
+				continue;
+			}
+
+			spprintf(&msg, 0, "unable to get locale %s", tag_name);
+			intl_error_set( NULL, status, msg);
+			efree(msg);
+			if (disp_name) {
+				efree(disp_name);
+			}
+			if (free_loc_name) {
+				efree((void *)disp_loc_name);
+				disp_loc_name = NULL;
+			}
+			RETURN_FALSE;
+		}
+	} while (buflen > disp_name_len);
+
+	if (free_loc_name) {
+		efree((void *)disp_loc_name);
+		disp_loc_name = NULL;
+	}
+
+	u8str = intl_convert_utf16_to_utf8(disp_name, buflen, &status);
+	efree(disp_name);
+	if (!u8str) {
+		spprintf(&msg, 0, "error converting display name for %s to UTF-8", tag_name);
+		intl_error_set( NULL, status, msg);
+		efree(msg);
+		RETURN_FALSE;
+	}
+
+	RETVAL_NEW_STR(u8str);
+}
+/* }}} */
+
 /* {{{ gets the name for the $locale in $in_locale or default_locale */
 U_CFUNC PHP_FUNCTION(locale_get_display_name)
 {
@@ -711,6 +814,20 @@ U_CFUNC PHP_FUNCTION(locale_get_display_variant)
 {
 	get_icu_disp_value_src_php( LOC_VARIANT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
 }
+/* }}} */
+
+/* {{{ gets the keyword display label in $in_locale or default_locale */
+U_CFUNC PHP_FUNCTION(locale_get_display_keyword)
+{
+	get_icu_disp_keyword_value_src_php(DISP_KEYWORD, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ gets the keyword value display label in $in_locale or default_locale */
+U_CFUNC PHP_FUNCTION(locale_get_display_keyword_value)
+{
+	get_icu_disp_keyword_value_src_php(DISP_KEYWORD_VALUE, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
 /* }}} */
 
  /* {{{ return an associative array containing keyword-value
diff --git a/ext/intl/php_intl.stub.php b/ext/intl/php_intl.stub.php
index 4bcb8587f786..1e6c5cacf203 100644
--- a/ext/intl/php_intl.stub.php
+++ b/ext/intl/php_intl.stub.php
@@ -487,6 +487,10 @@ function locale_get_display_language(string $locale, ?string $displayLocale = nu
 
 function locale_get_display_variant(string $locale, ?string $displayLocale = null): string|false {}
 
+function locale_get_display_keyword(string $keyword, ?string $displayLocale = null): string|false {}
+
+function locale_get_display_keyword_value(string $locale, string $keyword, ?string $displayLocale = null): string|false {}
+
 function locale_compose(array $subtags): string|false {}
 
 function locale_parse(string $locale): ?array {}
diff --git a/ext/intl/php_intl_arginfo.h b/ext/intl/php_intl_arginfo.h
index 81160349980c..00de5986f1ef 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: c52fd0def2530be628beedbbcdcfecdcb07449a8 */
+ * Stub hash: f94e7c9cc372878f1f8bd0e948092ea72076e687 */
 
 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")
@@ -553,6 +553,17 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_locale_get_display_variant arginfo_locale_get_display_script
 
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_locale_get_display_keyword, 0, 1, MAY_BE_STRING|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, keyword, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, displayLocale, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_locale_get_display_keyword_value, 0, 2, MAY_BE_STRING|MAY_BE_FALSE)
+	ZEND_ARG_TYPE_INFO(0, locale, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO(0, keyword, IS_STRING, 0)
+	ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, displayLocale, IS_STRING, 1, "null")
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_locale_compose, 0, 1, MAY_BE_STRING|MAY_BE_FALSE)
 	ZEND_ARG_TYPE_INFO(0, subtags, IS_ARRAY, 0)
 ZEND_END_ARG_INFO()
@@ -941,6 +952,8 @@ ZEND_FUNCTION(locale_get_display_region);
 ZEND_FUNCTION(locale_get_display_name);
 ZEND_FUNCTION(locale_get_display_language);
 ZEND_FUNCTION(locale_get_display_variant);
+ZEND_FUNCTION(locale_get_display_keyword);
+ZEND_FUNCTION(locale_get_display_keyword_value);
 ZEND_FUNCTION(locale_compose);
 ZEND_FUNCTION(locale_parse);
 ZEND_FUNCTION(locale_get_all_variants);
@@ -1133,6 +1146,8 @@ static const zend_function_entry ext_functions[] = {
 	ZEND_FE(locale_get_display_name, arginfo_locale_get_display_name)
 	ZEND_FE(locale_get_display_language, arginfo_locale_get_display_language)
 	ZEND_FE(locale_get_display_variant, arginfo_locale_get_display_variant)
+	ZEND_FE(locale_get_display_keyword, arginfo_locale_get_display_keyword)
+	ZEND_FE(locale_get_display_keyword_value, arginfo_locale_get_display_keyword_value)
 	ZEND_FE(locale_compose, arginfo_locale_compose)
 	ZEND_FE(locale_parse, arginfo_locale_parse)
 	ZEND_FE(locale_get_all_variants, arginfo_locale_get_all_variants)
diff --git a/ext/intl/tests/locale/bug74993.phpt b/ext/intl/tests/locale/bug74993.phpt
index 1d0a1a97b75c..7bcf931c4e2b 100644
--- a/ext/intl/tests/locale/bug74993.phpt
+++ b/ext/intl/tests/locale/bug74993.phpt
@@ -5,6 +5,8 @@ intl
 --FILE--
 <?php
 $funcs = [
+    'locale_get_display_keyword',
+    'locale_get_display_keyword_value',
     'locale_get_display_language',
     'locale_get_display_name',
     'locale_get_display_region',
@@ -19,6 +21,23 @@ foreach ($funcs as $func) {
 }
 ?>
 --EXPECT--
+Function [ <internal:intl> function locale_get_display_keyword ] {
+
+  - Parameters [2] {
+    Parameter #0 [ <required> string $keyword ]
+    Parameter #1 [ <optional> ?string $displayLocale = null ]
+  }
+  - Return [ string|false ]
+}
+Function [ <internal:intl> function locale_get_display_keyword_value ] {
+
+  - Parameters [3] {
+    Parameter #0 [ <required> string $locale ]
+    Parameter #1 [ <required> string $keyword ]
+    Parameter #2 [ <optional> ?string $displayLocale = null ]
+  }
+  - Return [ string|false ]
+}
 Function [ <internal:intl> function locale_get_display_language ] {
 
   - Parameters [2] {
diff --git a/ext/intl/tests/locale_get_display_keyword.phpt b/ext/intl/tests/locale_get_display_keyword.phpt
new file mode 100644
index 000000000000..9d53d7a02586
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_keyword.phpt
@@ -0,0 +1,40 @@
+--TEST--
+locale_get_display_keyword() basic
+--EXTENSIONS--
+intl
+--FILE--
+<?php
+
+function ut_main()
+{
+    $default = ut_loc_get_default();
+    ut_loc_set_default('en');
+
+    $keyword = ut_loc_get_display_keyword('calendar', 'en');
+    var_dump($keyword);
+    var_dump(ut_loc_get_display_keyword('calendar', null) === $keyword);
+
+    $keywordValue = ut_loc_get_display_keyword_value('de_DE@calendar=gregorian', 'calendar', 'en');
+    var_dump($keywordValue);
+    var_dump(ut_loc_get_display_keyword_value('de_DE@calendar=gregorian', 'calendar', null) === $keywordValue);
+
+    $collationValue = ut_loc_get_display_keyword_value('de_DE@collation=phonebook', 'collation', 'en');
+    var_dump($collationValue);
+
+    ut_loc_set_default($default);
+}
+
+include_once 'ut_common.inc';
+ut_run();
+?>
+--EXPECTREGEX--
+string\([1-9][0-9]*\) "[A-Za-z ]*Calendar[A-Za-z ]*"
+bool\(true\)
+string\([1-9][0-9]*\) "[A-Za-z ]*Gregorian[A-Za-z ]*"
+bool\(true\)
+string\([1-9][0-9]*\) "[A-Za-z ]*Phonebook[A-Za-z ]*"
+string\([1-9][0-9]*\) "[A-Za-z ]*Calendar[A-Za-z ]*"
+bool\(true\)
+string\([1-9][0-9]*\) "[A-Za-z ]*Gregorian[A-Za-z ]*"
+bool\(true\)
+string\([1-9][0-9]*\) "[A-Za-z ]*Phonebook[A-Za-z ]*"
diff --git a/ext/intl/tests/locale_get_display_keyword_error.phpt b/ext/intl/tests/locale_get_display_keyword_error.phpt
new file mode 100644
index 000000000000..6f489ccfe8fa
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_keyword_error.phpt
@@ -0,0 +1,20 @@
+--TEST--
+locale_get_display_keyword_value() error path
+--EXTENSIONS--
+intl
+--FILE--
+<?php
+
+$locale = str_repeat('*', 256);
+
+var_dump(Locale::getDisplayKeywordValue($locale, 'calendar', 'en'));
+var_dump(intl_get_error_message());
+var_dump(locale_get_display_keyword_value($locale, 'calendar', 'en'));
+var_dump(intl_get_error_message());
+
+?>
+--EXPECT--
+bool(false)
+string(73) "Locale::getDisplayKeywordValue(): name too long: U_ILLEGAL_ARGUMENT_ERROR"
+bool(false)
+string(75) "locale_get_display_keyword_value(): name too long: U_ILLEGAL_ARGUMENT_ERROR"
diff --git a/ext/intl/tests/locale_get_display_keyword_null_bytes.phpt b/ext/intl/tests/locale_get_display_keyword_null_bytes.phpt
new file mode 100644
index 000000000000..fe83ac1b731f
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_keyword_null_bytes.phpt
@@ -0,0 +1,40 @@
+--TEST--
+locale_get_display_keyword() throwing null bytes exceptions.
+--EXTENSIONS--
+intl
+--FILE--
+<?php
+
+function ut_main()
+{
+    $calls = [
+        fn() => ut_loc_get_display_keyword("cur\0rency", "fr"),
+        fn() => ut_loc_get_display_keyword("currency", "f\0r"),
+        fn() => ut_loc_get_display_keyword_value("de_DE@calendar=gregorian\0", "calendar", "en"),
+        fn() => ut_loc_get_display_keyword_value("de_DE@calendar=gregorian", "cal\0endar", "en"),
+        fn() => ut_loc_get_display_keyword_value("de_DE@calendar=gregorian", "calendar", "e\0n"),
+    ];
+
+    foreach ($calls as $call) {
+        try {
+            $call();
+        } catch (\ValueError $e) {
+            echo $e->getMessage(), PHP_EOL;
+        }
+    }
+}
+
+include_once 'ut_common.inc';
+ut_run();
+?>
+--EXPECT--
+Locale::getDisplayKeyword(): Argument #1 ($keyword) must not contain any null bytes
+Locale::getDisplayKeyword(): Argument #2 ($displayLocale) must not contain any null bytes
+Locale::getDisplayKeywordValue(): Argument #1 ($locale) must not contain any null bytes
+Locale::getDisplayKeywordValue(): Argument #2 ($keyword) must not contain any null bytes
+Locale::getDisplayKeywordValue(): Argument #3 ($displayLocale) must not contain any null bytes
+locale_get_display_keyword(): Argument #1 ($keyword) must not contain any null bytes
+locale_get_display_keyword(): Argument #2 ($displayLocale) must not contain any null bytes
+locale_get_display_keyword_value(): Argument #1 ($locale) must not contain any null bytes
+locale_get_display_keyword_value(): Argument #2 ($keyword) must not contain any null bytes
+locale_get_display_keyword_value(): Argument #3 ($displayLocale) must not contain any null bytes
diff --git a/ext/intl/tests/ut_common.inc b/ext/intl/tests/ut_common.inc
index fdc013dea41c..26f33f75f7fe 100644
--- a/ext/intl/tests/ut_common.inc
+++ b/ext/intl/tests/ut_common.inc
@@ -272,6 +272,14 @@ function ut_loc_get_display_variant( $locale , $dispLocale )
 {
     return $GLOBALS['oo-mode'] ? Locale::getDisplayVariant( $locale , $dispLocale ) : locale_get_display_variant( $locale, $dispLocale  );
 }
+function ut_loc_get_display_keyword( $keyword , $dispLocale )
+{
+    return $GLOBALS['oo-mode'] ? Locale::getDisplayKeyword( $keyword , $dispLocale ) : locale_get_display_keyword( $keyword, $dispLocale );
+}
+function ut_loc_get_display_keyword_value( $locale , $keyword , $dispLocale )
+{
+    return $GLOBALS['oo-mode'] ? Locale::getDisplayKeywordValue( $locale , $keyword , $dispLocale ) : locale_get_display_keyword_value( $locale, $keyword, $dispLocale );
+}
 function ut_loc_locale_compose( $loc_parts_arr )
 {
     return $GLOBALS['oo-mode'] ? Locale::composeLocale( $loc_parts_arr ) : locale_compose( $loc_parts_arr );
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.