/*
+------------------------------------------------------------------------+
| Zephir Language |
+------------------------------------------------------------------------+
| Copyright (c) 2011-2015 Zephir Team (http://www.zephir-lang.com) |
+------------------------------------------------------------------------+
| This source file is subject to the New BSD License that is bundled |
| with this package in the file docs/LICENSE.txt. |
| |
| If you did not receive a copy of the license and are unable to |
| obtain it through the world-wide-web, please send an email |
| to license@zephir-lang.com so we can send you a copy immediately. |
+------------------------------------------------------------------------+
| Authors: Andres Gutierrez <andres@zephir-lang.com> |
| Eduar Carvajal <eduar@zephir-lang.com> |
| Vladimir Kolesnikov <vladimir@extrememember.com> |
+------------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <php.h>
#include "php_ext.h"
#include <ext/standard/php_array.h>
#include <Zend/zend_hash.h>
#include "kernel/main.h"
#include "kernel/memory.h"
#include "kernel/debug.h"
#include "kernel/array.h"
#include "kernel/operators.h"
#include "kernel/hash.h"
#include "kernel/backtrace.h"
/**
* @brief Fetches @a index if it exists from the array @a arr
* @param[out] fetched <code>&$arr[$index]</code>; @a fetched is modified only when the function returns 1
* @param arr Array
* @param index Index
* @return isset($arr[$index])
* @retval 0 Not exists, @a arr is not an array or @a index is of not supported type
* @retval 1 Exists
* @note @c index will be handled as follows: @c NULL is treated as an empty string, @c double values are cast to @c integer, @c bool or @c resource are treated as @c integer
* @note $arr[$index] is returned as is: no copying occurs, reference copunt is not updated
* @throw E_WARNING if @a offset is not a scalar
*/
int zephir_array_isset_fetch(zval **fetched, const zval *arr, zval *index, int readonly TSRMLS_DC) {
HashTable *h;
zval **val;
int result;
if (Z_TYPE_P(arr) != IS_ARRAY) {
*fetched = ZEPHIR_GLOBAL(global_null);
if (!readonly) {
Z_ADDREF_P(*fetched);
}
return 0;
}
h = Z_ARRVAL_P(arr);
switch (Z_TYPE_P(index)) {
case IS_NULL:
result = zephir_hash_find(h, SS(""), (void**)&val);
break;
case IS_DOUBLE:
result = zend_hash_index_find(h, (ulong)Z_DVAL_P(index), (void**)&val);
break;
case IS_LONG:
case IS_BOOL:
case IS_RESOURCE:
result = zend_hash_index_find(h, Z_LVAL_P(index), (void**)&val);
break;
case IS_STRING:
result = zend_symtable_find(h, (Z_STRLEN_P(index) ? Z_STRVAL_P(index) : ""), Z_STRLEN_P(index)+1, (void**)&val);
break;
default:
zend_error(E_WARNING, "Illegal offset type");
*fetched = ZEPHIR_GLOBAL(global_null);
if (!readonly) {
Z_ADDREF_P(*fetched);
}
return 0;
}
if (result == SUCCESS) {
*fetched = *val;
if (!readonly) {
Z_ADDREF_P(*fetched);
}
return 1;
}
*fetched = ZEPHIR_GLOBAL(global_null);
if (!readonly) {
Z_ADDREF_P(*fetched);
}
return 0;
}
int zephir_array_isset_quick_string_fetch(zval **fetched, zval *arr, char *index, uint index_length, unsigned long key, int readonly TSRMLS_DC) {
zval **zv;
if (likely(Z_TYPE_P(arr) == IS_ARRAY)) {
if (zephir_hash_quick_find(Z_ARRVAL_P(arr), index, index_length, key, (void**) &zv) == SUCCESS) {
*fetched = *zv;
if (!readonly) {
Z_ADDREF_P(*fetched);
}
return 1;
}
}
*fetched = ZEPHIR_GLOBAL(global_null);
if (!readonly) {
Z_ADDREF_P(*fetched);
}
return 0;
}
int zephir_array_isset_string_fetch(zval **fetched, zval *arr, char *index, uint index_length, int readonly TSRMLS_DC) {
return zephir_array_isset_quick_string_fetch(fetched, arr, index, index_length, zend_inline_hash_func(index, index_length), readonly TSRMLS_CC);
}
int zephir_array_isset_long_fetch(zval **fetched, zval *arr, unsigned long index, int readonly TSRMLS_DC) {
zval **zv;
if (likely(Z_TYPE_P(arr) == IS_ARRAY)) {
if (zend_hash_index_find(Z_ARRVAL_P(arr), index, (void**)&zv) == SUCCESS) {
*fetched = *zv;
if (!readonly) {
Z_ADDREF_P(*fetched);
}
return 1;
}
}
*fetched = ZEPHIR_GLOBAL(global_null);
if (!readonly) {
Z_ADDREF_P(*fetched);
}
return 0;
}
/**
* @brief Checks whether @a index exists in array @a arr
* @param arr Array
* @param index Index
* @return isset($arr[$index])
* @retval 0 Not exists, @a arr is not an array or @a index is of not supported type
* @retval 1 Exists
* @note @c index will be handled as follows: @c NULL is treated as an empty string, @c double values are cast to @c integer, @c bool or @c resource are treated as @c integer
* @throw E_WARNING if @a offset is not a scalar
*/
int ZEPHIR_FASTCALL zephir_array_isset(const zval *arr, zval *index) {
HashTable *h;
if (Z_TYPE_P(arr) != IS_ARRAY) {
return 0;
}
h = Z_ARRVAL_P(arr);
switch (Z_TYPE_P(index)) {
case IS_NULL:
return zephir_hash_exists(h, SS(""));
case IS_DOUBLE:
return zend_hash_index_exists(h, (ulong)Z_DVAL_P(index));
case IS_BOOL:
case IS_LONG:
case IS_RESOURCE:
return zend_hash_index_exists(h, Z_LVAL_P(index));
case IS_STRING:
return zend_symtable_exists(h, Z_STRVAL_P(index), Z_STRLEN_P(index)+1);
default:
zend_error(E_WARNING, "Illegal offset type");
return 0;
}
}
/**
* @brief Checks whether string @a index exists in array @a arr
* @param arr Array
* @param index Index
* @param index_length strlen(index)+1
* @return isset($arr[$index])
* @retval 0 Not exists, @a arr is not an array
* @retval 1 Exists
* @note The function is a wrapper around zephir_array_isset_quick_string()
* @see zephir_array_isset_quick_string()
*/
int ZEPHIR_FASTCALL zephir_array_isset_string(const zval *arr, const char *index, uint index_length) {
return zephir_array_isset_quick_string(arr, index, index_length, zend_inline_hash_func(index, index_length));
}
/**
* @brief Checks whether string @a index exists in array @a arr using a precomputed key @a key
* @param arr Array
* @param index Index
* @param index_length strlen(index)+1
* @param key Precomputed key
* @return isset($arr[$index])
* @retval 0 Not exists or @a arr is not an array
* @retval 1 Exists
*/
int ZEPHIR_FASTCALL zephir_array_isset_quick_string(const zval *arr, const char *index, uint index_length, unsigned long key) {
if (likely(Z_TYPE_P(arr) == IS_ARRAY)) {
return zend_hash_quick_exists(Z_ARRVAL_P(arr), index, index_length, key);
}
return 0;
}
/**
* @brief Checks whether numeric @a index exists in array @a arr using a precomputed key @a key
* @param arr Array
* @param index Index
* @return isset($arr[$index])
* @retval 0 Not exists or @a arr is not an array
* @retval 1 Exists
*/
int ZEPHIR_FASTCALL zephir_array_isset_long(const zval *arr, unsigned long index) {
if (likely(Z_TYPE_P(arr) == IS_ARRAY)) {
return zend_hash_index_exists(Z_ARRVAL_P(arr), index);
}
return 0;
}
/**
* @brief Unsets @a index from array @a arr
* @param[in,out] arr Array
* @param index Index
* @param flags Flags (@c PH_SEPARATE: separate array if its reference count is greater than 1; @c arr will contain the separated array)
* @return Whether the operation succeeded
* @retval @c FAILURE Failure, @a arr is not an array or @a index is of not supported type
* @retval @c SUCCESS Success
* @note @c index will be handled as follows: @c NULL is treated as an empty string, @c double values are cast to @c integer, @c bool or @c resource are treated as @c integer
* @throw @c E_WARNING if @a offset is not a scalar
*/
int ZEPHIR_FASTCALL zephir_array_unset(zval **arr, zval *index, int flags) {
HashTable *ht;
if (Z_TYPE_PP(arr) != IS_ARRAY) {
return FAILURE;
}
if ((flags & PH_SEPARATE) == PH_SEPARATE) {
SEPARATE_ZVAL_IF_NOT_REF(arr);
}
ht = Z_ARRVAL_PP(arr);
switch (Z_TYPE_P(index)) {
case IS_NULL:
return (zend_hash_del(ht, "", 1) == SUCCESS);
case IS_DOUBLE:
return (zend_hash_index_del(ht, (ulong)Z_DVAL_P(index)) == SUCCESS);
case IS_LONG:
case IS_BOOL:
case IS_RESOURCE:
return (zend_hash_index_del(ht, Z_LVAL_P(index)) == SUCCESS);
case IS_STRING:
return (zend_symtable_del(ht, Z_STRVAL_P(index), Z_STRLEN_P(index)+1) == SUCCESS);
default:
zend_error(E_WARNING, "Illegal offset type");
return 0;
}
}
/**
* @brief Unsets string @a index from array @a arr
* @param[in,out] arr Array
* @param index Index
* @param index_length strlen(index)+1
* @param flags Flags (@c PH_SEPARATE: separate array if its reference count is greater than 1; @c arr will contain the separated array)
* @return Whether the operation succeeded
* @retval @c FAILURE Failure or @a arr is not an array
* @retval @c SUCCESS Success
*/
int ZEPHIR_FASTCALL zephir_array_unset_string(zval **arr, const char *index, uint index_length, int flags) {
if (Z_TYPE_PP(arr) != IS_ARRAY) {
return 0;
}
if ((flags & PH_SEPARATE) == PH_SEPARATE) {
SEPARATE_ZVAL_IF_NOT_REF(arr);
}
return zend_hash_del(Z_ARRVAL_PP(arr), index, index_length);
}
/**
* @brief Unsets numeric @a index from array @a arr
* @param[in,out] arr Array
* @param index Index
* @param flags Flags (@c PH_SEPARATE: separate array if its reference count is greater than 1; @c arr will contain the separated array)
* @return Whether the operation succeeded
* @retval @c FAILURE Failure or @a arr is not an array
* @retval @c SUCCESS Success
*/
int ZEPHIR_FASTCALL zephir_array_unset_long(zval **arr, unsigned long index, int flags) {
if (Z_TYPE_PP(arr) != IS_ARRAY) {
return 0;
}
if ((flags & PH_SEPARATE) == PH_SEPARATE) {
SEPARATE_ZVAL_IF_NOT_REF(arr);
}
return zend_hash_index_del(Z_ARRVAL_PP(arr), index);
}
/**
* @brief Pushes @a value onto the end of @a arr
* @param[in,out] arr Array
* @param[in,out] value Value to add; reference counter of @c *value will be incrememnted
* @param flags Flags (@c PH_SEPARATE: separate array if its reference count is greater than 1; @c arr will contain the separated array)
* @return Whether the operation succeeded
* @retval @c FAILURE Failure or @a arr is not an array
* @retval @c SUCCESS Success
* @throw @c E_WARNING if @a is not an array
*/
int zephir_array_append(zval **arr, zval *value, int flags ZEPHIR_DEBUG_PARAMS) {
if (Z_TYPE_PP(arr) != IS_ARRAY) {
zend_error(E_WARNING, "Cannot use a scalar value as an array in %s on line %d", file, line);
return FAILURE;
}
if ((flags & PH_SEPARATE) == PH_SEPARATE) {
SEPARATE_ZVAL_IF_NOT_REF(arr);
}
Z_ADDREF_P(value);
return add_next_index_zval(*arr, value);
}
/**
* @brief Appends a long integer @a value to @a arr
* @param[in,out] arr Array
* @param value Value
* @param separate Flags (@c PH_SEPARATE: separate array if its reference count is greater than 1; @c arr will contain the separated array)
* @return Whether the operation succeeded
* @retval @c FAILURE Failure or @a arr is not an array
* @retval @c SUCCESS Success
* @throw @c E_WARNING if @a is not an array
* @see zephir_array_append()
*
* Equivalent to <tt>$arr[] = $value</tt> in PHP, where @c $value is an integer.
*/
int zephir_array_append_long(zval **arr, long value, int separate) {
zval *zvalue;
ALLOC_INIT_ZVAL(zvalue);
Z_SET_REFCOUNT_P(zvalue, 0);
ZVAL_LONG(zvalue, value);
return zephir_array_append(arr, zvalue, separate ZEPHIR_DEBUG_PARAMS_DUMMY);
}
/**
* @brief Appends a string @a value to @a arr
* @param[in,out] arr Array
* @param value Value
* @param value_length Length of the value (usually <tt>strlen(value)</tt>)
* @param separate Flags (@c PH_SEPARATE: separate array if its reference count is greater than 1; @c arr will contain the separated array)
* @return Whether the operation succeeded
* @retval @c FAILURE Failure or @a arr is not an array
* @retval @c SUCCESS Success
* @throw @c E_WARNING if @a is not an array
* @see zephir_array_append()
*
* Equivalent to <tt>$arr[] = $value</tt> in PHP, where @c $value is a string.
*/
int zephir_array_append_string(zval **arr, char *value, uint value_length, int separate) {
zval *zvalue;
ALLOC_INIT_ZVAL(zvalue);
Z_SET_REFCOUNT_P(zvalue, 0);
ZVAL_STRINGL(zvalue, value, value_length, 1);
return zephir_array_append(arr, zvalue, separate ZEPHIR_DEBUG_PARAMS_DUMMY);
}
/**
* @brief Updates value in @a arr at position @a index with @a value
* @param[in,out] arr Array
* @param index Index
* @param[in,out] value Value
* @param flags Flags
* @return Whether the operation succeeded
* @retval @c FAILURE Failure, @a arr is not an array or @a index is of not supported type
* @retval @c SUCCESS Success
* @note @c index will be handled as follows: @c NULL is treated as an empty string, @c double values are cast to @c integer, @c bool or @c resource are treated as @c integer
* @throw @c E_WARNING if @a offset is not a scalar or @c arr is not an array
*
* Equivalent to <tt>$arr[$index] = $value</tt> in PHP.
* Flags may be a bitwise OR of the following values:
* @arg @c PH_CTOR: create a copy of @a value and work with that copy; @c *value will be updated with the newly constructed value
* @arg @c PH_SEPARATE: separate @a arr if its reference count is greater than 1; @c *arr will contain the separated version
* @arg @c PH_COPY: increment the reference count on @c **value
*/
int zephir_array_update_zval(zval **arr, zval *index, zval **value, int flags) {
HashTable *ht;
if (Z_TYPE_PP(arr) != IS_ARRAY) {
zend_error(E_WARNING, "Cannot use a scalar value as an array (2)");
return FAILURE;
}
if ((flags & PH_CTOR) == PH_CTOR) {
zval *new_zv;
Z_DELREF_PP(value);
ALLOC_ZVAL(new_zv);
INIT_PZVAL_COPY(new_zv, *value);
*value = new_zv;
zval_copy_ctor(new_zv);
}
if ((flags & PH_SEPARATE) == PH_SEPARATE) {
SEPARATE_ZVAL_IF_NOT_REF(arr);
}
if ((flags & PH_COPY) == PH_COPY) {
Z_ADDREF_PP(value);
}
ht = Z_ARRVAL_PP(arr);
switch (Z_TYPE_P(index)) {
case IS_NULL:
return zend_symtable_update(ht, "", 1, value, sizeof(zval*), NULL);
case IS_DOUBLE:
return zend_hash_index_update(ht, (ulong)Z_DVAL_P(index), value, sizeof(zval*), NULL);
case IS_LONG:
case IS_BOOL:
case IS_RESOURCE:
return zend_hash_index_update(ht, Z_LVAL_P(index), value, sizeof(zval*), NULL);
case IS_STRING:
return zend_symtable_update(ht, Z_STRVAL_P(index), Z_STRLEN_P(index)+1, value, sizeof(zval*), NULL);
default:
zend_error(E_WARNING, "Illegal offset type");
return FAILURE;
}
}
/**
* @brief Updates value in @a arr at position @a index with boolean @a value
* @param[in,out] arr Array
* @param index Index
* @param value Value
* @param flags Flags
* @return Whether the operation succeeded
* @retval @c FAILURE Failure, @a arr is not an array or @a index is of not supported type
* @retval @c SUCCESS Success
* @throw @c E_WARNING if @a arr is not an array
* @see zephir_array_update_zval()
*
* Equivalent to <tt>$arr[$index] = $value</tt> in PHP, where @c $value is a boolean.
* Flags may be a bitwise OR of the following values:
* @arg @c PH_CTOR: create a copy of @a value and work with that copy
* @arg @c PH_SEPARATE: separate @a arr if its reference count is greater than 1; @c *arr will contain the separated version
* @arg @c PH_COPY: increment the reference count on the internally constructed value
*
* Only @c PH_SEPARATE is meaningful with this function
*/
int zephir_array_update_zval_bool(zval **arr, zval *index, int value, int flags) {
zval *zvalue;
ALLOC_INIT_ZVAL(zvalue);
ZVAL_BOOL(zvalue, value);
return zephir_array_update_zval(arr, index, &zvalue, flags);
}
/**
* @brief Updates value in @a arr at position @a index with boolean @a value
* @param[in,out] arr Array
* @param index Index
* @param value Value
* @param value_length Length of value (usually <tt>strlen(value)</tt>)
* @param flags Flags
* @return Whether the operation succeeded
* @retval @c FAILURE Failure, @a arr is not an array or @a index is of not supported type
* @retval @c SUCCESS Success
* @throw @c E_WARNING if @a arr is not an array
* @see zephir_array_update_zval()
*
* Equivalent to <tt>$arr[$index] = $value</tt> in PHP, where @c $value is a string.
* Flags may be a bitwise OR of the following values:
* @arg @c PH_CTOR: create a copy of @a value and work with that copy
* @arg @c PH_SEPARATE: separate @a arr if its reference count is greater than 1; @c *arr will contain the separated version
* @arg @c PH_COPY: increment the reference count on the internally constructed value
*
* Only @c PH_SEPARATE is meaningful with this function
*/
int zephir_array_update_zval_string(zval **arr, zval *index, char *value, uint value_length, int flags) {
zval *zvalue;
ALLOC_INIT_ZVAL(zvalue);
ZVAL_STRINGL(zvalue, value, value_length, 1);
return zephir_array_update_zval(arr, index, &zvalue, flags);
}
/**
* @brief Updates value in @a arr at position @a index with long integer @a value
* @param[in,out] arr Array
* @param index Index
* @param value Value
* @param flags Flags
* @return Whether the operation succeeded
* @retval @c FAILURE Failure, @a arr is not an array or @a index is of not supported type
* @retval @c SUCCESS Success
* @throw @c E_WARNING if @a arr is not an array
* @see zephir_array_update_zval()
*
* Equivalent to <tt>$arr[$index] = $value</tt> in PHP, where @c $value is an integer.
* Flags may be a bitwise OR of the following values:
* @arg @c PH_CTOR: create a copy of @a value and work with that copy
* @arg @c PH_SEPARATE: separate @a arr if its reference count is greater than 1; @c *arr will contain the separated version
* @arg @c PH_COPY: increment the reference count on the internally constructed value
*
* Only @c PH_SEPARATE is meaningful with this function.
*/
int zephir_array_update_zval_long(zval **arr, zval *index, long value, int flags) {
zval *zvalue;
ALLOC_INIT_ZVAL(zvalue);
ZVAL_LONG(zvalue, value);
return zephir_array_update_zval(arr, index, &zvalue, flags);
}
/**
* @brief Updates value in @a arr at position @a index with @a value using the precomputed hash @a key
* @param[in,out] arr Array
* @param index Index
* @param index_length Length of the index, should include the trailing zero
* @param key Precomputed hash of @c value
* @param value Value
* @param flags Flags
* @return Whether the operation succeeded
* @retval @c FAILURE Failure, @a arr is not an array
* @retval @c SUCCESS Success
* @throw @c E_WARNING if @a arr is not an array
*
* Equivalent to <tt>$arr[$index] = $value</tt> in PHP.
*
* Flags may be a bitwise OR of the following values:
* @arg @c PH_CTOR: create a copy of @a value and work with that copy; @c *value will be updated with the newly constructed value
* @arg @c PH_SEPARATE: separate @a arr if its reference count is greater than 1; @c *arr will contain the separated version
* @arg @c PH_COPY: increment the reference count on @c **value
*/
int zephir_array_update_quick_string(zval **arr, const char *index, uint index_length, unsigned long key, zval **value, int flags){
if (Z_TYPE_PP(arr) != IS_ARRAY) {
zend_error(E_WARNING, "Cannot use a scalar value as an array (3)");
return FAILURE;
}
if ((flags & PH_CTOR) == PH_CTOR) {
zval *new_zv;
Z_DELREF_PP(value);
ALLOC_ZVAL(new_zv);
INIT_PZVAL_COPY(new_zv, *value);
*value = new_zv;
zval_copy_ctor(new_zv);
}
if ((flags & PH_SEPARATE) == PH_SEPARATE) {
SEPARATE_ZVAL_IF_NOT_REF(arr);
}
if ((flags & PH_COPY) == PH_COPY) {
Z_ADDREF_PP(value);
}
return zend_hash_quick_update(Z_ARRVAL_PP(arr), index, index_length, key, value, sizeof(zval *), NULL);
}
/**
* @brief Updates value in @a arr at position @a index with @a value
* @param[in,out] arr Array
* @param index Index
* @param index_length Length of the index, should include the trailing zero
* @param value Value
* @param flags Flags
* @return Whether the operation succeeded
* @retval @c FAILURE Failure, @a arr is not an array
* @retval @c SUCCESS Success
* @throw @c E_WARNING if @a arr is not an array
* @see zephir_array_update_quick_string()
*
* The function is a wrapper over @c zephir_array_update_quick_string()
*
* Flags may be a bitwise OR of the following values:
* @arg @c PH_CTOR: create a copy of @a value and work with that copy; @c *value will be updated with the newly constructed value
* @arg @c PH_SEPARATE: separate @a arr if its reference count is greater than 1; @c *arr will contain the separated version
* @arg @c PH_COPY: increment the reference count on @c **value
*/
int zephir_array_update_string(zval **arr, const char *index, uint index_length, zval **value, int flags) {
return zephir_array_update_quick_string(arr, index, index_length + 1, zend_inline_hash_func(index, index_length + 1), value, flags);
}
/**
* @brief Updates value in @a arr at position @a index with boolean @a value
* @param[in,out] arr Array
* @param index Index
* @param index_length Length of the index, should include the trailing zero
* @param value Value
* @param flags Flags
* @return Whether the operation succeeded
* @retval @c FAILURE Failure, @a arr is not an array
* @retval @c SUCCESS Success
* @throw @c E_WARNING if @a arr is not an array
* @see zephir_array_update_string()
*
* Equivalent to <tt>$arr[$index] = $value</tt> in PHP, where @c $index is a string key and $value is a boolean.
*
* Flags may be a bitwise OR of the following values:
* @arg @c PH_CTOR: create a copy of @a value and work with that copy
* @arg @c PH_SEPARATE: separate @a arr if its reference count is greater than 1; @c *arr will contain the separated version
* @arg @c PH_COPY: increment the reference count on the internally constructed value
*
* Only @c PH_SEPARATE is meaningful with this function.
*/
int zephir_array_update_string_bool(zval **arr, const char *index, uint index_length, int value, int flags){
zval *zvalue;
ALLOC_INIT_ZVAL(zvalue);
ZVAL_BOOL(zvalue, value);
return zephir_array_update_string(arr, index, index_length, &zvalue, flags);
}
/**
* @brief Updates value in @a arr at position @a index with integer @a value
* @param[in,out] arr Array
* @param index Index
* @param index_length Length of the index, should include the trailing zero
* @param value Value
* @param flags Flags
* @return Whether the operation succeeded
* @retval @c FAILURE Failure, @a arr is not an array
* @retval @c SUCCESS Success
* @throw @c E_WARNING if @a arr is not an array
* @see zephir_array_update_string()
*
* Equivalent to <tt>$arr[$index] = $value</tt> in PHP, where @c $index is a string key and $value is an integer.
*
* Flags may be a bitwise OR of the following values:
* @arg @c PH_CTOR: create a copy of @a value and work with that copy
* @arg @c PH_SEPARATE: separate @a arr if its reference count is greater than 1; @c *arr will contain the separated version
* @arg @c PH_COPY: increment the reference count on the internally constructed value
*
* Only @c PH_SEPARATE is meaningful with this function.
*/
int zephir_array_update_string_long(zval **arr, const char *index, uint index_length, long value, int flags){
zval *zvalue;
ALLOC_INIT_ZVAL(zvalue);
ZVAL_LONG(zvalue, value);
return zephir_array_update_string(arr, index, index_length, &zvalue, flags);
}
/**
* @brief Updates value in @a arr at position @a index with string @a value
* @param[in,out] arr Array
* @param index Index
* @param index_length Length of the index, should include the trailing zero
* @param value Value
* @param value_length Length of the @a value; usually @c strlen()
* @param flags Flags
* @return Whether the operation succeeded
* @retval @c FAILURE Failure, @a arr is not an array
* @retval @c SUCCESS Success
* @throw @c E_WARNING if @a arr is not an array
* @see zephir_array_update_string()
*
* Equivalent to <tt>$arr[$index] = $value</tt> in PHP, where @c $index is a string key and $value is a boolean.
*
* Flags may be a bitwise OR of the following values:
* @arg @c PH_CTOR: create a copy of @a value and work with that copy
* @arg @c PH_SEPARATE: separate @a arr if its reference count is greater than 1; @c *arr will contain the separated version
* @arg @c PH_COPY: increment the reference count on the internally constructed value
*
* Only @c PH_SEPARATE is meaningful with this function.
*/
int zephir_array_update_string_string(zval **arr, const char *index, uint index_length, char *value, uint value_length, int flags){
zval *zvalue;
ALLOC_INIT_ZVAL(zvalue);
ZVAL_STRINGL(zvalue, value, value_length, 1);
return zephir_array_update_string(arr, index, index_length, &zvalue, flags);
}
/**
* @brief Updates value in @a arr at position @a index with @a value
* @param[in,out] arr Array
* @param index Index
* @param[in,out] value Value
* @param flags Flags
* @return Whether the operation succeeded
* @retval @c FAILURE Failure, @a arr is not an array
* @retval @c SUCCESS Success
* @throw @c E_WARNING if @c arr is not an array
*
* Equivalent to <tt>$arr[$index] = $value</tt> in PHP where @c $index is an integer.
* Flags may be a bitwise OR of the following values:
* @arg @c PH_CTOR: create a copy of @a value and work with that copy; @c *value will be updated with the newly constructed value
* @arg @c PH_SEPARATE: separate @a arr if its reference count is greater than 1; @c *arr will contain the separated version
* @arg @c PH_COPY: increment the reference count on @c **value
*/
int zephir_array_update_long(zval **arr, unsigned long index, zval **value, int flags ZEPHIR_DEBUG_PARAMS){
if (Z_TYPE_PP(arr) != IS_ARRAY) {
zend_error(E_WARNING, "Cannot use a scalar value as an array in %s on line %d", file, line);
return FAILURE;
}
if ((flags & PH_CTOR) == PH_CTOR) {
zval *new_zv;
Z_DELREF_PP(value);
ALLOC_ZVAL(new_zv);
INIT_PZVAL_COPY(new_zv, *value);
*value = new_zv;
zval_copy_ctor(new_zv);
}
if ((flags & PH_SEPARATE) == PH_SEPARATE) {
SEPARATE_ZVAL_IF_NOT_REF(arr);
}
if ((flags & PH_COPY) == PH_COPY) {
Z_ADDREF_PP(value);
}
return zend_hash_index_update(Z_ARRVAL_PP(arr), index, value, sizeof(zval *), NULL);
}
/**
* @brief Reads an item from @a arr at position @a index and stores it to @a return_value
* @param return_value[out] Return value
* @param arr Array
* @param index Index
* @param silent 0 to suppress all warnings, @c PH_NOISY to enable
* @return Whether the operation succeeded
* @retval @c FAILURE Failure, @a arr is not an array
* @retval @c SUCCESS Success
* @throw @c E_WARNING if @c arr is not an array and @c silent = @c PH_NOISY
* @throw @c E_WARNING if @c index is not of the supported type and @c silent = @c PH_NOISY
* @throw @c E_NOTICE if @c index does not exist and @c silent = @c PH_NOISY
* @warning @c *return_value should be either @c NULL (preferred) or point to not initialized memory; if @c *return_value points to a valid variable, mmemory leak is possible
* @note @c index will be handled as follows: @c NULL is treated as an empty string, @c double values are cast to @c integer, @c bool or @c resource are treated as @c integer
*/
int zephir_array_fetch(zval **return_value, zval *arr, zval *index, int flags ZEPHIR_DEBUG_PARAMS TSRMLS_DC){
zval **zv;
HashTable *ht;
int result;
ulong uidx = 0;
char *sidx = NULL;
if (Z_TYPE_P(arr) == IS_ARRAY) {
ht = Z_ARRVAL_P(arr);
switch (Z_TYPE_P(index)) {
case IS_NULL:
result = zephir_hash_find(ht, SS(""), (void**) &zv);
sidx = "";
break;
case IS_DOUBLE:
uidx = (ulong)Z_DVAL_P(index);
result = zend_hash_index_find(ht, uidx, (void**) &zv);
break;
case IS_LONG:
case IS_BOOL:
case IS_RESOURCE:
uidx = Z_LVAL_P(index);
result = zend_hash_index_find(ht, uidx, (void**) &zv);
break;
case IS_STRING:
sidx = Z_STRLEN_P(index) ? Z_STRVAL_P(index) : "";
result = zend_symtable_find(ht, Z_STRVAL_P(index), Z_STRLEN_P(index)+1, (void**) &zv);
break;
default:
if ((flags & PH_NOISY) == PH_NOISY) {
zend_error(E_WARNING, "Illegal offset type in %s on line %d", file, line);
}
result = FAILURE;
break;
}
if (result != FAILURE) {
*return_value = *zv;
if ((flags & PH_READONLY) != PH_READONLY) {
Z_ADDREF_PP(return_value);
}
return SUCCESS;
}
if ((flags & PH_NOISY) == PH_NOISY) {
if (sidx == NULL) {
zend_error(E_NOTICE, "Undefined index: %ld in %s on line %d", uidx, file, line);
} else {
zend_error(E_NOTICE, "Undefined index: %s in %s on line %d", sidx, file, line);
}
}
}
*return_value = ZEPHIR_GLOBAL(global_null);
if ((flags & PH_READONLY) != PH_READONLY) {
Z_ADDREF_PP(return_value);
}
return FAILURE;
}
/**
* @brief Reads an item from @a arr at position @a index using the precomputed hash @c key and stores it to @a return_value
* @param return_value[out] Return value
* @param arr Array
* @param index Index
* @param index Index length; must contain the trailing zero, if any
* @param key Precomputed hash of @c index
* @param silent 0 to suppress all warnings, @c PH_NOISY to enable
* @return Whether the operation succeeded
* @retval @c FAILURE Failure, @a arr is not an array
* @retval @c SUCCESS Success
* @throw @c E_WARNING if @c arr is not an array and @c silent = @c PH_NOISY
* @throw @c E_NOTICE if @c index does not exist and @c silent = @c PH_NOISY
* @warning @c *return_value should be either @c NULL (preferred) or point to not initialized memory; if @c *return_value points to a valid variable, mmemory leak is possible
*/
int zephir_array_fetch_quick_string(zval **return_value, zval *arr, const char *index, uint index_length, unsigned long key, int flags ZEPHIR_DEBUG_PARAMS TSRMLS_DC){
zval **zv;
if (likely(Z_TYPE_P(arr) == IS_ARRAY)) {
if (zephir_hash_quick_find(Z_ARRVAL_P(arr), index, index_length, key, (void**) &zv) == SUCCESS) {
*return_value = *zv;
if ((flags & PH_READONLY) != PH_READONLY) {
Z_ADDREF_PP(return_value);
}
return SUCCESS;
}
if ((flags & PH_NOISY) == PH_NOISY) {
zend_error(E_NOTICE, "Undefined index: %s", index);
}
} else {
if ((flags & PH_NOISY) == PH_NOISY) {
zend_error(E_NOTICE, "Cannot use a scalar value as an array in %s on line %d", file, line);
}
}
*return_value = ZEPHIR_GLOBAL(global_null);
if ((flags & PH_READONLY) != PH_READONLY) {
Z_ADDREF_PP(return_value);
}
return FAILURE;
}
/**
* @brief Reads an item from @a arr at position @a index and stores it to @a return_value
* @param return_value[out] Return value
* @param arr Array
* @param index Index
* @param index Index length; must contain the trailing zero, if any
* @param silent 0 to suppress all warnings, @c PH_NOISY to enable
* @return Whether the operation succeeded
* @retval @c FAILURE Failure, @a arr is not an array
* @retval @c SUCCESS Success
* @throw @c E_WARNING if @c arr is not an array and @c silent = @c PH_NOISY
* @throw @c E_NOTICE if @c index does not exist and @c silent = @c PH_NOISY
* @warning @c *return_value should be either @c NULL (preferred) or point to not initialized memory; if @c *return_value points to a valid variable, mmemory leak is possible
* @see zephir_array_fetch_quick_string()
*
* The function is a wrapper over @c zephir_array_fetch_quick_string()
*/
int zephir_array_fetch_string(zval **return_value, zval *arr, const char *index, uint index_length, int flags ZEPHIR_DEBUG_PARAMS TSRMLS_DC){
return zephir_array_fetch_quick_string(return_value, arr, index, index_length + 1, zend_inline_hash_func(index, index_length + 1), flags, file, line TSRMLS_CC);
}
/**
* @brief Reads an item from @a arr at position @a index and stores it to @a return_value
* @param return_value[out] Return value
* @param arr Array
* @param index Index
* @param silent 0 to suppress all warnings, @c PH_NOISY to enable
* @return Whether the operation succeeded
* @retval @c FAILURE Failure, @a arr is not an array
* @retval @c SUCCESS Success
* @throw @c E_WARNING if @c arr is not an array and @c silent = @c PH_NOISY
* @throw @c E_NOTICE if @c index does not exist and @c silent = @c PH_NOISY
* @warning @c *return_value should be either @c NULL (preferred) or point to not initialized memory; if @c *return_value points to a valid variable, mmemory leak is possible
*/
int zephir_array_fetch_long(zval **return_value, zval *arr, unsigned long index, int flags ZEPHIR_DEBUG_PARAMS TSRMLS_DC){
zval **zv;
if (likely(Z_TYPE_P(arr) == IS_ARRAY)) {
if (zend_hash_index_find(Z_ARRVAL_P(arr), index, (void**)&zv) == SUCCESS) {
*return_value = *zv;
if ((flags & PH_READONLY) != PH_READONLY) {
Z_ADDREF_PP(return_value);
}
return SUCCESS;
}
if ((flags & PH_NOISY) == PH_NOISY) {
zend_error(E_NOTICE, "Undefined index: %lu in %s on line %d", index, file, line);
}
}
else {
if ((flags & PH_NOISY) == PH_NOISY) {
zend_error(E_NOTICE, "Cannot use a scalar value as an array in %s on line %d", file, line);
}
}
*return_value = ZEPHIR_GLOBAL(global_null);
if ((flags & PH_READONLY) != PH_READONLY) {
Z_ADDREF_PP(return_value);
}
return FAILURE;
}
/**
* Appends every element of an array at the end of the left array
*/
void zephir_merge_append(zval *left, zval *values){
zval **tmp;
HashTable *arr_values;
HashPosition pos;
if (Z_TYPE_P(left) != IS_ARRAY) {
zend_error(E_NOTICE, "First parameter of zephir_merge_append must be an array");
return;
}
if (Z_TYPE_P(values) == IS_ARRAY) {
arr_values = Z_ARRVAL_P(values);
zend_hash_internal_pointer_reset_ex(arr_values, &pos);
while (zend_hash_get_current_data_ex(arr_values, (void **) &tmp, &pos) == SUCCESS) {
Z_ADDREF_PP(tmp);
add_next_index_zval(left, *tmp);
zend_hash_move_forward_ex(arr_values, &pos);
}
} else {
Z_ADDREF_P(values);
add_next_index_zval(left, values);
}
}
/**
* Gets the current element in a zval hash
*/
void zephir_array_get_current(zval *return_value, zval *array){
zval **entry;
if (Z_TYPE_P(array) == IS_ARRAY) {
if (zend_hash_get_current_data(Z_ARRVAL_P(array), (void **) &entry) == FAILURE) {
RETURN_FALSE;
}
RETURN_ZVAL(*entry, 1, 0);
}
RETURN_FALSE;
}
/**
* Gets the current element in a zval hash
*/
void zephir_array_next(zval *array){
if (Z_TYPE_P(array) == IS_ARRAY) {
zend_hash_move_forward(Z_ARRVAL_P(array));
}
}
/**
* Fast in_array function
*/
int zephir_fast_in_array(zval *needle, zval *haystack TSRMLS_DC) {
zval **tmp;
HashTable *arr;
HashPosition pos;
unsigned int numelems;
if (Z_TYPE_P(haystack) != IS_ARRAY) {
return 0;
}
arr = Z_ARRVAL_P(haystack);
numelems = zend_hash_num_elements(arr);
if (numelems == 0) {
return 0;
}
zend_hash_internal_pointer_reset_ex(arr, &pos);
while (zend_hash_get_current_data_ex(arr, (void **) &tmp, &pos) == SUCCESS) {
if (ZEPHIR_IS_EQUAL(needle, *tmp)) {
return 1;
}
zend_hash_move_forward_ex(arr, &pos);
}
return 0;
}
/**
* Fast array merge
*/
void zephir_fast_array_merge(zval *return_value, zval **array1, zval **array2 TSRMLS_DC) {
int init_size, num;
if (Z_TYPE_PP(array1) != IS_ARRAY) {
zend_error(E_WARNING, "First argument is not an array");
RETURN_NULL();
}
if (Z_TYPE_PP(array2) != IS_ARRAY) {
zend_error(E_WARNING, "Second argument is not an array");
RETURN_NULL();
}
init_size = zend_hash_num_elements(Z_ARRVAL_PP(array1));
num = zend_hash_num_elements(Z_ARRVAL_PP(array2));
if (num > init_size) {
init_size = num;
}
array_init_size(return_value, init_size);
php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(array1), 0 TSRMLS_CC);
php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(array2), 0 TSRMLS_CC);
}
/**
* @brief Merge @a a1 and @a a2 recursively preserving all keys
* @warning Both @a a1 and @a a2 are assumed to be arrays, no checks are performed
* @param[in,out] a1 LHS operand
* @param a2 RHS operand
*
* Equivalent to <tt>$a1 = array_merge_recursive($a1, $a2)</tt> in PHP with the only exception
* that Phalcon's version preserves numeric keys
*/
void zephir_array_merge_recursive_n(zval **a1, zval *a2 TSRMLS_DC)
{
HashPosition hp;
zval **value, key, *tmp1, *tmp2;
assert(Z_TYPE_PP(a1) == IS_ARRAY);
assert(Z_TYPE_P(a2) == IS_ARRAY);
for (
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(a2), &hp);
zend_hash_get_current_data_ex(Z_ARRVAL_P(a2), (void**) &value, &hp) == SUCCESS;
zend_hash_move_forward_ex(Z_ARRVAL_P(a2), &hp)
) {
key = zephir_get_current_key_w(Z_ARRVAL_P(a2), &hp);
if (!zephir_array_isset(*a1, &key) || Z_TYPE_PP(value) != IS_ARRAY) {
zephir_array_update_zval(a1, &key, value, PH_COPY | PH_SEPARATE);
} else {
zephir_array_fetch(&tmp1, *a1, &key, PH_NOISY ZEPHIR_DEBUG_PARAMS_DUMMY TSRMLS_CC);
zephir_array_fetch(&tmp2, a2, &key, PH_NOISY ZEPHIR_DEBUG_PARAMS_DUMMY TSRMLS_CC);
zephir_array_merge_recursive_n(&tmp1, tmp2 TSRMLS_CC);
zval_ptr_dtor(&tmp1);
zval_ptr_dtor(&tmp2);
}
}
}
/**
* @brief array_unshift($arr, $arg)
* @param arr
* @param arg
* @note Reference count of @c arg will be incremented
*/
void zephir_array_unshift(zval *arr, zval *arg TSRMLS_DC)
{
if (likely(Z_TYPE_P(arr) == IS_ARRAY)) {
zval** args[1] = { &arg };
#if PHP_VERSION_ID < 50600
HashTable oldhash;
HashTable *newhash = Z_ARRVAL_P(arr);
newhash = php_splice(newhash, 0, 0, args, 1, NULL);
oldhash = *Z_ARRVAL_P(arr);
if (Z_ARRVAL_P(arr) == &EG(symbol_table)) {
zend_reset_all_cv(&EG(symbol_table) TSRMLS_CC);
}
*Z_ARRVAL_P(arr) = *newhash;
FREE_HASHTABLE(newhash);
zend_hash_destroy(&oldhash);
#else
php_splice(Z_ARRVAL_P(arr), 0, 0, args, 1, NULL TSRMLS_CC);
#endif
}
}
void zephir_array_keys(zval *return_value, zval *input TSRMLS_DC)
{
zval *new_val, **entry;
char *string_key;
uint string_key_len;
ulong num_key;
HashPosition pos;
if (likely(Z_TYPE_P(input) == IS_ARRAY)) {
array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
/* Go through input array and add keys to the return array */
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS) {
MAKE_STD_ZVAL(new_val);
switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 1, &pos)) {
case HASH_KEY_IS_STRING:
ZVAL_STRINGL(new_val, string_key, string_key_len - 1, 0);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL);
break;
case HASH_KEY_IS_LONG:
Z_TYPE_P(new_val) = IS_LONG;
Z_LVAL_P(new_val) = num_key;
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL);
break;
}
zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
}
}
}
void zephir_array_values(zval *return_value, zval *arr)
{
if (likely(Z_TYPE_P(arr) == IS_ARRAY)) {
zval **entry;
HashPosition pos;
array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(arr)));
for (
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(arr), &pos);
zend_hash_get_current_data_ex(Z_ARRVAL_P(arr), (void **)&entry, &pos) == SUCCESS;
zend_hash_move_forward_ex(Z_ARRVAL_P(arr), &pos)
) {
Z_ADDREF_PP(entry);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), entry, sizeof(zval*), NULL);
}
}
}
int zephir_array_key_exists(zval *arr, zval *key TSRMLS_DC)
{
HashTable *h = HASH_OF(arr);
if (h) {
switch (Z_TYPE_P(key)) {
case IS_STRING:
return zend_symtable_exists(h, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1);
case IS_LONG:
return zend_hash_index_exists(h, Z_LVAL_P(key));
case IS_NULL:
return zend_hash_exists(h, "", 1);
default:
zend_error(E_WARNING, "The key should be either a string or an integer");
return 0;
}
}
return 0;
}
int zephir_array_is_associative(zval *arr) {
if (likely(Z_TYPE_P(arr) == IS_ARRAY)) {
HashPosition pos;
zval **entry;
char *skey;
uint skey_len;
ulong nkey;
ulong expected = 0;
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(arr), &pos);
while (zend_hash_get_current_data_ex(Z_ARRVAL_P(arr), (void**) &entry, &pos) == SUCCESS) {
if (HASH_KEY_IS_LONG == zend_hash_get_current_key_ex(Z_ARRVAL_P(arr), &skey, &skey_len, &nkey, 1, &pos)) {
if (expected != nkey) {
return 1;
}
} else {
return 1;
}
++expected;
zend_hash_move_forward_ex(Z_ARRVAL_P(arr), &pos);
}
}
return 0;
}
/**
* Implementation of Multiple array-offset update
*/
void zephir_array_update_multi_ex(zval **arr, zval **value, const char *types, int types_length, int types_count, va_list ap TSRMLS_DC)
{
long old_l[ZEPHIR_MAX_ARRAY_LEVELS], old_ll[ZEPHIR_MAX_ARRAY_LEVELS];
char *s, *old_s[ZEPHIR_MAX_ARRAY_LEVELS], old_type[ZEPHIR_MAX_ARRAY_LEVELS];
zval *fetched, *tmp, *p, *item, *old_item[ZEPHIR_MAX_ARRAY_LEVELS], *old_p[ZEPHIR_MAX_ARRAY_LEVELS];
int i, j, l, ll, re_update, must_continue, wrap_tmp;
assert(types_length < ZEPHIR_MAX_ARRAY_LEVELS);
p = *arr;
for (i = 0; i < types_length; ++i) {
re_update = 0;
must_continue = 0;
wrap_tmp = 0;
old_p[i] = p;
switch (types[i]) {
case 's':
s = va_arg(ap, char*);
l = va_arg(ap, int);
old_s[i] = s;
old_l[i] = l;
if (zephir_array_isset_string_fetch(&fetched, p, s, l + 1, 0 TSRMLS_CC)) {
if (Z_TYPE_P(fetched) == IS_ARRAY) {
if (i == (types_length - 1)) {
re_update = Z_REFCOUNT_P(p) > 1 && !Z_ISREF_P(p);
zephir_array_update_string(&p, s, l, value, PH_COPY | PH_SEPARATE);
} else {
p = fetched;
}
must_continue = 1;
}
} else {
Z_DELREF_P(fetched);
}
if (!must_continue) {
re_update = Z_REFCOUNT_P(p) > 1 && !Z_ISREF_P(p);
if (i == (types_length - 1)) {
zephir_array_update_string(&p, s, l, value, PH_COPY | PH_SEPARATE);
} else {
MAKE_STD_ZVAL(tmp);
array_init(tmp);
zephir_array_update_string(&p, s, l, &tmp, PH_SEPARATE);
if (re_update) {
wrap_tmp = 1;
} else {
p = tmp;
}
}
}
break;
case 'l':
ll = va_arg(ap, long);
old_ll[i] = ll;
if (zephir_array_isset_long_fetch(&fetched, p, ll, 0 TSRMLS_CC)) {
if (Z_TYPE_P(fetched) == IS_ARRAY) {
if (i == (types_length - 1)) {
re_update = Z_REFCOUNT_P(p) > 1 && !Z_ISREF_P(p);
zephir_array_update_long(&p, ll, value, PH_COPY | PH_SEPARATE ZEPHIR_DEBUG_PARAMS_DUMMY);
} else {
p = fetched;
}
must_continue = 1;
}
} else {
Z_DELREF_P(fetched);
}
if (!must_continue) {
re_update = Z_REFCOUNT_P(p) > 1 && !Z_ISREF_P(p);
if (i == (types_length - 1)) {
zephir_array_update_long(&p, ll, value, PH_COPY | PH_SEPARATE ZEPHIR_DEBUG_PARAMS_DUMMY);
} else {
MAKE_STD_ZVAL(tmp);
array_init(tmp);
zephir_array_update_long(&p, ll, &tmp, PH_SEPARATE ZEPHIR_DEBUG_PARAMS_DUMMY);
if (re_update) {
wrap_tmp = 1;
} else {
p = tmp;
}
}
}
break;
case 'z':
item = va_arg(ap, zval*);
old_item[i] = item;
if (zephir_array_isset_fetch(&fetched, p, item, 0 TSRMLS_CC)) {
if (Z_TYPE_P(fetched) == IS_ARRAY) {
if (i == (types_length - 1)) {
re_update = Z_REFCOUNT_P(p) > 1 && !Z_ISREF_P(p);
zephir_array_update_zval(&p, item, value, PH_COPY | PH_SEPARATE);
} else {
p = fetched;
}
must_continue = 1;
}
} else {
Z_DELREF_P(fetched);
}
if (!must_continue) {
re_update = Z_REFCOUNT_P(p) > 1 && !Z_ISREF_P(p);
if (i == (types_length - 1)) {
zephir_array_update_zval(&p, item, value, PH_COPY | PH_SEPARATE);
} else {
MAKE_STD_ZVAL(tmp);
array_init(tmp);
zephir_array_update_zval(&p, item, &tmp, PH_SEPARATE);
if (re_update) {
wrap_tmp = 1;
} else {
p = tmp;
}
}
}
break;
case 'a':
re_update = Z_REFCOUNT_P(p) > 1 && !Z_ISREF_P(p);
zephir_array_append(&p, *value, PH_SEPARATE ZEPHIR_DEBUG_PARAMS_DUMMY);
break;
}
if (re_update) {
for (j = i - 1; j >= 0; j--) {
if (!re_update) {
break;
}
re_update = Z_REFCOUNT_P(old_p[j]) > 1 && !Z_ISREF_P(old_p[j]);
switch (old_type[j]) {
case 's':
if (j == i - 1) {
zephir_array_update_string(&(old_p[j]), old_s[j], old_l[j], &p, PH_SEPARATE);
} else {
zephir_array_update_string(&(old_p[j]), old_s[j], old_l[j], &old_p[j+1], PH_SEPARATE);
}
if (wrap_tmp) {
p = tmp;
wrap_tmp = 0;
}
break;
case 'l':
if (j == i - 1) {
zephir_array_update_long(&(old_p[j]), old_ll[j], &p, PH_SEPARATE ZEPHIR_DEBUG_PARAMS_DUMMY);
} else {
zephir_array_update_long(&(old_p[j]), old_ll[j], &old_p[j+1], PH_SEPARATE ZEPHIR_DEBUG_PARAMS_DUMMY);
}
if (wrap_tmp) {
p = tmp;
wrap_tmp = 0;
}
break;
case 'z':
if (j == i - 1) {
zephir_array_update_zval(&(old_p[j]), old_item[j], &p, PH_SEPARATE);
} else {
zephir_array_update_zval(&(old_p[j]), old_item[j], &old_p[j+1], PH_SEPARATE);
}
if (wrap_tmp) {
p = tmp;
wrap_tmp = 0;
}
break;
}
}
}
if (i != (types_length - 1)) {
old_type[i] = types[i];
}
}
}
int zephir_array_update_multi(zval **arr, zval **value TSRMLS_DC, const char *types, int types_length, int types_count, ...)
{
va_list ap;
va_start(ap, types_count);
SEPARATE_ZVAL_IF_NOT_REF(arr);
/*
memset(old_type, '\0', ZEPHIR_MAX_ARRAY_LEVELS);
memset(old_s, '\0', ZEPHIR_MAX_ARRAY_LEVELS);
memset(old_p, '\0', ZEPHIR_MAX_ARRAY_LEVELS);
memset(old_item, '\0', ZEPHIR_MAX_ARRAY_LEVELS);
*/
zephir_array_update_multi_ex(arr, value, types, types_length, types_count, ap TSRMLS_CC);
va_end(ap);
return 0;
}
void ZEPHIR_FASTCALL zephir_create_array(zval *return_value, uint size, int initialize TSRMLS_DC) {
uint i;
zval *null_value;
HashTable *hashTable;
if (size > 0) {
hashTable = (HashTable *) emalloc(sizeof(HashTable));
zephir_hash_init(hashTable, size, NULL, ZVAL_PTR_DTOR, 0);
if (initialize) {
MAKE_STD_ZVAL(null_value);
ZVAL_NULL(null_value);
Z_SET_REFCOUNT_P(null_value, size);
for (i = 0; i < size; i++) {
zend_hash_next_index_insert(hashTable, &null_value, sizeof(zval *), NULL);
}
}
Z_ARRVAL_P(return_value) = hashTable;
Z_TYPE_P(return_value) = IS_ARRAY;
} else {
array_init(return_value);
}
}
|