summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2019-04-16 10:40:01 +0200
committerNikita Popov <nikita.ppv@gmail.com>2019-04-16 10:40:01 +0200
commit772a158d7a2fd67a7ccae1f87b0abf4ead73561b (patch)
treef49b77d9ea8ac3dce2619ea076efd83cb3c23ac1
parent9e62ba1533504a32ce5cfea01e2f11fb27a4abb1 (diff)
parent32fb2d93431aa5d5b111051db998501a8d7117ee (diff)
downloadphp-git-772a158d7a2fd67a7ccae1f87b0abf4ead73561b.tar.gz
Merge branch 'PHP-7.4'
-rw-r--r--Zend/tests/foreach_by_ref_repacking_insert.phpt18
-rw-r--r--Zend/zend_hash.c67
-rw-r--r--ext/spl/tests/bug77903.phpt52
-rw-r--r--php.ini-development4
-rw-r--r--php.ini-production4
5 files changed, 94 insertions, 51 deletions
diff --git a/Zend/tests/foreach_by_ref_repacking_insert.phpt b/Zend/tests/foreach_by_ref_repacking_insert.phpt
new file mode 100644
index 0000000000..1a2f9c7068
--- /dev/null
+++ b/Zend/tests/foreach_by_ref_repacking_insert.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Perform a packed to hash insert when the iterator is at the end of the array
+--FILE--
+
+<?php
+$a = [];
+$a[1] = 1;
+foreach ($a as $k => &$v) {
+ var_dump($v);
+ if ($k == 1) $a[4] = 4;
+ if ($k == 4) $a[2] = 2;
+}
+
+?>
+--EXPECT--
+int(1)
+int(4)
+int(2)
diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c
index c6ad84ae2d..bee425c05f 100644
--- a/Zend/zend_hash.c
+++ b/Zend/zend_hash.c
@@ -400,10 +400,8 @@ ZEND_API uint32_t zend_array_count(HashTable *ht)
}
/* }}} */
-static zend_always_inline HashPosition _zend_hash_get_first_pos(const HashTable *ht)
+static zend_always_inline HashPosition _zend_hash_get_valid_pos(const HashTable *ht, HashPosition pos)
{
- HashPosition pos = 0;
-
while (pos < ht->nNumUsed && Z_ISUNDEF(ht->arData[pos].val)) {
pos++;
}
@@ -412,12 +410,7 @@ static zend_always_inline HashPosition _zend_hash_get_first_pos(const HashTable
static zend_always_inline HashPosition _zend_hash_get_current_pos(const HashTable *ht)
{
- HashPosition pos = ht->nInternalPointer;
-
- if (pos == 0) {
- pos = _zend_hash_get_first_pos(ht);
- }
- return pos;
+ return _zend_hash_get_valid_pos(ht, ht->nInternalPointer);
}
ZEND_API HashPosition ZEND_FASTCALL zend_hash_get_current_pos(const HashTable *ht)
@@ -1168,6 +1161,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_rehash(HashTable *ht)
p++;
} while (++i < ht->nNumUsed);
} else {
+ uint32_t old_num_used = ht->nNumUsed;
do {
if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) {
uint32_t j = i;
@@ -1224,6 +1218,12 @@ ZEND_API int ZEND_FASTCALL zend_hash_rehash(HashTable *ht)
HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(i);
p++;
} while (++i < ht->nNumUsed);
+
+ /* Migrate pointer to one past the end of the array to the new one past the end, so that
+ * newly inserted elements are picked up correctly. */
+ if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
+ _zend_hash_iterators_update(ht, old_num_used, ht->nNumUsed);
+ }
}
return SUCCESS;
}
@@ -2241,7 +2241,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_reset_ex(HashTable *ht, H
{
IS_CONSISTENT(ht);
HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1);
- *pos = _zend_hash_get_first_pos(ht);
+ *pos = _zend_hash_get_valid_pos(ht, 0);
}
@@ -2269,19 +2269,13 @@ ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_end_ex(HashTable *ht, Has
ZEND_API int ZEND_FASTCALL zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos)
{
- uint32_t idx = *pos;
+ uint32_t idx;
IS_CONSISTENT(ht);
HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1);
+ idx = _zend_hash_get_valid_pos(ht, *pos);
if (idx < ht->nNumUsed) {
- if (idx == 0) {
- idx = _zend_hash_get_first_pos(ht);
- if (idx >= ht->nNumUsed) {
- *pos = idx;
- return SUCCESS;
- }
- }
while (1) {
idx++;
if (idx >= ht->nNumUsed) {
@@ -2324,17 +2318,12 @@ ZEND_API int ZEND_FASTCALL zend_hash_move_backwards_ex(HashTable *ht, HashPositi
/* This function should be made binary safe */
ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_ex(const HashTable *ht, zend_string **str_index, zend_ulong *num_index, HashPosition *pos)
{
- uint32_t idx = *pos;
+ uint32_t idx;
Bucket *p;
IS_CONSISTENT(ht);
+ idx = _zend_hash_get_valid_pos(ht, *pos);
if (idx < ht->nNumUsed) {
- if (idx == 0) {
- idx = _zend_hash_get_first_pos(ht);
- if (idx >= ht->nNumUsed) {
- return HASH_KEY_NON_EXISTENT;
- }
- }
p = ht->arData + idx;
if (p->key) {
*str_index = p->key;
@@ -2349,20 +2338,14 @@ ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_ex(const HashTable *ht, zen
ZEND_API void ZEND_FASTCALL zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos)
{
- uint32_t idx = *pos;
+ uint32_t idx;
Bucket *p;
IS_CONSISTENT(ht);
+ idx = _zend_hash_get_valid_pos(ht, *pos);
if (idx >= ht->nNumUsed) {
ZVAL_NULL(key);
} else {
- if (idx == 0) {
- idx = _zend_hash_get_first_pos(ht);
- if (idx >= ht->nNumUsed) {
- ZVAL_NULL(key);
- return;
- }
- }
p = ht->arData + idx;
if (p->key) {
ZVAL_STR_COPY(key, p->key);
@@ -2374,17 +2357,12 @@ ZEND_API void ZEND_FASTCALL zend_hash_get_current_key_zval_ex(const HashTable *h
ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_type_ex(HashTable *ht, HashPosition *pos)
{
- uint32_t idx = *pos;
+ uint32_t idx;
Bucket *p;
IS_CONSISTENT(ht);
+ idx = _zend_hash_get_valid_pos(ht, *pos);
if (idx < ht->nNumUsed) {
- if (idx == 0) {
- idx = _zend_hash_get_first_pos(ht);
- if (idx >= ht->nNumUsed) {
- return HASH_KEY_NON_EXISTENT;
- }
- }
p = ht->arData + idx;
if (p->key) {
return HASH_KEY_IS_STRING;
@@ -2398,17 +2376,12 @@ ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_type_ex(HashTable *ht, Hash
ZEND_API zval* ZEND_FASTCALL zend_hash_get_current_data_ex(HashTable *ht, HashPosition *pos)
{
- uint32_t idx = *pos;
+ uint32_t idx;
Bucket *p;
IS_CONSISTENT(ht);
+ idx = _zend_hash_get_valid_pos(ht, *pos);
if (idx < ht->nNumUsed) {
- if (idx == 0) {
- idx = _zend_hash_get_first_pos(ht);
- if (idx >= ht->nNumUsed) {
- return NULL;
- }
- }
p = ht->arData + idx;
return &p->val;
} else {
diff --git a/ext/spl/tests/bug77903.phpt b/ext/spl/tests/bug77903.phpt
new file mode 100644
index 0000000000..842de9cca2
--- /dev/null
+++ b/ext/spl/tests/bug77903.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Bug #77903: ArrayIterator stops iterating after offsetSet call
+--FILE--
+<?php
+$a = new ArrayIterator();
+$a->rewind();
+var_dump($a->valid()); // false
+var_dump($a->current()); // null
+$a->offsetSet(1,1);
+var_dump($a->valid()); // true
+var_dump($a->current()); // 1
+$a->next();
+var_dump($a->valid()); // false
+var_dump($a->current()); // null
+$a->offsetSet(4,4);
+var_dump($a->valid()); // true
+var_dump($a->current()); // 4
+$a->next();
+var_dump($a->valid()); // false
+var_dump($a->current()); // null
+$a->next();
+var_dump($a->valid()); // false
+var_dump($a->current()); // null
+$a->offsetSet(2,2);
+var_dump($a->valid()); // true
+var_dump($a->current()); // 2
+$a->next();
+var_dump($a->valid()); // false
+var_dump($a->current()); // null
+$a->next();
+var_dump($a->valid()); // false
+var_dump($a->current()); // null
+?>
+--EXPECT--
+bool(false)
+NULL
+bool(true)
+int(1)
+bool(false)
+NULL
+bool(true)
+int(4)
+bool(false)
+NULL
+bool(false)
+NULL
+bool(true)
+int(2)
+bool(false)
+NULL
+bool(false)
+NULL
diff --git a/php.ini-development b/php.ini-development
index 374d26cc44..4329bfae1f 100644
--- a/php.ini-development
+++ b/php.ini-development
@@ -1176,11 +1176,11 @@ mysqli.default_port = 3306
; http://php.net/mysqli.default-socket
mysqli.default_socket =
-; Default host for mysql_connect() (doesn't apply in safe mode).
+; Default host for mysqli_connect() (doesn't apply in safe mode).
; http://php.net/mysqli.default-host
mysqli.default_host =
-; Default user for mysql_connect() (doesn't apply in safe mode).
+; Default user for mysqli_connect() (doesn't apply in safe mode).
; http://php.net/mysqli.default-user
mysqli.default_user =
diff --git a/php.ini-production b/php.ini-production
index fecff7c473..c8bf73950c 100644
--- a/php.ini-production
+++ b/php.ini-production
@@ -1176,11 +1176,11 @@ mysqli.default_port = 3306
; http://php.net/mysqli.default-socket
mysqli.default_socket =
-; Default host for mysql_connect() (doesn't apply in safe mode).
+; Default host for mysql_iconnect() (doesn't apply in safe mode).
; http://php.net/mysqli.default-host
mysqli.default_host =
-; Default user for mysql_connect() (doesn't apply in safe mode).
+; Default user for mysql_iconnect() (doesn't apply in safe mode).
; http://php.net/mysqli.default-user
mysqli.default_user =