Skip to content

Commit f511a2f

Browse files
committed
ext/intl: Fix Collator sort conversion error handling
1 parent 1805822 commit f511a2f

7 files changed

Lines changed: 111 additions & 15 deletions

File tree

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ PHP NEWS
2222
(Weilin Du)
2323

2424
- Intl:
25+
. Fixed Collator::sort(), collator_sort(), Collator::asort(), and
26+
collator_asort() to report string conversion errors through the intl error
27+
handler instead of emitting a warning and continuing with an empty string.
28+
(Weilin Du)
2529
. Fixed grammatical issues in Normalizer invalid form and IntlCalendar time
2630
zone offset error messages. (Weilin Du)
2731

UPGRADING

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ PHP 8.6 UPGRADE NOTES
7676
IntlDateFormatter::localtime()/datefmt_localtime() now raise a TypeError
7777
when the offset argument is not of type int instead of silently converting
7878
the value.
79+
. Collator::sort(), collator_sort(), Collator::asort(), and
80+
collator_asort() now report string conversion failures during comparison
81+
through the intl error mechanism and return false. With
82+
intl.use_exceptions enabled, these failures throw IntlException.
83+
Previously, these paths emitted a warning and compared the value as an
84+
empty string.
7985

8086
- PCNTL:
8187
. pcntl_alarm() now raises a ValueError if the seconds argument is

ext/intl/collator/collator_convert.cpp

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,19 @@ extern "C" {
3838
return retval; \
3939
}
4040

41+
static void collator_set_conversion_error(UErrorCode status, const char *message)
42+
{
43+
if (U_SUCCESS(status)) {
44+
status = U_MEMORY_ALLOCATION_ERROR;
45+
}
46+
47+
if (INTL_G(current_collator_error)) {
48+
intl_error_set(INTL_G(current_collator_error), status, message);
49+
} else {
50+
intl_error_set(nullptr, status, message);
51+
}
52+
}
53+
4154
/* {{{ collator_convert_hash_item_from_utf8_to_utf16 */
4255
static void collator_convert_hash_item_from_utf8_to_utf16(
4356
HashTable* hash, zval *hashData, zend_string *hashKey, zend_ulong hashIndex,
@@ -166,11 +179,11 @@ U_CFUNC zval* collator_convert_zstr_utf16_to_utf8( zval* utf16_zval, zval *rv )
166179
u8str = intl_convert_utf16_to_utf8(
167180
(UChar*) Z_STRVAL_P(utf16_zval), UCHARS( Z_STRLEN_P(utf16_zval) ), &status );
168181
if( !u8str ) {
169-
php_error( E_WARNING, "Error converting utf16 to utf8 in collator_convert_zval_utf16_to_utf8()" );
170-
ZVAL_EMPTY_STRING( rv );
171-
} else {
172-
ZVAL_NEW_STR( rv, u8str );
182+
collator_set_conversion_error(status, "Error converting string from UTF-16 to UTF-8");
183+
return nullptr;
173184
}
185+
186+
ZVAL_NEW_STR( rv, u8str );
174187
return rv;
175188
}
176189
/* }}} */
@@ -183,11 +196,9 @@ U_CFUNC zend_string *collator_convert_zstr_utf8_to_utf16(zend_string *utf8_str)
183196
zend_string *zstr = intl_convert_utf8_to_utf16_zstr(
184197
ZSTR_VAL(utf8_str), ZSTR_LEN(utf8_str),
185198
&status);
186-
// FIXME Or throw error or use intl internal error handler
187199
if (U_FAILURE(status)) {
188-
php_error(E_WARNING,
189-
"Error casting object to string in collator_convert_zstr_utf8_to_utf16()");
190-
zstr = ZSTR_EMPTY_ALLOC();
200+
collator_set_conversion_error(status, "Error converting string from UTF-8 to UTF-16");
201+
return nullptr;
191202
}
192203

