diff --git a/ext/standard/array.c b/ext/standard/array.c index d4e3742bb71f..a1a929340848 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1029,11 +1029,50 @@ static inline HashTable *get_ht_for_iap(zval *zv, bool separate) { return zobj->handlers->get_properties(zobj); } +static zval *php_array_iter_seek_current(HashTable *array, bool forward_direction) +{ + zval *entry; + + while (true) { + if ((entry = zend_hash_get_current_data(array)) == NULL) { + return NULL; + } + + ZVAL_DEINDIRECT(entry); + + /* Possible with an uninitialized typed property */ + if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) { + zend_result result; + if (forward_direction) { + result = zend_hash_move_forward(array); + } else { + result = zend_hash_move_backwards(array); + } + if (result != SUCCESS) { + return NULL; + } + } else { + break; + } + } + + return entry; +} + +static void php_array_iter_return_current(zval *return_value, HashTable *array, bool forward_direction) +{ + zval *entry = php_array_iter_seek_current(array, forward_direction); + if (EXPECTED(entry)) { + RETURN_COPY_DEREF(entry); + } else { + RETURN_FALSE; + } +} + /* {{{ Advances array argument's internal pointer to the last element and return it */ PHP_FUNCTION(end) { zval *array_zv; - zval *entry; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) @@ -1047,15 +1086,7 @@ PHP_FUNCTION(end) zend_hash_internal_pointer_end(array); if (USED_RET()) { - if ((entry = zend_hash_get_current_data(array)) == NULL) { - RETURN_FALSE; - } - - if (Z_TYPE_P(entry) == IS_INDIRECT) { - entry = Z_INDIRECT_P(entry); - } - - RETURN_COPY_DEREF(entry); + php_array_iter_return_current(return_value, array, false); } } /* }}} */ @@ -1064,7 +1095,6 @@ PHP_FUNCTION(end) PHP_FUNCTION(prev) { zval *array_zv; - zval *entry; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) @@ -1078,15 +1108,7 @@ PHP_FUNCTION(prev) zend_hash_move_backwards(array); if (USED_RET()) { - if ((entry = zend_hash_get_current_data(array)) == NULL) { - RETURN_FALSE; - } - - if (Z_TYPE_P(entry) == IS_INDIRECT) { - entry = Z_INDIRECT_P(entry); - } - - RETURN_COPY_DEREF(entry); + php_array_iter_return_current(return_value, array, false); } } /* }}} */ @@ -1095,7 +1117,6 @@ PHP_FUNCTION(prev) PHP_FUNCTION(next) { zval *array_zv; - zval *entry; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) @@ -1109,15 +1130,7 @@ PHP_FUNCTION(next) zend_hash_move_forward(array); if (USED_RET()) { - if ((entry = zend_hash_get_current_data(array)) == NULL) { - RETURN_FALSE; - } - - if (Z_TYPE_P(entry) == IS_INDIRECT) { - entry = Z_INDIRECT_P(entry); - } - - RETURN_COPY_DEREF(entry); + php_array_iter_return_current(return_value, array, true); } } /* }}} */ @@ -1126,7 +1139,6 @@ PHP_FUNCTION(next) PHP_FUNCTION(reset) { zval *array_zv; - zval *entry; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) @@ -1140,15 +1152,7 @@ PHP_FUNCTION(reset) zend_hash_internal_pointer_reset(array); if (USED_RET()) { - if ((entry = zend_hash_get_current_data(array)) == NULL) { - RETURN_FALSE; - } - - if (Z_TYPE_P(entry) == IS_INDIRECT) { - entry = Z_INDIRECT_P(entry); - } - - RETURN_COPY_DEREF(entry); + php_array_iter_return_current(return_value, array, true); } } /* }}} */ @@ -1157,22 +1161,13 @@ PHP_FUNCTION(reset) PHP_FUNCTION(current) { zval *array_zv; - zval *entry; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_OR_OBJECT(array_zv) ZEND_PARSE_PARAMETERS_END(); HashTable *array = get_ht_for_iap(array_zv, /* separate */ false); - if ((entry = zend_hash_get_current_data(array)) == NULL) { - RETURN_FALSE; - } - - if (Z_TYPE_P(entry) == IS_INDIRECT) { - entry = Z_INDIRECT_P(entry); - } - - RETURN_COPY_DEREF(entry); + php_array_iter_return_current(return_value, array, true); } /* }}} */ @@ -1186,7 +1181,10 @@ PHP_FUNCTION(key) ZEND_PARSE_PARAMETERS_END(); HashTable *array = get_ht_for_iap(array_zv, /* separate */ false); - zend_hash_get_current_key_zval(array, return_value); + zval *entry = php_array_iter_seek_current(array, true); + if (EXPECTED(entry)) { + zend_hash_get_current_key_zval(array, return_value); + } } /* }}} */ diff --git a/ext/standard/tests/array/gh16905.phpt b/ext/standard/tests/array/gh16905.phpt new file mode 100644 index 000000000000..89d11575789e --- /dev/null +++ b/ext/standard/tests/array/gh16905.phpt @@ -0,0 +1,92 @@ +--TEST-- +GH-16905 (Internal iterator functions can't handle UNDEF properties) +--FILE-- +b = 1; +$x->c = 2; + +var_dump(reset($x)); +var_dump(current($x)); +var_dump(end($x)); + +var_dump(reset($x)); +var_dump(next($x)); + +var_dump(end($x)); +var_dump(prev($x)); + +var_dump(key($x)); +var_dump(current($x)); + +$x = new TestAllUndef; +var_dump(key($x)); +var_dump(current($x)); + +$x->a = 1; +var_dump(key($x)); +var_dump(current($x)); +reset($x); +var_dump(key($x)); +var_dump(current($x)); + +?> +--EXPECTF-- +Deprecated: reset(): Calling reset() on an object is deprecated in %s on line %d +int(1) + +Deprecated: current(): Calling current() on an object is deprecated in %s on line %d +int(1) + +Deprecated: end(): Calling end() on an object is deprecated in %s on line %d +int(2) + +Deprecated: reset(): Calling reset() on an object is deprecated in %s on line %d +int(1) + +Deprecated: next(): Calling next() on an object is deprecated in %s on line %d +int(2) + +Deprecated: end(): Calling end() on an object is deprecated in %s on line %d +int(2) + +Deprecated: prev(): Calling prev() on an object is deprecated in %s on line %d +int(1) + +Deprecated: key(): Calling key() on an object is deprecated in %s on line %d +string(1) "b" + +Deprecated: current(): Calling current() on an object is deprecated in %s on line %d +int(1) + +Deprecated: key(): Calling key() on an object is deprecated in %s on line %d +NULL + +Deprecated: current(): Calling current() on an object is deprecated in %s on line %d +bool(false) + +Deprecated: key(): Calling key() on an object is deprecated in %s on line %d +NULL + +Deprecated: current(): Calling current() on an object is deprecated in %s on line %d +bool(false) + +Deprecated: reset(): Calling reset() on an object is deprecated in %s on line %d + +Deprecated: key(): Calling key() on an object is deprecated in %s on line %d +string(1) "a" + +Deprecated: current(): Calling current() on an object is deprecated in %s on line %d +int(1)