summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2015-01-30 12:24:31 +0300
committerDmitry Stogov <dmitry@zend.com>2015-01-30 12:24:31 +0300
commitcc4b7be41e2e2b9b0d7a3c8e98466b8886692e6e (patch)
treea3e4474c19c64b4754857be7526438b88a0bfba4
parent5aa9712b0a30303aadfe3bdd8ae1f072ca3e6ba1 (diff)
downloadphp-git-cc4b7be41e2e2b9b0d7a3c8e98466b8886692e6e.tar.gz
Make internal function, operation on array passed by reference, to preserve foreach hash position
-rw-r--r--Zend/tests/foreach_011.phpt19
-rw-r--r--Zend/tests/foreach_012.phpt18
-rw-r--r--Zend/tests/foreach_013.phpt17
-rw-r--r--Zend/tests/foreach_014.phpt15
-rw-r--r--Zend/tests/foreach_015.phpt18
-rw-r--r--Zend/tests/foreach_016.phpt18
-rw-r--r--ext/standard/array.c137
-rw-r--r--tests/lang/foreachLoop.013.phpt103
-rw-r--r--tests/lang/foreachLoop.015.phpt103
9 files changed, 253 insertions, 195 deletions
diff --git a/Zend/tests/foreach_011.phpt b/Zend/tests/foreach_011.phpt
new file mode 100644
index 0000000000..e91426fb27
--- /dev/null
+++ b/Zend/tests/foreach_011.phpt
@@ -0,0 +1,19 @@
+--TEST--
+sort() functions precerve foreach by reference iterator pointer
+--FILE--
+<?php
+$a = [1,2,3,4,5,0];
+foreach($a as &$v) {
+ echo "$v\n";
+ if ($v == 3) {
+ rsort($a);
+ }
+}
+?>
+--EXPECT--
+1
+2
+3
+2
+1
+0
diff --git a/Zend/tests/foreach_012.phpt b/Zend/tests/foreach_012.phpt
new file mode 100644
index 0000000000..5e5538cd4d
--- /dev/null
+++ b/Zend/tests/foreach_012.phpt
@@ -0,0 +1,18 @@
+--TEST--
+array_walk() function precerve foreach by reference iterator pointer
+--FILE--
+<?php
+$a = [1,2,3,4,5];
+foreach($a as &$v) {
+ echo "$v\n";
+ if ($v == 3) {
+ array_walk($a, function (&$x) {$x+=10;});
+ }
+}
+?>
+--EXPECT--
+1
+2
+3
+14
+15 \ No newline at end of file
diff --git a/Zend/tests/foreach_013.phpt b/Zend/tests/foreach_013.phpt
new file mode 100644
index 0000000000..cfbb3d7f79
--- /dev/null
+++ b/Zend/tests/foreach_013.phpt
@@ -0,0 +1,17 @@
+--TEST--
+array_push() function precerve foreach by reference iterator pointer
+--FILE--
+<?php
+$a = [1,2,3];
+foreach($a as &$v) {
+ echo "$v\n";
+ if ($v == 3) {
+ array_push($a, 4);
+ }
+}
+?>
+--EXPECT--
+1
+2
+3
+4
diff --git a/Zend/tests/foreach_014.phpt b/Zend/tests/foreach_014.phpt
new file mode 100644
index 0000000000..8d0ac582a9
--- /dev/null
+++ b/Zend/tests/foreach_014.phpt
@@ -0,0 +1,15 @@
+--TEST--
+array_pop() function precerve foreach by reference iterator pointer
+--FILE--
+<?php
+$a = [1,2,3];
+foreach($a as &$v) {
+ echo "$v\n";
+ if ($v == 2) {
+ array_pop($a);
+ }
+}
+?>
+--EXPECT--
+1
+2
diff --git a/Zend/tests/foreach_015.phpt b/Zend/tests/foreach_015.phpt
new file mode 100644
index 0000000000..adc8085f34
--- /dev/null
+++ b/Zend/tests/foreach_015.phpt
@@ -0,0 +1,18 @@
+--TEST--
+array_shift() function precerve foreach by reference iterator pointer
+--FILE--
+<?php
+$a = [1,2,3,4];
+foreach($a as &$v) {
+ echo "$v\n";
+ array_shift($a);
+}
+var_dump($a);
+?>
+--EXPECT--
+1
+2
+3
+4
+array(0) {
+} \ No newline at end of file
diff --git a/Zend/tests/foreach_016.phpt b/Zend/tests/foreach_016.phpt
new file mode 100644
index 0000000000..423c8dd0a6
--- /dev/null
+++ b/Zend/tests/foreach_016.phpt
@@ -0,0 +1,18 @@
+--TEST--
+array_unshift() function precerve foreach by reference iterator pointer
+--FILE--
+<?php
+$a = [1,2,3];
+foreach($a as &$v) {
+ echo "$v\n";
+ if ($v == 2) {
+ array_unshift($a, 0, 0, 0, 0, 0, 0, 0, 0);
+ }
+}
+var_dump(count($a));
+?>
+--EXPECT--
+1
+2
+3
+int(11)
diff --git a/ext/standard/array.c b/ext/standard/array.c
index 49c6cd0789..526c9210c1 100644
--- a/ext/standard/array.c
+++ b/ext/standard/array.c
@@ -1877,26 +1877,49 @@ static void php_array_data_shuffle(zval *array) /* {{{ */
hash = Z_ARRVAL_P(array);
n_left = n_elems;
- if (hash->nNumUsed != hash->nNumOfElements) {
- for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
- p = hash->arData + idx;
- if (Z_TYPE(p->val) == IS_UNDEF) continue;
- if (j != idx) {
- hash->arData[j] = *p;
+ if (EXPECTED(hash->u.v.nIteratorsCount == 0)) {
+ if (hash->nNumUsed != hash->nNumOfElements) {
+ for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
+ p = hash->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
+ if (j != idx) {
+ hash->arData[j] = *p;
+ }
+ j++;
}
- j++;
}
- }
- while (--n_left) {
- rnd_idx = php_rand();
- RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
- if (rnd_idx != n_left) {
- temp = hash->arData[n_left];
- hash->arData[n_left] = hash->arData[rnd_idx];
- hash->arData[rnd_idx] = temp;
+ while (--n_left) {
+ rnd_idx = php_rand();
+ RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
+ if (rnd_idx != n_left) {
+ temp = hash->arData[n_left];
+ hash->arData[n_left] = hash->arData[rnd_idx];
+ hash->arData[rnd_idx] = temp;
+ }
+ }
+ } else {
+ if (hash->nNumUsed != hash->nNumOfElements) {
+ for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
+ p = hash->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
+ if (j != idx) {
+ hash->arData[j] = *p;
+ zend_hash_iterators_update(hash, idx, j);
+ }
+ j++;
+ }
+ }
+ while (--n_left) {
+ rnd_idx = php_rand();
+ RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX);
+ if (rnd_idx != n_left) {
+ temp = hash->arData[n_left];
+ hash->arData[n_left] = hash->arData[rnd_idx];
+ hash->arData[rnd_idx] = temp;
+ zend_hash_iterators_update(hash, rnd_idx, n_left);
+ }
}
}
-
HANDLE_BLOCK_INTERRUPTIONS();
hash->nNumUsed = n_elems;
hash->nInternalPointer = 0;
@@ -2194,18 +2217,33 @@ PHP_FUNCTION(array_shift)
if (Z_ARRVAL_P(stack)->u.flags & HASH_FLAG_PACKED) {
uint32_t k = 0;
- for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
- p = Z_ARRVAL_P(stack)->arData + idx;
- if (Z_TYPE(p->val) == IS_UNDEF) continue;
- if (idx != k) {
- Bucket *q = Z_ARRVAL_P(stack)->arData + k;
- q->h = k;
- q->key = NULL;
- ZVAL_COPY_VALUE(&q->val, &p->val);
- ZVAL_UNDEF(&p->val);
- zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k);
+ if (EXPECTED(Z_ARRVAL_P(stack)->u.v.nIteratorsCount == 0)) {
+ for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
+ p = Z_ARRVAL_P(stack)->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
+ if (idx != k) {
+ Bucket *q = Z_ARRVAL_P(stack)->arData + k;
+ q->h = k;
+ q->key = NULL;
+ ZVAL_COPY_VALUE(&q->val, &p->val);
+ ZVAL_UNDEF(&p->val);
+ }
+ k++;
+ }
+ } else {
+ for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
+ p = Z_ARRVAL_P(stack)->arData + idx;
+ if (Z_TYPE(p->val) == IS_UNDEF) continue;
+ if (idx != k) {
+ Bucket *q = Z_ARRVAL_P(stack)->arData + k;
+ q->h = k;
+ q->key = NULL;
+ ZVAL_COPY_VALUE(&q->val, &p->val);
+ ZVAL_UNDEF(&p->val);
+ zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k);
+ }
+ k++;
}
- k++;
}
Z_ARRVAL_P(stack)->nNumUsed = k;
Z_ARRVAL_P(stack)->nNextFreeElement = k;
@@ -2258,21 +2296,46 @@ PHP_FUNCTION(array_unshift)
}
zend_hash_next_index_insert_new(&new_hash, &args[i]);
}
- ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
- if (key) {
- zend_hash_add_new(&new_hash, key, value);
- } else {
- zend_hash_next_index_insert_new(&new_hash, value);
- }
- } ZEND_HASH_FOREACH_END();
+ if (EXPECTED(Z_ARRVAL_P(stack)->u.v.nIteratorsCount == 0)) {
+ ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
+ if (key) {
+ zend_hash_add_new(&new_hash, key, value);
+ } else {
+ zend_hash_next_index_insert_new(&new_hash, value);
+ }
+ } ZEND_HASH_FOREACH_END();
+ } else {
+ uint32_t old_idx;
+ uint32_t new_idx = i;
- new_hash.u.v.nIteratorsCount = Z_ARRVAL_P(stack)->u.v.nIteratorsCount;
+ ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
+ if (key) {
+ zend_hash_add_new(&new_hash, key, value);
+ } else {
+ zend_hash_next_index_insert_new(&new_hash, value);
+ }
+ old_idx = (Bucket*)value - Z_ARRVAL_P(stack)->arData;
+ zend_hash_iterators_update(Z_ARRVAL_P(stack), old_idx, new_idx);
+ new_idx++;
+ } ZEND_HASH_FOREACH_END();
+ }
+
+ /* replace HashTable data */
Z_ARRVAL_P(stack)->u.v.nIteratorsCount = 0;
Z_ARRVAL_P(stack)->pDestructor = NULL;
zend_hash_destroy(Z_ARRVAL_P(stack));
- *Z_ARRVAL_P(stack) = new_hash;
+
+ Z_ARRVAL_P(stack)->u.v.flags = new_hash.u.v.flags;
+ Z_ARRVAL_P(stack)->nTableSize = new_hash.nTableSize;
+ Z_ARRVAL_P(stack)->nTableMask = new_hash.nTableMask;
+ Z_ARRVAL_P(stack)->nNumUsed = new_hash.nNumUsed;
+ Z_ARRVAL_P(stack)->nNumOfElements = new_hash.nNumOfElements;
+ Z_ARRVAL_P(stack)->nNextFreeElement = new_hash.nNextFreeElement;
+ Z_ARRVAL_P(stack)->arData = new_hash.arData;
+ Z_ARRVAL_P(stack)->arHash = new_hash.arHash;
+ Z_ARRVAL_P(stack)->pDestructor = new_hash.pDestructor;
+
zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
- zend_hash_iterators_reset(Z_ARRVAL_P(stack));
/* Clean up and return the number of elements in the stack */
RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
diff --git a/tests/lang/foreachLoop.013.phpt b/tests/lang/foreachLoop.013.phpt
index 10f43f3998..ea728156f0 100644
--- a/tests/lang/foreachLoop.013.phpt
+++ b/tests/lang/foreachLoop.013.phpt
@@ -445,28 +445,12 @@ array(1) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
- iteration 1: $k=0; $v=new.0
- iteration 2: $k=0; $v=new.1
- iteration 3: $k=0; $v=new.2
- iteration 4: $k=0; $v=new.3
- iteration 5: $k=0; $v=new.4
- ** Stuck in a loop! **
--> State of array after loop:
-array(7) {
+array(2) {
[0]=>
- string(5) "new.5"
- [1]=>
- &string(5) "new.4"
- [2]=>
- string(5) "new.3"
- [3]=>
- string(5) "new.2"
- [4]=>
- string(5) "new.1"
- [5]=>
string(5) "new.0"
- [6]=>
- string(3) "v.0"
+ [1]=>
+ &string(3) "v.0"
}
---( Array with 2 element(s): )---
@@ -479,30 +463,17 @@ array(2) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
- iteration 1: $k=0; $v=new.0
- iteration 2: $k=0; $v=new.1
- iteration 3: $k=0; $v=new.2
- iteration 4: $k=0; $v=new.3
- iteration 5: $k=0; $v=new.4
- ** Stuck in a loop! **
+ iteration 1: $k=2; $v=v.1
--> State of array after loop:
-array(8) {
+array(4) {
[0]=>
- string(5) "new.5"
- [1]=>
- &string(5) "new.4"
- [2]=>
- string(5) "new.3"
- [3]=>
- string(5) "new.2"
- [4]=>
string(5) "new.1"
- [5]=>
+ [1]=>
string(5) "new.0"
- [6]=>
+ [2]=>
string(3) "v.0"
- [7]=>
- string(3) "v.1"
+ [3]=>
+ &string(3) "v.1"
}
---( Array with 3 element(s): )---
@@ -517,32 +488,19 @@ array(3) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
- iteration 1: $k=0; $v=new.0
- iteration 2: $k=0; $v=new.1
- iteration 3: $k=0; $v=new.2
- iteration 4: $k=0; $v=new.3
- iteration 5: $k=0; $v=new.4
- ** Stuck in a loop! **
+ iteration 1: $k=3; $v=v.2
--> State of array after loop:
-array(9) {
+array(5) {
[0]=>
- string(5) "new.5"
- [1]=>
- &string(5) "new.4"
- [2]=>
- string(5) "new.3"
- [3]=>
- string(5) "new.2"
- [4]=>
string(5) "new.1"
- [5]=>
+ [1]=>
string(5) "new.0"
- [6]=>
+ [2]=>
string(3) "v.0"
- [7]=>
+ [3]=>
string(3) "v.1"
- [8]=>
- string(3) "v.2"
+ [4]=>
+ &string(3) "v.2"
}
---( Array with 4 element(s): )---
@@ -559,32 +517,19 @@ array(4) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
- iteration 1: $k=0; $v=new.0
- iteration 2: $k=0; $v=new.1
- iteration 3: $k=0; $v=new.2
- iteration 4: $k=0; $v=new.3
- iteration 5: $k=0; $v=new.4
- ** Stuck in a loop! **
+ iteration 1: $k=4; $v=v.3
--> State of array after loop:
-array(10) {
+array(6) {
[0]=>
- string(5) "new.5"
- [1]=>
- &string(5) "new.4"
- [2]=>
- string(5) "new.3"
- [3]=>
- string(5) "new.2"
- [4]=>
string(5) "new.1"
- [5]=>
+ [1]=>
string(5) "new.0"
- [6]=>
+ [2]=>
string(3) "v.0"
- [7]=>
+ [3]=>
string(3) "v.1"
- [8]=>
+ [4]=>
string(3) "v.2"
- [9]=>
- string(3) "v.3"
+ [5]=>
+ &string(3) "v.3"
}
diff --git a/tests/lang/foreachLoop.015.phpt b/tests/lang/foreachLoop.015.phpt
index a56249ee0b..a4ee09d6a9 100644
--- a/tests/lang/foreachLoop.015.phpt
+++ b/tests/lang/foreachLoop.015.phpt
@@ -447,28 +447,12 @@ array(1) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
- iteration 1: $k=0; $v=new.0
- iteration 2: $k=0; $v=new.1
- iteration 3: $k=0; $v=new.2
- iteration 4: $k=0; $v=new.3
- iteration 5: $k=0; $v=new.4
- ** Stuck in a loop! **
--> State of array after loop:
-array(7) {
+array(2) {
[0]=>
- string(5) "new.5"
- [1]=>
- &string(5) "new.4"
- [2]=>
- string(5) "new.3"
- [3]=>
- string(5) "new.2"
- [4]=>
- string(5) "new.1"
- [5]=>
string(5) "new.0"
- [6]=>
- string(3) "v.0"
+ [1]=>
+ &string(3) "v.0"
}
---( Array with 2 element(s): )---
@@ -481,30 +465,17 @@ array(2) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
- iteration 1: $k=0; $v=new.0
- iteration 2: $k=0; $v=new.1
- iteration 3: $k=0; $v=new.2
- iteration 4: $k=0; $v=new.3
- iteration 5: $k=0; $v=new.4
- ** Stuck in a loop! **
+ iteration 1: $k=2; $v=v.1
--> State of array after loop:
-array(8) {
+array(4) {
[0]=>
- string(5) "new.5"
- [1]=>
- &string(5) "new.4"
- [2]=>
- string(5) "new.3"
- [3]=>
- string(5) "new.2"
- [4]=>
string(5) "new.1"
- [5]=>
+ [1]=>
string(5) "new.0"
- [6]=>
+ [2]=>
string(3) "v.0"
- [7]=>
- string(3) "v.1"
+ [3]=>
+ &string(3) "v.1"
}
---( Array with 3 element(s): )---
@@ -519,32 +490,19 @@ array(3) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
- iteration 1: $k=0; $v=new.0
- iteration 2: $k=0; $v=new.1
- iteration 3: $k=0; $v=new.2
- iteration 4: $k=0; $v=new.3
- iteration 5: $k=0; $v=new.4
- ** Stuck in a loop! **
+ iteration 1: $k=3; $v=v.2
--> State of array after loop:
-array(9) {
+array(5) {
[0]=>
- string(5) "new.5"
- [1]=>
- &string(5) "new.4"
- [2]=>
- string(5) "new.3"
- [3]=>
- string(5) "new.2"
- [4]=>
string(5) "new.1"
- [5]=>
+ [1]=>
string(5) "new.0"
- [6]=>
+ [2]=>
string(3) "v.0"
- [7]=>
+ [3]=>
string(3) "v.1"
- [8]=>
- string(3) "v.2"
+ [4]=>
+ &string(3) "v.2"
}
---( Array with 4 element(s): )---
@@ -561,32 +519,19 @@ array(4) {
}
--> Do loop:
iteration 0: $k=0; $v=v.0
- iteration 1: $k=0; $v=new.0
- iteration 2: $k=0; $v=new.1
- iteration 3: $k=0; $v=new.2
- iteration 4: $k=0; $v=new.3
- iteration 5: $k=0; $v=new.4
- ** Stuck in a loop! **
+ iteration 1: $k=4; $v=v.3
--> State of array after loop:
-array(10) {
+array(6) {
[0]=>
- string(5) "new.5"
- [1]=>
- &string(5) "new.4"
- [2]=>
- string(5) "new.3"
- [3]=>
- string(5) "new.2"
- [4]=>
string(5) "new.1"
- [5]=>
+ [1]=>
string(5) "new.0"
- [6]=>
+ [2]=>
string(3) "v.0"
- [7]=>
+ [3]=>
string(3) "v.1"
- [8]=>
+ [4]=>
string(3) "v.2"
- [9]=>
- string(3) "v.3"
+ [5]=>
+ &string(3) "v.3"
}