193204
return zstr;
@@ -227,10 +238,10 @@ U_CFUNC zval* collator_convert_object_to_string( zval* obj, zval *rv )
227238
zend_string *converted_str = intl_convert_utf8_to_utf16_zstr(
228239
Z_STRVAL_P( zstr ), Z_STRLEN_P( zstr ),
229240
&status );
230-
// FIXME Or throw error or use intl internal error handler
231241
if( U_FAILURE( status ) ) {
232-
php_error( E_WARNING, "Error casting object to string in collator_convert_object_to_string()" );
233-
converted_str = ZSTR_EMPTY_ALLOC();
242+
collator_set_conversion_error(status, "Error converting object string from UTF-8 to UTF-16");
243+
zval_ptr_dtor( zstr );
244+
return nullptr;
234245
}
235246

236247
/* Cleanup zstr to hold utf16 string. */

ext/intl/collator/collator_sort.cpp

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,19 @@ static int collator_regular_compare_function(zval *result, zval *op1, zval *op2)
5959
zval norm1, norm2;
6060
zval *num1_p = nullptr, *num2_p = nullptr;
6161
zval *norm1_p = nullptr, *norm2_p = nullptr;
62-
zval *str1_p, *str2_p;
62+
zval *str1_p = nullptr, *str2_p = nullptr;
6363

6464
ZVAL_NULL(&str1);
6565
str1_p = collator_convert_object_to_string( op1, &str1 );
66+
if( str1_p == nullptr )
67+
return FAILURE;
68+
6669
ZVAL_NULL(&str2);
6770
str2_p = collator_convert_object_to_string( op2, &str2 );
71+
if( str2_p == nullptr ) {
72+
rc = FAILURE;
73+
goto cleanup;
74+
}
6875

6976
/* If both args are strings AND either of args is not numeric string
7077
* then use ICU-compare. Otherwise PHP-compare. */
@@ -90,9 +97,17 @@ static int collator_regular_compare_function(zval *result, zval *op1, zval *op2)
9097
* just convert it to utf8.
9198
*/
9299
norm1_p = collator_convert_zstr_utf16_to_utf8( str1_p, &norm1 );
100+
if( norm1_p == nullptr ) {
101+
rc = FAILURE;
102+
goto cleanup;
103+
}
93104

94105
/* num2 is not set but str2 is string => do normalization. */
95106
norm2_p = collator_normalize_sort_argument( str2_p, &norm2 );
107+
if( norm2_p == nullptr ) {
108+
rc = FAILURE;
109+
goto cleanup;
110+
}
96111
}
97112
else
98113
{
@@ -109,25 +124,40 @@ static int collator_regular_compare_function(zval *result, zval *op1, zval *op2)
109124
{
110125
/* num1 is not set if str1 or str2 is not a string => do normalization. */
111126
norm1_p = collator_normalize_sort_argument( str1_p, &norm1 );
127+
if( norm1_p == nullptr ) {
128+
rc = FAILURE;
129+
goto cleanup;
130+
}
112131

113132
/* if num1 is not set then num2 is not set as well => do normalization. */
114133
norm2_p = collator_normalize_sort_argument( str2_p, &norm2 );
134+
if( norm2_p == nullptr ) {
135+
rc = FAILURE;
136+
goto cleanup;
137+
}
115138
}
116139

117140
rc = compare_function( result, norm1_p, norm2_p );
141+
}
118142

143+
cleanup:
144+
if( norm1_p )
119145
zval_ptr_dtor( norm1_p );
146+
147+
if( norm2_p )
120148
zval_ptr_dtor( norm2_p );
121-
}
122149

123150
if( num1_p )
124151
zval_ptr_dtor( num1_p );
125152

126153
if( num2_p )
127154
zval_ptr_dtor( num2_p );
128155

129-
zval_ptr_dtor( str1_p );
130-
zval_ptr_dtor( str2_p );
156+
if( str1_p )
157+
zval_ptr_dtor( str1_p );
158+
159+
if( str2_p )
160+
zval_ptr_dtor( str2_p );
131161

