Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions ext/intl/locale/locale.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@ public static function getDisplayLanguage(string $locale, ?string $displayLocale
*/
public static function getDisplayVariant(string $locale, ?string $displayLocale = null): string|false {}

/**
* @tentative-return-type
* @alias locale_get_display_keyword
*/
public static function getDisplayKeyword(string $keyword, ?string $displayLocale = null): string|false {}

/**
* @tentative-return-type
* @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
Expand Down
17 changes: 16 additions & 1 deletion ext/intl/locale/locale_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

117 changes: 117 additions & 0 deletions ext/intl/locale/locale_methods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions ext/intl/php_intl.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Expand Down
17 changes: 16 additions & 1 deletion ext/intl/php_intl_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions ext/intl/tests/locale/bug74993.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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] {
Expand Down
35 changes: 35 additions & 0 deletions ext/intl/tests/locale_get_display_keyword.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
--TEST--
locale_get_display_keyword() basic
--EXTENSIONS--
intl
--FILE--
<?php

function ut_main()
{
$default = ut_loc_get_default();
ut_loc_set_default('en');

var_dump(ut_loc_get_display_keyword('calendar', 'en'));
var_dump(ut_loc_get_display_keyword('calendar', null));
var_dump(ut_loc_get_display_keyword_value('de_DE@calendar=gregorian', 'calendar', 'en'));
var_dump(ut_loc_get_display_keyword_value('de_DE@calendar=gregorian', 'calendar', null));
var_dump(ut_loc_get_display_keyword_value('de_DE@collation=phonebook', 'collation', 'en'));

ut_loc_set_default($default);
}

include_once 'ut_common.inc';
ut_run();
?>
--EXPECT--
string(8) "Calendar"
string(8) "Calendar"
string(18) "Gregorian Calendar"
string(18) "Gregorian Calendar"
string(20) "Phonebook Sort Order"
string(8) "Calendar"
string(8) "Calendar"
string(18) "Gregorian Calendar"
string(18) "Gregorian Calendar"
string(20) "Phonebook Sort Order"
40 changes: 40 additions & 0 deletions ext/intl/tests/locale_get_display_keyword_null_bytes.phpt
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions ext/intl/tests/ut_common.inc
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down
Loading