1 #ifndef DS_COMMON_H 2 #define DS_COMMON_H 3 4 #include "php.h" 5 #include "zend_exceptions.h" 6 #include "ext/standard/php_var.h" 7 #include "ext/spl/spl_exceptions.h" 8 #include "zend_smart_str.h" 9 #include "../php_ds.h" 10 11 /** 12 * Used for consistent naming when working with callables. 13 */ 14 #define FCI_PARAMS zend_fcall_info fci, zend_fcall_info_cache fci_cache 15 #define FCI_ARGS fci, fci_cache 16 17 /** 18 * Used for consistent naming when working with variadics. 19 */ 20 #define VA_PARAMS zend_long argc, zval *argv 21 #define VA_ARGS argc, argv 22 23 /** 24 * Default namespace. 25 */ 26 #define PHP_DS_NS_NAME "Ds\\" 27 #define PHP_DS_NS(cls) PHP_DS_NS_NAME #cls 28 29 /** 30 * This is useful for functions that require the string and its length. 31 */ 32 #define STR_AND_LEN(str) str, sizeof(str) - 1 33 34 /** 35 * Slightly faster check when the given zval is probably a boolean. 36 */ 37 #define EXPECTED_BOOL_IS_TRUE(z) (Z_TYPE_P(z) != IS_FALSE && zend_is_true(z)) 38 39 /** 40 * Combined class, name, and arginfo method entry. 41 */ 42 #define PHP_DS_ME(cls, name) \ 43 PHP_ME(cls, name, arginfo_##cls##_##name, ZEND_ACC_PUBLIC) 44 45 /** 46 * 47 */ 48 #define DTOR_AND_UNDEF(z) \ 49 do { \ 50 zval *_z = z; \ 51 if (_z && ! Z_ISUNDEF_P(_z)) { \ 52 zval_ptr_dtor(_z); \ 53 ZVAL_UNDEF(_z); \ 54 } \ 55 } while (0) 56 57 /** 58 * 59 */ 60 #define SET_AS_RETURN_AND_UNDEF(z) \ 61 do { \ 62 zval *_z = z; \ 63 if (return_value) { \ 64 ZVAL_COPY_VALUE(return_value, _z); \ 65 ZVAL_UNDEF(_z); \ 66 } else { \ 67 if ( ! Z_ISUNDEF_P(_z)) { \ 68 zval_ptr_dtor(_z); \ 69 ZVAL_UNDEF(_z); \ 70 } \ 71 } \ 72 } while (0) 73 74 /** 75 * Swaps two zval's. 76 */ 77 #define SWAP_ZVAL(a, b) \ 78 do { \ 79 zval _t = a; \ 80 a = b; \ 81 b = _t; \ 82 } while (0) 83 84 /** 85 * Adds the given zval "val" to "sum". 86 */ 87 #if PHP_MAJOR_VERSION < 8 || PHP_MAJOR_VERSION == 8 && PHP_MINOR_VERSION < 3 88 #define DS_ADD_TO_SUM(val, sum) \ 89 do { \ 90 if (Z_TYPE_P(val) == IS_LONG || Z_TYPE_P(val) == IS_DOUBLE) { \ 91 fast_add_function(sum, sum, val); \ 92 } else { \ 93 zval _num; \ 94 ZVAL_COPY(&_num, val); \ 95 convert_scalar_to_number(&_num); \ 96 fast_add_function(sum, sum, &_num); \ 97 } \ 98 } while (0) 99 #else 100 #define DS_ADD_TO_SUM(val, sum) \ 101 do { \ 102 if (Z_TYPE_P(val) == IS_LONG || Z_TYPE_P(val) == IS_DOUBLE) { \ 103 add_function(sum, sum, val); \ 104 } else { \ 105 zval _num; \ 106 ZVAL_COPY(&_num, val); \ 107 convert_scalar_to_number(&_num); \ 108 add_function(sum, sum, &_num); \ 109 } \ 110 } while (0) 111 #endif 112 113 /** 114 * Used to replace a buffer with a new one. 115 */ 116 #define FREE_AND_REPLACE(ptr, repl) \ 117 do { \ 118 void *_repl = repl; \ 119 efree(ptr); \ 120 ptr = _repl; \ 121 } while (0) 122 123 /** 124 * Copies 'len' values from 'src' into 'dst'. 125 */ 126 #define COPY_ZVAL_BUFFER(dst, src, len) \ 127 do { \ 128 zval *_src = src; \ 129 zval *_dst = dst; \ 130 zval *_end = src + len; \ 131 for (; _src != _end; ++_src, ++_dst) { \ 132 ZVAL_COPY(_dst, _src); \ 133 } \ 134 } while (0) 135 136 /** 137 * Used to determine if a string zval is equal to a string literal. 138 * Eg. ZVAL_EQUALS_STRING(value, "test") 139 */ 140 #define ZVAL_EQUALS_STRING(z, s) zend_string_equals_literal(Z_STR_P(z), s) 141 142 /** 143 * Copies a zval into the return_value. 144 */ 145 #define RETURN_ZVAL_COPY(z) \ 146 do { \ 147 zval *_z = z; \ 148 if (_z) { \ 149 ZVAL_COPY(return_value, _z); \ 150 } \ 151 return; \ 152 } while (0) 153 154 #define SERIALIZE_SET_ZSTR(s) \ 155 *buffer = (unsigned char *) estrndup(ZSTR_VAL((s)), ZSTR_LEN((s))); \ 156 *length = ZSTR_LEN((s)); 157 158 #define PHP_DS_SERIALIZE_FUNCIONS(name) \ 159 int name##_serialize( \ 160 zval *object, \ 161 unsigned char **buffer, \ 162 size_t *length, \ 163 zend_serialize_data *data \ 164 ); \ 165 int name##_unserialize( \ 166 zval *object, \ 167 zend_class_entry *ce, \ 168 const unsigned char *buffer, \ 169 size_t length, \ 170 zend_unserialize_data *data \ 171 ) 172 173 /** EXCEPTIONS **************************************************************/ 174 175 #define ARRAY_ACCESS_BY_KEY_NOT_SUPPORTED() ds_throw_exception( \ 176 zend_ce_error, \ 177 "Array access by key is not supported") 178 179 #define INDEX_OUT_OF_RANGE(index, max) ds_throw_exception( \ 180 spl_ce_OutOfRangeException, \ 181 max == 0 \ 182 ? "Index out of range: %d" \ 183 : "Index out of range: %d, expected 0 <= x <= %d", \ 184 index, \ 185 max - 1) 186 187 #define OFFSET_OUT_OF_BOUNDS() ds_throw_exception( \ 188 spl_ce_OutOfBoundsException, \ 189 "Offset out of bounds") 190 191 #define ARRAY_ACCESS_PUSH_NOT_SUPPORTED() ds_throw_exception( \ 192 spl_ce_OutOfBoundsException, \ 193 "Array access push syntax is not supported") 194 195 #define KEY_NOT_FOUND() ds_throw_exception( \ 196 spl_ce_OutOfBoundsException, \ 197 "Key not found") 198 199 #define OBJ_HASH_MUST_BE_SCALAR(z) ds_throw_exception( \ 200 spl_ce_UnexpectedValueException, \ 201 "Object hash must be scalar, %s given", zend_get_type_by_const(Z_TYPE_P(z))) 202 203 #define VALUE_MUST_BE_INTEGER(z) ds_throw_exception( \ 204 spl_ce_UnexpectedValueException, \ 205 "Value must be of type integer, %d given", zend_get_type_by_const(Z_TYPE_P(z))) 206 207 #define NOT_ALLOWED_WHEN_EMPTY() ds_throw_exception( \ 208 spl_ce_UnderflowException, \ 209 "Unexpected empty state") 210 211 #define ARRAY_OR_TRAVERSABLE_REQUIRED() ds_throw_exception( \ 212 spl_ce_InvalidArgumentException, \ 213 "Value must be an array or traversable object") 214 215 #define INTEGER_INDEX_REQUIRED(z) ds_throw_exception( \ 216 zend_ce_type_error, \ 217 "Index must be of type integer, %s given", zend_get_type_by_const(Z_TYPE_P(z))) 218 219 #define INTEGER_LENGTH_REQUIRED(z) ds_throw_exception( \ 220 zend_ce_type_error, \ 221 "Length must be of type integer, %s given", zend_get_type_by_const(Z_TYPE_P(z))) 222 223 #define ITERATION_BY_REF_NOT_SUPPORTED() ds_throw_exception( \ 224 zend_ce_error, \ 225 "Iterating by reference is not supported") 226 227 #define ACCESS_BY_REF_NOT_ALLOWED() ds_throw_exception( \ 228 zend_ce_error, \ 229 "Access by reference is not allowed") 230 231 #define UNSERIALIZE_ERROR() ds_throw_exception( \ 232 zend_ce_error, \ 233 "Failed to unserialize data") 234 235 #define RECONSTRUCTION_NOT_ALLOWED() ds_throw_exception( \ 236 zend_ce_error, \ 237 "Immutable objects may not be reconstructed") 238 239 #define MUTABILITY_NOT_ALLOWED() ds_throw_exception( \ 240 zend_ce_error, \ 241 "Immutable objects may not be changed") 242 243 // https://bugs.php.net/bug.php?id=80816 244 #if PHP_VERSION_ID >= 80100 245 #define spl_ce_Aggregate zend_ce_aggregate 246 #define spl_ce_ArrayAccess zend_ce_arrayaccess 247 #define spl_ce_Countable zend_ce_countable 248 #define spl_ce_Iterator zend_ce_iterator 249 #define spl_ce_Serializable zend_ce_serializable 250 #define spl_ce_Stringable zend_ce_stringable 251 #define spl_ce_Traversable zend_ce_traversable 252 #endif 253 254 /** 255 * 256 */ 257 void ds_throw_exception(zend_class_entry *ce, const char *format, ...); 258 259 /*****************************************************************************/ 260 261 /** 262 * Returns the next power of 2 greater than or equal to n. 263 */ 264 uint32_t ds_next_power_of_2(uint32_t n, uint32_t min); 265 266 /** 267 * Similar to 'implode', joins a zval buffer using an optional 'glue'. 268 * Use NULL and 0 for 'str' and 'len' to indicate an optional glue. 269 */ 270 zend_string *ds_join_zval_buffer( 271 zval *buffer, 272 zend_long size, 273 char *str, 274 size_t len 275 ); 276 277 278 /** 279 * Normalizes input parameters for slicing so that the implementation can focus 280 * on the actual slicing. Takes care of negative values, length > size etc. 281 */ 282 void ds_normalize_slice_args( 283 zend_long *offset, 284 zend_long *length, 285 zend_long size 286 ); 287 288 /** 289 * Allocates a zval buffer of a specified length. 290 */ 291 zval *ds_allocate_zval_buffer(zend_long length); 292 293 /** 294 * Reallocates a zval buffer to a specified length. 295 * 296 * @param buffer 297 * @param length The resulting length of the buffer. 298 * @param used Number of slots currently in use in the given buffer. 299 */ 300 zval *ds_reallocate_zval_buffer(zval *buffer, zend_long length, zend_long current, zend_long used); 301 302 /** 303 * Sorts a zval buffer in place using the default internal compare_func. 304 */ 305 void ds_sort_zval_buffer(zval *buffer, zend_long size); 306 307 /** 308 * Sorts a zval buffer in place using a user-provided, global compare function. 309 */ 310 void ds_user_sort_zval_buffer(zval *buffer, zend_long size); 311 312 /** 313 * Reverses zvals between two ranges, usually a range within a buffer. 314 */ 315 void ds_reverse_zval_range(zval *x, zval *y); 316 317 /** 318 * Determines if a zval is set, ie. 'isset' and 'empty'. 319 */ 320 int ds_zval_isset(zval *value, int check_empty); 321 322 /** 323 * Determines if a zval is an array. 324 */ 325 bool ds_is_array(zval *value); 326 327 /** 328 * Determines if an array uses keys, similar to how json_encode does it. 329 */ 330 bool ds_php_array_uses_keys(HashTable *ht); 331 332 /** 333 * Determines if a zval is an object and implements Traversable. 334 */ 335 bool ds_is_traversable(zval *value); 336 337 /** 338 * 339 */ 340 void smart_str_appendz(smart_str *buffer, zval *value); 341 342 /** 343 * 344 */ 345 bool ds_special_is_equal(zval *op1, zval *op2); 346 347 #endif 348