132162
return rc;
133163
}
@@ -172,7 +202,14 @@ static int collator_icu_compare_function(zval *result, zval *op1, zval *op2)
172202
{
173203
int rc = SUCCESS;
174204
zend_string *str1 = collator_zval_to_string(op1);
205+
if( str1 == nullptr )
206+
return FAILURE;
207+
175208
zend_string *str2 = collator_zval_to_string(op2);
209+
if( str2 == nullptr ) {
210+
zend_string_release(str1);
211+
return FAILURE;
212+
}
176213

177214
/* Compare the strings using ICU. */
178215
ZEND_ASSERT(INTL_G(current_collator) != nullptr);
@@ -260,6 +297,7 @@ static collator_compare_func_t collator_get_compare_function( const zend_long so
260297
static void collator_sort_internal( int renumber, INTERNAL_FUNCTION_PARAMETERS )
261298
{
262299
UCollator* saved_collator;
300+
intl_error* saved_collator_error;
263301
zval* array = nullptr;
264302
HashTable* hash = nullptr;
265303
zend_array* sorted = nullptr;
@@ -299,13 +337,21 @@ static void collator_sort_internal( int renumber, INTERNAL_FUNCTION_PARAMETERS )
299337

300338
/* Save specified collator in the request-global (?) variable. */
301339
saved_collator = INTL_G( current_collator );
340+
saved_collator_error = INTL_G( current_collator_error );
302341
INTL_G( current_collator ) = co->ucoll;
342+
INTL_G( current_collator_error ) = COLLATOR_ERROR_P( co );
303343

304344
/* Sort specified array. */
305345
zend_hash_sort( sorted, collator_compare_func, renumber );
306346

307347
/* Restore saved collator. */
308348
INTL_G( current_collator ) = saved_collator;
349+
INTL_G( current_collator_error ) = saved_collator_error;
350+
351+
if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) ) {
352+
zend_array_destroy( sorted );
353+
}
354+
COLLATOR_CHECK_STATUS( co, "Error comparing array values" );
309355

310356
/* Convert strings in the specified array back to UTF-8. */
311357
collator_convert_hash_from_utf16_to_utf8( sorted, COLLATOR_ERROR_CODE_P( co ) );

ext/intl/php_intl.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ PHP_RINIT_FUNCTION( intl )
278278
PHP_RSHUTDOWN_FUNCTION( intl )
279279
{
280280
INTL_G(current_collator) = NULL;
281+
INTL_G(current_collator_error) = NULL;
281282
if (INTL_G(grapheme_iterator)) {
282283
grapheme_close_global_iterator( );
283284
INTL_G(grapheme_iterator) = NULL;

ext/intl/php_intl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ extern zend_module_entry intl_module_entry;
4545

4646
ZEND_BEGIN_MODULE_GLOBALS(intl)
4747
struct UCollator *current_collator;
48+
intl_error *current_collator_error;
4849
char* default_locale;
4950
collator_compare_func_t compare_func;
5051
UBreakIterator* grapheme_iterator;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Collator::sort() reports conversion errors from comparison callbacks
3+
--EXTENSIONS--
4+
intl
5+
--FILE--
6+
<?php
7+
class BadString {
8+
public function __toString(): string {
9+
return "\xFF";
10+
}
11+
}
12+
13+
$coll = new Collator('en_US');
14+
$array = ['b', new BadString(), 'a'];
15+
16+
var_dump($coll->sort($array, Collator::SORT_STRING));
17+
var_dump(intl_get_error_code() === U_INVALID_CHAR_FOUND);
18+
echo intl_get_error_message(), "\n";
19+
var_dump($coll->getErrorCode() === U_INVALID_CHAR_FOUND);
20+
echo $coll->getErrorMessage(), "\n";
21+
?>
22+
--EXPECT--
23+
bool(false)
24+
bool(true)
25+
Collator::sort(): Error comparing array values: U_INVALID_CHAR_FOUND
26+
bool(true)
27+
Collator::sort(): Error comparing array values: U_INVALID_CHAR_FOUND

0 commit comments

Comments
 (0)