diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/opcache/Optimizer/block_pass.c | 20 | ||||
-rw-r--r-- | ext/opcache/Optimizer/nop_removal.c | 6 | ||||
-rw-r--r-- | ext/opcache/Optimizer/optimize_temp_vars_5.c | 34 | ||||
-rw-r--r-- | ext/opcache/Optimizer/pass1_5.c | 6 | ||||
-rw-r--r-- | ext/opcache/Optimizer/pass2.c | 4 | ||||
-rw-r--r-- | ext/opcache/Optimizer/pass3.c | 6 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_optimizer.c | 12 | ||||
-rw-r--r-- | ext/opcache/zend_persist.c | 6 | ||||
-rw-r--r-- | ext/standard/array.c | 191 |
9 files changed, 198 insertions, 87 deletions
diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index 7948990f81..4db48944e1 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -166,14 +166,16 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: - case ZEND_FE_RESET: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: case ZEND_NEW: case ZEND_JMP_SET: case ZEND_COALESCE: START_BLOCK_OP(ZEND_OP2(opline).opline_num); START_BLOCK_OP(opno + 1); break; - case ZEND_FE_FETCH: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: START_BLOCK_OP(ZEND_OP2(opline).opline_num); START_BLOCK_OP(opno + 2); break; @@ -205,12 +207,15 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz for (i = 0; i< op_array->last_brk_cont; i++) { if (op_array->brk_cont_array[i].start >= 0 && (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE || + op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE || op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) { int parent = op_array->brk_cont_array[i].parent; while (parent >= 0 && op_array->brk_cont_array[parent].start < 0 && - op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE) { + (op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE || + op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FE_FREE || + op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_END_SILENCE)) { parent = op_array->brk_cont_array[parent].parent; } op_array->brk_cont_array[i].parent = parent; @@ -225,6 +230,7 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz for (i = 0; i< op_array->last_brk_cont; i++) { if (op_array->brk_cont_array[i].start >= 0 && (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE || + op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE || op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) { if (i != j) { op_array->brk_cont_array[j] = op_array->brk_cont_array[i]; @@ -293,11 +299,13 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: - case ZEND_FE_RESET: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: case ZEND_NEW: case ZEND_JMP_SET: case ZEND_COALESCE: - case ZEND_FE_FETCH: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num]; /* break missing intentionally */ default: @@ -619,7 +627,7 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, ZEND_OP1_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST && opline->opcode != ZEND_CASE && /* CASE _always_ expects variable */ opline->opcode != ZEND_FETCH_LIST && - opline->opcode != ZEND_FE_RESET && + (opline->opcode != ZEND_FE_RESET_R || opline->opcode != ZEND_FE_RESET_RW) && opline->opcode != ZEND_FREE ) { zend_op *src = VAR_SOURCE(opline->op1); diff --git a/ext/opcache/Optimizer/nop_removal.c b/ext/opcache/Optimizer/nop_removal.c index 0bb8cce17f..dccd010ae4 100644 --- a/ext/opcache/Optimizer/nop_removal.c +++ b/ext/opcache/Optimizer/nop_removal.c @@ -93,8 +93,10 @@ void zend_optimizer_nop_removal(zend_op_array *op_array) case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: - case ZEND_FE_FETCH: - case ZEND_FE_RESET: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: case ZEND_NEW: case ZEND_JMP_SET: case ZEND_COALESCE: diff --git a/ext/opcache/Optimizer/optimize_temp_vars_5.c b/ext/opcache/Optimizer/optimize_temp_vars_5.c index 27ba6bad8e..dc93ce2f4c 100644 --- a/ext/opcache/Optimizer/optimize_temp_vars_5.c +++ b/ext/opcache/Optimizer/optimize_temp_vars_5.c @@ -66,16 +66,6 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) { start_of_T[VAR_NUM(ZEND_RESULT(opline).var) - offset] = opline; } - /* special puprose variable to keep HashPointer on VM stack */ - if (opline->opcode == ZEND_OP_DATA && - (opline-1)->opcode == ZEND_FE_FETCH && - opline->op1_type == IS_TMP_VAR) { - start_of_T[VAR_NUM(ZEND_OP1(opline).var) - offset] = opline; - if (sizeof(HashPointer) > sizeof(zval)) { - /* Make shure 1 zval is enough for HashPointer (2 must be enough) */ - start_of_T[VAR_NUM(ZEND_OP1(opline).var) + 1 - offset] = opline; - } - } opline--; } @@ -88,25 +78,13 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c while (opline >= end) { if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) { - /* special puprose variable to keep HashPointer on VM stack */ - if (opline->opcode == ZEND_OP_DATA && - (opline-1)->opcode == ZEND_FE_FETCH && - opline->op1_type == IS_TMP_VAR) { - max++; - ZEND_OP1(opline).var = NUM_VAR(max + offset); - if (sizeof(HashPointer) > sizeof(zval)) { - /* Make shure 1 zval is enough for HashPointer (2 must be enough) */ - max++; - } - } else { - currT = VAR_NUM(ZEND_OP1(opline).var) - offset; - if (!valid_T[currT]) { - GET_AVAILABLE_T(); - map_T[currT] = i; - valid_T[currT] = 1; - } - ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset); + currT = VAR_NUM(ZEND_OP1(opline).var) - offset; + if (!valid_T[currT]) { + GET_AVAILABLE_T(); + map_T[currT] = i; + valid_T[currT] = 1; } + ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset); } /* Skip OP_DATA */ diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index 348fcb8efb..341e80501b 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -602,8 +602,10 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: - case ZEND_FE_RESET: - case ZEND_FE_FETCH: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: case ZEND_NEW: case ZEND_JMP_SET: case ZEND_COALESCE: diff --git a/ext/opcache/Optimizer/pass2.c b/ext/opcache/Optimizer/pass2.c index 5be1e30c9f..b578592493 100644 --- a/ext/opcache/Optimizer/pass2.c +++ b/ext/opcache/Optimizer/pass2.c @@ -203,7 +203,9 @@ void zend_optimizer_pass2(zend_op_array *op_array) jmp_to = &op_array->brk_cont_array[array_offset]; array_offset = jmp_to->parent; if (--nest_levels > 0) { - if (op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE) { + if (op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE || + op_array->opcodes[jmp_to->brk].opcode == ZEND_FE_FREE || + op_array->opcodes[jmp_to->brk].opcode == ZEND_END_SILENCE) { dont_optimize = 1; break; } diff --git a/ext/opcache/Optimizer/pass3.c b/ext/opcache/Optimizer/pass3.c index c3a55b319f..3019b274e9 100644 --- a/ext/opcache/Optimizer/pass3.c +++ b/ext/opcache/Optimizer/pass3.c @@ -328,7 +328,8 @@ continue_jmp_ex_optimization: op->opcode == ZEND_RETURN || op->opcode == ZEND_RETURN_BY_REF || op->opcode == ZEND_FAST_RET || - op->opcode == ZEND_FE_FETCH || + op->opcode == ZEND_FE_FETCH_R || + op->opcode == ZEND_FE_FETCH_RW || op->opcode == ZEND_EXIT) { break; } @@ -363,7 +364,8 @@ continue_jmp_ex_optimization: op->opcode == ZEND_RETURN || op->opcode == ZEND_RETURN_BY_REF || op->opcode == ZEND_FAST_RET || - op->opcode == ZEND_FE_FETCH || + op->opcode == ZEND_FE_FETCH_R || + op->opcode == ZEND_FE_FETCH_RW || op->opcode == ZEND_EXIT) { break; } diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 54063d0ed3..a8506fb078 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -450,8 +450,10 @@ static void zend_accel_optimize(zend_op_array *op_array, case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_NEW: - case ZEND_FE_RESET: - case ZEND_FE_FETCH: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: ZEND_PASS_TWO_UNDO_JMP_TARGET(op_array, opline, ZEND_OP2(opline)); break; } @@ -488,8 +490,10 @@ static void zend_accel_optimize(zend_op_array *op_array, case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_NEW: - case ZEND_FE_RESET: - case ZEND_FE_FETCH: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, ZEND_OP2(opline)); break; } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 3d63f96e34..5b32baa64b 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -378,8 +378,10 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_NEW: - case ZEND_FE_RESET: - case ZEND_FE_FETCH: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: ZEND_OP2(opline).jmp_addr = &new_opcodes[ZEND_OP2(opline).jmp_addr - op_array->opcodes]; break; } diff --git a/ext/standard/array.c b/ext/standard/array.c index 966ea0f1f6..f923ad9e7b 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1877,26 +1877,54 @@ 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 { + uint32_t iter_pos = zend_hash_iterators_lower_pos(hash, 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; + if (idx == iter_pos) { + zend_hash_iterators_update(hash, idx, j); + iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1); + } + } + 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; @@ -1941,6 +1969,7 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re uint idx; Bucket *p; /* Pointer to hash bucket */ zval *entry; /* Hash entry */ + uint32_t iter_pos = zend_hash_iterators_lower_pos(in_hash, 0); /* Get number of entries in the input hash */ num_in = zend_hash_num_elements(in_hash); @@ -1966,7 +1995,6 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++) { p = in_hash->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; - pos++; /* Get entry and increase reference count */ entry = &p->val; @@ -1976,6 +2004,13 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re } else { zend_hash_add_new(&out_hash, p->key, entry); } + if (idx == iter_pos) { + if (idx != pos) { + zend_hash_iterators_update(in_hash, idx, pos); + } + iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1); + } + pos++; } /* If hash for removed entries exists, go until offset+length and copy the entries to it */ @@ -2001,10 +2036,12 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re } } } else { /* otherwise just skip those entries */ - for ( ; pos < offset + length && idx < in_hash->nNumUsed; idx++) { + int pos2 = pos; + + for ( ; pos2 < offset + length && idx < in_hash->nNumUsed; idx++) { p = in_hash->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; - pos++; + pos2++; if (p->key == NULL) { zend_hash_index_del(in_hash, p->h); } else { @@ -2016,12 +2053,14 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re } } } + iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos); /* If there are entries to insert.. */ if (replace) { ZEND_HASH_FOREACH_VAL_IND(replace, entry) { if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry); zend_hash_next_index_insert_new(&out_hash, entry); + pos++; } ZEND_HASH_FOREACH_END(); } @@ -2035,13 +2074,31 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re } else { zend_hash_add_new(&out_hash, p->key, entry); } + if (idx == iter_pos) { + if (idx != pos) { + zend_hash_iterators_update(in_hash, idx, pos); + } + iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1); + } + pos++; } - zend_hash_internal_pointer_reset(&out_hash); - + /* replace HashTable data */ + in_hash->u.v.nIteratorsCount = 0; in_hash->pDestructor = NULL; zend_hash_destroy(in_hash); - *in_hash = out_hash; + + in_hash->u.v.flags = out_hash.u.v.flags; + in_hash->nTableSize = out_hash.nTableSize; + in_hash->nTableMask = out_hash.nTableMask; + in_hash->nNumUsed = out_hash.nNumUsed; + in_hash->nNumOfElements = out_hash.nNumOfElements; + in_hash->nNextFreeElement = out_hash.nNextFreeElement; + in_hash->arData = out_hash.arData; + in_hash->arHash = out_hash.arHash; + in_hash->pDestructor = out_hash.pDestructor; + + zend_hash_internal_pointer_reset(in_hash); } /* }}} */ @@ -2194,17 +2251,38 @@ 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); + 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 { + uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 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); + if (idx == iter_pos) { + zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k); + iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1); + } + } + k++; } - k++; } Z_ARRVAL_P(stack)->nNumUsed = k; Z_ARRVAL_P(stack)->nNextFreeElement = k; @@ -2257,17 +2335,50 @@ 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; + uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 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); + } + old_idx = (Bucket*)value - Z_ARRVAL_P(stack)->arData; + if (old_idx == iter_pos) { + zend_hash_iterators_update(Z_ARRVAL_P(stack), old_idx, new_idx); + iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1); + } + 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)); /* Clean up and return the number of elements in the stack */ RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack))); |