#ifndef DS_COMMON_H #define DS_COMMON_H #include "php.h" #include "zend_exceptions.h" #include "ext/standard/php_var.h" #include "ext/spl/spl_exceptions.h" #include "zend_smart_str.h" #include "../php_ds.h" /** * Used for consistent naming when working with callables. */ #define FCI_PARAMS zend_fcall_info fci, zend_fcall_info_cache fci_cache #define FCI_ARGS fci, fci_cache /** * Used for consistent naming when working with variadics. */ #define VA_PARAMS zend_long argc, zval *argv #define VA_ARGS argc, argv /** * Default namespace. */ #define PHP_DS_NS_NAME "Ds\\" #define PHP_DS_NS(cls) PHP_DS_NS_NAME #cls /** * This is useful for functions that require the string and its length. */ #define STR_AND_LEN(str) str, sizeof(str) - 1 /** * Slightly faster check when the given zval is probably a boolean. */ #define EXPECTED_BOOL_IS_TRUE(z) (Z_TYPE_P(z) != IS_FALSE && zend_is_true(z)) /** * Combined class, name, and arginfo method entry. */ #define PHP_DS_ME(cls, name) \ PHP_ME(cls, name, arginfo_##cls##_##name, ZEND_ACC_PUBLIC) /** * */ #define DTOR_AND_UNDEF(z) \ do { \ zval *_z = z; \ if (_z && ! Z_ISUNDEF_P(_z)) { \ zval_ptr_dtor(_z); \ ZVAL_UNDEF(_z); \ } \ } while (0) /** * */ #define SET_AS_RETURN_AND_UNDEF(z) \ do { \ zval *_z = z; \ if (return_value) { \ ZVAL_COPY_VALUE(return_value, _z); \ ZVAL_UNDEF(_z); \ } else { \ if ( ! Z_ISUNDEF_P(_z)) { \ zval_ptr_dtor(_z); \ ZVAL_UNDEF(_z); \ } \ } \ } while (0) /** * Swaps two zval's. */ #define SWAP_ZVAL(a, b) \ do { \ zval _t = a; \ a = b; \ b = _t; \ } while (0) /** * Adds the given zval "val" to "sum". */ #if PHP_MAJOR_VERSION < 8 || PHP_MAJOR_VERSION == 8 && PHP_MINOR_VERSION < 3 #define DS_ADD_TO_SUM(val, sum) \ do { \ if (Z_TYPE_P(val) == IS_LONG || Z_TYPE_P(val) == IS_DOUBLE) { \ fast_add_function(sum, sum, val); \ } else { \ zval _num; \ ZVAL_COPY(&_num, val); \ convert_scalar_to_number(&_num); \ fast_add_function(sum, sum, &_num); \ } \ } while (0) #else #define DS_ADD_TO_SUM(val, sum) \ do { \ if (Z_TYPE_P(val) == IS_LONG || Z_TYPE_P(val) == IS_DOUBLE) { \ add_function(sum, sum, val); \ } else { \ zval _num; \ ZVAL_COPY(&_num, val); \ convert_scalar_to_number(&_num); \ add_function(sum, sum, &_num); \ } \ } while (0) #endif /** * Used to replace a buffer with a new one. */ #define FREE_AND_REPLACE(ptr, repl) \ do { \ void *_repl = repl; \ efree(ptr); \ ptr = _repl; \ } while (0) /** * Copies 'len' values from 'src' into 'dst'. */ #define COPY_ZVAL_BUFFER(dst, src, len) \ do { \ zval *_src = src; \ zval *_dst = dst; \ zval *_end = src + len; \ for (; _src != _end; ++_src, ++_dst) { \ ZVAL_COPY(_dst, _src); \ } \ } while (0) /** * Used to determine if a string zval is equal to a string literal. * Eg. ZVAL_EQUALS_STRING(value, "test") */ #define ZVAL_EQUALS_STRING(z, s) zend_string_equals_literal(Z_STR_P(z), s) /** * Copies a zval into the return_value. */ #define RETURN_ZVAL_COPY(z) \ do { \ zval *_z = z; \ if (_z) { \ ZVAL_COPY(return_value, _z); \ } \ return; \ } while (0) #define SERIALIZE_SET_ZSTR(s) \ *buffer = (unsigned char *) estrndup(ZSTR_VAL((s)), ZSTR_LEN((s))); \ *length = ZSTR_LEN((s)); #define PHP_DS_SERIALIZE_FUNCIONS(name) \ int name##_serialize( \ zval *object, \ unsigned char **buffer, \ size_t *length, \ zend_serialize_data *data \ ); \ int name##_unserialize( \ zval *object, \ zend_class_entry *ce, \ const unsigned char *buffer, \ size_t length, \ zend_unserialize_data *data \ ) /** EXCEPTIONS **************************************************************/ #define ARRAY_ACCESS_BY_KEY_NOT_SUPPORTED() ds_throw_exception( \ zend_ce_error, \ "Array access by key is not supported") #define INDEX_OUT_OF_RANGE(index, max) ds_throw_exception( \ spl_ce_OutOfRangeException, \ max == 0 \ ? "Index out of range: %d" \ : "Index out of range: %d, expected 0 <= x <= %d", \ index, \ max - 1) #define OFFSET_OUT_OF_BOUNDS() ds_throw_exception( \ spl_ce_OutOfBoundsException, \ "Offset out of bounds") #define ARRAY_ACCESS_PUSH_NOT_SUPPORTED() ds_throw_exception( \ spl_ce_OutOfBoundsException, \ "Array access push syntax is not supported") #define KEY_NOT_FOUND() ds_throw_exception( \ spl_ce_OutOfBoundsException, \ "Key not found") #define OBJ_HASH_MUST_BE_SCALAR(z) ds_throw_exception( \ spl_ce_UnexpectedValueException, \ "Object hash must be scalar, %s given", zend_get_type_by_const(Z_TYPE_P(z))) #define VALUE_MUST_BE_INTEGER(z) ds_throw_exception( \ spl_ce_UnexpectedValueException, \ "Value must be of type integer, %d given", zend_get_type_by_const(Z_TYPE_P(z))) #define NOT_ALLOWED_WHEN_EMPTY() ds_throw_exception( \ spl_ce_UnderflowException, \ "Unexpected empty state") #define ARRAY_OR_TRAVERSABLE_REQUIRED() ds_throw_exception( \ spl_ce_InvalidArgumentException, \ "Value must be an array or traversable object") #define INTEGER_INDEX_REQUIRED(z) ds_throw_exception( \ zend_ce_type_error, \ "Index must be of type integer, %s given", zend_get_type_by_const(Z_TYPE_P(z))) #define INTEGER_LENGTH_REQUIRED(z) ds_throw_exception( \ zend_ce_type_error, \ "Length must be of type integer, %s given", zend_get_type_by_const(Z_TYPE_P(z))) #define ITERATION_BY_REF_NOT_SUPPORTED() ds_throw_exception( \ zend_ce_error, \ "Iterating by reference is not supported") #define ACCESS_BY_REF_NOT_ALLOWED() ds_throw_exception( \ zend_ce_error, \ "Access by reference is not allowed") #define UNSERIALIZE_ERROR() ds_throw_exception( \ zend_ce_error, \ "Failed to unserialize data") #define RECONSTRUCTION_NOT_ALLOWED() ds_throw_exception( \ zend_ce_error, \ "Immutable objects may not be reconstructed") #define MUTABILITY_NOT_ALLOWED() ds_throw_exception( \ zend_ce_error, \ "Immutable objects may not be changed") // https://bugs.php.net/bug.php?id=80816 #if PHP_VERSION_ID >= 80100 #define spl_ce_Aggregate zend_ce_aggregate #define spl_ce_ArrayAccess zend_ce_arrayaccess #define spl_ce_Countable zend_ce_countable #define spl_ce_Iterator zend_ce_iterator #define spl_ce_Serializable zend_ce_serializable #define spl_ce_Stringable zend_ce_stringable #define spl_ce_Traversable zend_ce_traversable #endif /** * */ void ds_throw_exception(zend_class_entry *ce, const char *format, ...); /*****************************************************************************/ /** * Returns the next power of 2 greater than or equal to n. */ uint32_t ds_next_power_of_2(uint32_t n, uint32_t min); /** * Similar to 'implode', joins a zval buffer using an optional 'glue'. * Use NULL and 0 for 'str' and 'len' to indicate an optional glue. */ zend_string *ds_join_zval_buffer( zval *buffer, zend_long size, char *str, size_t len ); /** * Normalizes input parameters for slicing so that the implementation can focus * on the actual slicing. Takes care of negative values, length > size etc. */ void ds_normalize_slice_args( zend_long *offset, zend_long *length, zend_long size ); /** * Allocates a zval buffer of a specified length. */ zval *ds_allocate_zval_buffer(zend_long length); /** * Reallocates a zval buffer to a specified length. * * @param buffer * @param length The resulting length of the buffer. * @param used Number of slots currently in use in the given buffer. */ zval *ds_reallocate_zval_buffer(zval *buffer, zend_long length, zend_long current, zend_long used); /** * Sorts a zval buffer in place using the default internal compare_func. */ void ds_sort_zval_buffer(zval *buffer, zend_long size); /** * Sorts a zval buffer in place using a user-provided, global compare function. */ void ds_user_sort_zval_buffer(zval *buffer, zend_long size); /** * Reverses zvals between two ranges, usually a range within a buffer. */ void ds_reverse_zval_range(zval *x, zval *y); /** * Determines if a zval is set, ie. 'isset' and 'empty'. */ int ds_zval_isset(zval *value, int check_empty); /** * Determines if a zval is an array. */ bool ds_is_array(zval *value); /** * Determines if an array uses keys, similar to how json_encode does it. */ bool ds_php_array_uses_keys(HashTable *ht); /** * Determines if a zval is an object and implements Traversable. */ bool ds_is_traversable(zval *value); /** * */ void smart_str_appendz(smart_str *buffer, zval *value); /** * */ bool ds_special_is_equal(zval *op1, zval *op2); #endif