xref: /ext-ds/src/common.h (revision 468a68cf)
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