diff --git a/ext/intl/resourcebundle/resourcebundle_iterator.c b/ext/intl/resourcebundle/resourcebundle_iterator.c index 9ec97f64f51d4..7d8d21a61fbab 100644 --- a/ext/intl/resourcebundle/resourcebundle_iterator.c +++ b/ext/intl/resourcebundle/resourcebundle_iterator.c @@ -29,10 +29,17 @@ /* {{{ resourcebundle_iterator_read */ static void resourcebundle_iterator_read( ResourceBundle_iterator *iterator ) { - UErrorCode icuerror = U_ZERO_ERROR; + UErrorCode icuerror; ResourceBundle_object *rb = iterator->subject; - rb->child = ures_getByIndex( rb->me, iterator->i, rb->child, &icuerror ); + ZEND_ASSERT(Z_ISUNDEF(iterator->current)); + + /* Skip non-existing resource entries in the bundle, similar to how holes in arrays are skipped + * in foreach loops. See also GH-17317. */ + do { + icuerror = U_ZERO_ERROR; + rb->child = ures_getByIndex( rb->me, iterator->i, rb->child, &icuerror ); + } while (icuerror == U_MISSING_RESOURCE_ERROR && iterator->i++ < iterator->length); if (U_SUCCESS(icuerror)) { /* ATTN: key extraction must be the first thing to do... rb->child might be reset in read! */ @@ -43,7 +50,6 @@ static void resourcebundle_iterator_read( ResourceBundle_iterator *iterator ) } else { // zend_throw_exception( spl_ce_OutOfRangeException, "Running past end of ResourceBundle", 0); - ZVAL_UNDEF(&iterator->current); } } /* }}} */ @@ -89,7 +95,7 @@ static zval *resourcebundle_iterator_current( zend_object_iterator *iter ) { ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter; if (Z_ISUNDEF(iterator->current)) { - resourcebundle_iterator_read( iterator); + return NULL; } return &iterator->current; } @@ -100,9 +106,7 @@ static void resourcebundle_iterator_key( zend_object_iterator *iter, zval *key ) { ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter; - if (Z_ISUNDEF(iterator->current)) { - resourcebundle_iterator_read( iterator); - } + ZEND_ASSERT(!Z_ISUNDEF(iterator->current)); if (iterator->is_table) { ZVAL_STRING(key, iterator->currentkey); @@ -119,6 +123,7 @@ static void resourcebundle_iterator_step( zend_object_iterator *iter ) iterator->i++; resourcebundle_iterator_invalidate( iter ); + resourcebundle_iterator_read(iterator); } /* }}} */ @@ -129,6 +134,7 @@ static void resourcebundle_iterator_reset( zend_object_iterator *iter ) iterator->i = 0; resourcebundle_iterator_invalidate( iter ); + resourcebundle_iterator_read(iterator); } /* }}} */ diff --git a/ext/intl/tests/gh17319.phpt b/ext/intl/tests/gh17319.phpt new file mode 100644 index 0000000000000..7ed0c1eed3702 --- /dev/null +++ b/ext/intl/tests/gh17319.phpt @@ -0,0 +1,63 @@ +--TEST-- +GH-17319 (ResourceBundle iterator crash on NULL key) +--EXTENSIONS-- +intl +--CREDITS-- +KidFlo +--FILE-- +get('calendar')->get('buddhist') as $key => $value) { + echo "KEY: "; var_dump($key); + echo "VALUE: "; var_dump($value); +} + +$bundle = (new ResourceBundle('', NULL))->get('calendar')->get('buddhist'); +$iterator = $bundle->getIterator(); +while ($iterator->valid()) { + var_dump($iterator->key()); + $iterator->next(); +} + +?> +--EXPECT-- +KEY: string(15) "AmPmMarkersAbbr" +VALUE: object(ResourceBundle)#3 (0) { +} +KEY: string(15) "AmPmMarkersAbbr" +VALUE: object(ResourceBundle)#4 (0) { +} +KEY: string(16) "DateTimePatterns" +VALUE: object(ResourceBundle)#3 (0) { +} +KEY: string(11) "appendItems" +VALUE: object(ResourceBundle)#4 (0) { +} +KEY: string(16) "availableFormats" +VALUE: object(ResourceBundle)#3 (0) { +} +KEY: string(8) "dayNames" +VALUE: object(ResourceBundle)#4 (0) { +} +KEY: string(4) "eras" +VALUE: object(ResourceBundle)#3 (0) { +} +KEY: string(15) "intervalFormats" +VALUE: object(ResourceBundle)#4 (0) { +} +KEY: string(10) "monthNames" +VALUE: object(ResourceBundle)#3 (0) { +} +KEY: string(8) "quarters" +VALUE: object(ResourceBundle)#4 (0) { +} +string(15) "AmPmMarkersAbbr" +string(15) "AmPmMarkersAbbr" +string(16) "DateTimePatterns" +string(11) "appendItems" +string(16) "availableFormats" +string(8) "dayNames" +string(4) "eras" +string(15) "intervalFormats" +string(10) "monthNames" +string(8) "quarters"