Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
10 changes: 10 additions & 0 deletions ext/intl/locale/locale.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,16 @@ public static function getDisplayVariant(string $locale, ?string $displayLocale

/**
* @tentative-return-type
Comment thread
LamentXU123 marked this conversation as resolved.
Outdated
* @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 {}

/**
* @alias locale_compose
*/
public static function composeLocale(array $subtags): string|false {}
Expand Down
19 changes: 17 additions & 2 deletions 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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: loc_name is bounded but keyword_name is not, in either branch. Unlike the locale-name overflow (bug 67397), uloc_getDisplayKeyword{,Value} grows the output buffer in the loop and echoes unknown keywords back, so I do not think an overlong keyword can crash. Noting it for symmetry; fine to leave as-is.

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 {}
Comment thread
TimWolla marked this conversation as resolved.

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"
Comment thread
LamentXU123 marked this conversation as resolved.
Outdated
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