/src/php-src/ext/random/randomizer.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Copyright (c) The PHP Group | |
4 | | +----------------------------------------------------------------------+ |
5 | | | This source file is subject to version 3.01 of the PHP license, | |
6 | | | that is bundled with this package in the file LICENSE, and is | |
7 | | | available through the world-wide-web at the following url: | |
8 | | | https://www.php.net/license/3_01.txt | |
9 | | | If you did not receive a copy of the PHP license and are unable to | |
10 | | | obtain it through the world-wide-web, please send a note to | |
11 | | | license@php.net so we can mail you a copy immediately. | |
12 | | +----------------------------------------------------------------------+ |
13 | | | Author: Go Kudo <zeriyoshi@php.net> | |
14 | | +----------------------------------------------------------------------+ |
15 | | */ |
16 | | |
17 | | #ifdef HAVE_CONFIG_H |
18 | | # include "config.h" |
19 | | #endif |
20 | | |
21 | | #include "php.h" |
22 | | #include "php_random.h" |
23 | | |
24 | | #include "ext/standard/php_array.h" |
25 | | #include "ext/standard/php_string.h" |
26 | | |
27 | | #include "Zend/zend_enum.h" |
28 | | #include "Zend/zend_exceptions.h" |
29 | | #include "zend_portability.h" |
30 | | |
31 | 5 | static inline void randomizer_common_init(php_random_randomizer *randomizer, zend_object *engine_object) { |
32 | 5 | if (engine_object->ce->type == ZEND_INTERNAL_CLASS) { |
33 | | /* Internal classes always php_random_engine struct */ |
34 | 5 | php_random_engine *engine = php_random_engine_from_obj(engine_object); |
35 | | |
36 | | /* Copy engine pointers */ |
37 | 5 | randomizer->engine = engine->engine; |
38 | 5 | } else { |
39 | | /* Self allocation */ |
40 | 0 | php_random_status_state_user *state = php_random_status_alloc(&php_random_algo_user, false); |
41 | 0 | randomizer->engine = (php_random_algo_with_state){ |
42 | 0 | .algo = &php_random_algo_user, |
43 | 0 | .state = state, |
44 | 0 | }; |
45 | | |
46 | | /* Create compatible state */ |
47 | 0 | state->object = engine_object; |
48 | 0 | state->generate_method = zend_hash_str_find_ptr(&engine_object->ce->function_table, "generate", strlen("generate")); |
49 | | |
50 | | /* Mark self-allocated for memory management */ |
51 | 0 | randomizer->is_userland_algo = true; |
52 | 0 | } |
53 | 5 | } |
54 | | |
55 | | /* {{{ Random\Randomizer::__construct() */ |
56 | | PHP_METHOD(Random_Randomizer, __construct) |
57 | 5 | { |
58 | 5 | php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); |
59 | 5 | zval engine; |
60 | 5 | zval *param_engine = NULL; |
61 | | |
62 | 15 | ZEND_PARSE_PARAMETERS_START(0, 1) |
63 | 15 | Z_PARAM_OPTIONAL |
64 | 15 | Z_PARAM_OBJECT_OF_CLASS_OR_NULL(param_engine, random_ce_Random_Engine); |
65 | 5 | ZEND_PARSE_PARAMETERS_END(); |
66 | | |
67 | 5 | if (param_engine != NULL) { |
68 | 0 | ZVAL_COPY(&engine, param_engine); |
69 | 5 | } else { |
70 | | /* Create default RNG instance */ |
71 | 5 | object_init_ex(&engine, random_ce_Random_Engine_Secure); |
72 | 5 | } |
73 | | |
74 | 5 | zend_update_property(random_ce_Random_Randomizer, Z_OBJ_P(ZEND_THIS), "engine", strlen("engine"), &engine); |
75 | | |
76 | 5 | OBJ_RELEASE(Z_OBJ_P(&engine)); |
77 | | |
78 | 5 | if (EG(exception)) { |
79 | 0 | RETURN_THROWS(); |
80 | 0 | } |
81 | | |
82 | 5 | randomizer_common_init(randomizer, Z_OBJ_P(&engine)); |
83 | 5 | } |
84 | | /* }}} */ |
85 | | |
86 | | /* {{{ Generate a float in [0, 1) */ |
87 | | PHP_METHOD(Random_Randomizer, nextFloat) |
88 | 0 | { |
89 | 0 | php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); |
90 | 0 | php_random_algo_with_state engine = randomizer->engine; |
91 | |
|
92 | 0 | uint64_t result; |
93 | 0 | size_t total_size; |
94 | |
|
95 | 0 | ZEND_PARSE_PARAMETERS_NONE(); |
96 | | |
97 | 0 | result = 0; |
98 | 0 | total_size = 0; |
99 | 0 | do { |
100 | 0 | php_random_result r = engine.algo->generate(engine.state); |
101 | 0 | result = result | (r.result << (total_size * 8)); |
102 | 0 | total_size += r.size; |
103 | 0 | if (EG(exception)) { |
104 | 0 | RETURN_THROWS(); |
105 | 0 | } |
106 | 0 | } while (total_size < sizeof(uint64_t)); |
107 | | |
108 | | /* A double has 53 bits of precision, thus we must not |
109 | | * use the full 64 bits of the uint64_t, because we would |
110 | | * introduce a bias / rounding error. |
111 | | */ |
112 | | #if DBL_MANT_DIG != 53 |
113 | | # error "Random_Randomizer::nextFloat(): Requires DBL_MANT_DIG == 53 to work." |
114 | | #endif |
115 | 0 | const double step_size = 1.0 / (1ULL << 53); |
116 | | |
117 | | /* Use the upper 53 bits, because some engine's lower bits |
118 | | * are of lower quality. |
119 | | */ |
120 | 0 | result = (result >> 11); |
121 | |
|
122 | 0 | RETURN_DOUBLE(step_size * result); |
123 | 0 | } |
124 | | /* }}} */ |
125 | | |
126 | | /* {{{ Generates a random float within a configurable interval. |
127 | | * |
128 | | * This method uses the γ-section algorithm by Frédéric Goualard. |
129 | | */ |
130 | | PHP_METHOD(Random_Randomizer, getFloat) |
131 | 0 | { |
132 | 0 | php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); |
133 | 0 | double min, max; |
134 | 0 | zend_object *bounds = NULL; |
135 | 0 | int bounds_type = 'C' + sizeof("ClosedOpen") - 1; |
136 | |
|
137 | 0 | ZEND_PARSE_PARAMETERS_START(2, 3) |
138 | 0 | Z_PARAM_DOUBLE(min) |
139 | 0 | Z_PARAM_DOUBLE(max) |
140 | 0 | Z_PARAM_OPTIONAL |
141 | 0 | Z_PARAM_OBJ_OF_CLASS(bounds, random_ce_Random_IntervalBoundary); |
142 | 0 | ZEND_PARSE_PARAMETERS_END(); |
143 | | |
144 | 0 | if (!zend_finite(min)) { |
145 | 0 | zend_argument_value_error(1, "must be finite"); |
146 | 0 | RETURN_THROWS(); |
147 | 0 | } |
148 | | |
149 | 0 | if (!zend_finite(max)) { |
150 | 0 | zend_argument_value_error(2, "must be finite"); |
151 | 0 | RETURN_THROWS(); |
152 | 0 | } |
153 | | |
154 | 0 | if (bounds) { |
155 | 0 | zval *case_name = zend_enum_fetch_case_name(bounds); |
156 | 0 | zend_string *bounds_name = Z_STR_P(case_name); |
157 | |
|
158 | 0 | bounds_type = ZSTR_VAL(bounds_name)[0] + ZSTR_LEN(bounds_name); |
159 | 0 | } |
160 | |
|
161 | 0 | switch (bounds_type) { |
162 | 0 | case 'C' + sizeof("ClosedOpen") - 1: |
163 | 0 | if (UNEXPECTED(max <= min)) { |
164 | 0 | zend_argument_value_error(2, "must be greater than argument #1 ($min)"); |
165 | 0 | RETURN_THROWS(); |
166 | 0 | } |
167 | | |
168 | 0 | RETURN_DOUBLE(php_random_gammasection_closed_open(randomizer->engine, min, max)); |
169 | 0 | case 'C' + sizeof("ClosedClosed") - 1: |
170 | 0 | if (UNEXPECTED(max < min)) { |
171 | 0 | zend_argument_value_error(2, "must be greater than or equal to argument #1 ($min)"); |
172 | 0 | RETURN_THROWS(); |
173 | 0 | } |
174 | | |
175 | 0 | RETURN_DOUBLE(php_random_gammasection_closed_closed(randomizer->engine, min, max)); |
176 | 0 | case 'O' + sizeof("OpenClosed") - 1: |
177 | 0 | if (UNEXPECTED(max <= min)) { |
178 | 0 | zend_argument_value_error(2, "must be greater than argument #1 ($min)"); |
179 | 0 | RETURN_THROWS(); |
180 | 0 | } |
181 | | |
182 | 0 | RETURN_DOUBLE(php_random_gammasection_open_closed(randomizer->engine, min, max)); |
183 | 0 | case 'O' + sizeof("OpenOpen") - 1: |
184 | 0 | if (UNEXPECTED(max <= min)) { |
185 | 0 | zend_argument_value_error(2, "must be greater than argument #1 ($min)"); |
186 | 0 | RETURN_THROWS(); |
187 | 0 | } |
188 | | |
189 | 0 | RETVAL_DOUBLE(php_random_gammasection_open_open(randomizer->engine, min, max)); |
190 | |
|
191 | 0 | if (UNEXPECTED(isnan(Z_DVAL_P(return_value)))) { |
192 | 0 | zend_value_error("The given interval is empty, there are no floats between argument #1 ($min) and argument #2 ($max)"); |
193 | 0 | RETURN_THROWS(); |
194 | 0 | } |
195 | | |
196 | 0 | return; |
197 | 0 | default: |
198 | 0 | ZEND_UNREACHABLE(); |
199 | 0 | } |
200 | 0 | } |
201 | | /* }}} */ |
202 | | |
203 | | /* {{{ Generate positive random number */ |
204 | | PHP_METHOD(Random_Randomizer, nextInt) |
205 | 0 | { |
206 | 0 | php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); |
207 | 0 | php_random_algo_with_state engine = randomizer->engine; |
208 | |
|
209 | 0 | ZEND_PARSE_PARAMETERS_NONE(); |
210 | | |
211 | 0 | php_random_result result = engine.algo->generate(engine.state); |
212 | 0 | if (EG(exception)) { |
213 | 0 | RETURN_THROWS(); |
214 | 0 | } |
215 | 0 | if (result.size > sizeof(zend_long)) { |
216 | 0 | zend_throw_exception(random_ce_Random_RandomException, "Generated value exceeds size of int", 0); |
217 | 0 | RETURN_THROWS(); |
218 | 0 | } |
219 | | |
220 | 0 | RETURN_LONG((zend_long) (result.result >> 1)); |
221 | 0 | } |
222 | | /* }}} */ |
223 | | |
224 | | /* {{{ Generate random number in range */ |
225 | | PHP_METHOD(Random_Randomizer, getInt) |
226 | 0 | { |
227 | 0 | php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); |
228 | 0 | php_random_algo_with_state engine = randomizer->engine; |
229 | |
|
230 | 0 | uint64_t result; |
231 | 0 | zend_long min, max; |
232 | |
|
233 | 0 | ZEND_PARSE_PARAMETERS_START(2, 2) |
234 | 0 | Z_PARAM_LONG(min) |
235 | 0 | Z_PARAM_LONG(max) |
236 | 0 | ZEND_PARSE_PARAMETERS_END(); |
237 | | |
238 | 0 | if (UNEXPECTED(max < min)) { |
239 | 0 | zend_argument_value_error(2, "must be greater than or equal to argument #1 ($min)"); |
240 | 0 | RETURN_THROWS(); |
241 | 0 | } |
242 | | |
243 | 0 | if (UNEXPECTED( |
244 | 0 | engine.algo->range == php_random_algo_mt19937.range |
245 | 0 | && ((php_random_status_state_mt19937 *) engine.state)->mode != MT_RAND_MT19937 |
246 | 0 | )) { |
247 | 0 | uint64_t r = php_random_algo_mt19937.generate(engine.state).result >> 1; |
248 | | |
249 | | /* This is an inlined version of the RAND_RANGE_BADSCALING macro that does not invoke UB when encountering |
250 | | * (max - min) > ZEND_LONG_MAX. |
251 | | */ |
252 | 0 | zend_ulong offset = (double) ( (double) max - min + 1.0) * (r / (PHP_MT_RAND_MAX + 1.0)); |
253 | |
|
254 | 0 | result = (zend_long) (offset + min); |
255 | 0 | } else { |
256 | 0 | result = engine.algo->range(engine.state, min, max); |
257 | 0 | } |
258 | |
|
259 | 0 | if (EG(exception)) { |
260 | 0 | RETURN_THROWS(); |
261 | 0 | } |
262 | | |
263 | 0 | RETURN_LONG((zend_long) result); |
264 | 0 | } |
265 | | /* }}} */ |
266 | | |
267 | | /* {{{ Generate random bytes string in ordered length */ |
268 | | PHP_METHOD(Random_Randomizer, getBytes) |
269 | 0 | { |
270 | 0 | php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); |
271 | 0 | php_random_algo_with_state engine = randomizer->engine; |
272 | |
|
273 | 0 | zend_string *retval; |
274 | 0 | zend_long user_length; |
275 | 0 | size_t total_size = 0; |
276 | |
|
277 | 0 | ZEND_PARSE_PARAMETERS_START(1, 1) |
278 | 0 | Z_PARAM_LONG(user_length) |
279 | 0 | ZEND_PARSE_PARAMETERS_END(); |
280 | | |
281 | 0 | if (user_length < 1) { |
282 | 0 | zend_argument_value_error(1, "must be greater than 0"); |
283 | 0 | RETURN_THROWS(); |
284 | 0 | } |
285 | | |
286 | 0 | size_t length = (size_t)user_length; |
287 | 0 | retval = zend_string_alloc(length, 0); |
288 | |
|
289 | 0 | php_random_result result; |
290 | 0 | while (total_size + 8 <= length) { |
291 | 0 | result = engine.algo->generate(engine.state); |
292 | 0 | if (EG(exception)) { |
293 | 0 | zend_string_efree(retval); |
294 | 0 | RETURN_THROWS(); |
295 | 0 | } |
296 | | |
297 | | /* If the result is not 64 bits, we can't use the fast path and |
298 | | * we don't attempt to use it in the future, because we don't |
299 | | * expect engines to change their output size. |
300 | | * |
301 | | * While it would be possible to always memcpy() the entire output, |
302 | | * using result.size as the length that would result in much worse |
303 | | * assembly, because it will actually emit a call to memcpy() |
304 | | * instead of just storing the 64 bit value at a memory offset. |
305 | | */ |
306 | 0 | if (result.size != 8) { |
307 | 0 | goto non_64; |
308 | 0 | } |
309 | | |
310 | | #ifdef WORDS_BIGENDIAN |
311 | | uint64_t swapped = ZEND_BYTES_SWAP64(result.result); |
312 | | memcpy(ZSTR_VAL(retval) + total_size, &swapped, 8); |
313 | | #else |
314 | 0 | memcpy(ZSTR_VAL(retval) + total_size, &result.result, 8); |
315 | 0 | #endif |
316 | 0 | total_size += 8; |
317 | 0 | } |
318 | | |
319 | 0 | while (total_size < length) { |
320 | 0 | result = engine.algo->generate(engine.state); |
321 | 0 | if (EG(exception)) { |
322 | 0 | zend_string_efree(retval); |
323 | 0 | RETURN_THROWS(); |
324 | 0 | } |
325 | | |
326 | 0 | non_64: |
327 | |
|
328 | 0 | for (size_t i = 0; i < result.size; i++) { |
329 | 0 | ZSTR_VAL(retval)[total_size++] = result.result & 0xff; |
330 | 0 | result.result >>= 8; |
331 | 0 | if (total_size >= length) { |
332 | 0 | break; |
333 | 0 | } |
334 | 0 | } |
335 | 0 | } |
336 | | |
337 | 0 | ZSTR_VAL(retval)[length] = '\0'; |
338 | 0 | RETURN_NEW_STR(retval); |
339 | 0 | } |
340 | | /* }}} */ |
341 | | |
342 | | /* {{{ Shuffling array */ |
343 | | PHP_METHOD(Random_Randomizer, shuffleArray) |
344 | 0 | { |
345 | 0 | php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); |
346 | 0 | zval *array; |
347 | |
|
348 | 0 | ZEND_PARSE_PARAMETERS_START(1, 1) |
349 | 0 | Z_PARAM_ARRAY(array) |
350 | 0 | ZEND_PARSE_PARAMETERS_END(); |
351 | | |
352 | 0 | RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array))); |
353 | 0 | if (!php_array_data_shuffle(randomizer->engine, return_value)) { |
354 | 0 | RETURN_THROWS(); |
355 | 0 | } |
356 | 0 | } |
357 | | /* }}} */ |
358 | | |
359 | | /* {{{ Shuffling binary */ |
360 | | PHP_METHOD(Random_Randomizer, shuffleBytes) |
361 | 0 | { |
362 | 0 | php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); |
363 | 0 | zend_string *bytes; |
364 | |
|
365 | 0 | ZEND_PARSE_PARAMETERS_START(1, 1) |
366 | 0 | Z_PARAM_STR(bytes) |
367 | 0 | ZEND_PARSE_PARAMETERS_END(); |
368 | | |
369 | 0 | if (ZSTR_LEN(bytes) < 2) { |
370 | 0 | RETURN_STR_COPY(bytes); |
371 | 0 | } |
372 | | |
373 | 0 | RETVAL_STRINGL(ZSTR_VAL(bytes), ZSTR_LEN(bytes)); |
374 | 0 | if (!php_binary_string_shuffle(randomizer->engine, Z_STRVAL_P(return_value), (zend_long) Z_STRLEN_P(return_value))) { |
375 | 0 | RETURN_THROWS(); |
376 | 0 | } |
377 | 0 | } |
378 | | /* }}} */ |
379 | | |
380 | | /* {{{ Pick keys */ |
381 | | PHP_METHOD(Random_Randomizer, pickArrayKeys) |
382 | 0 | { |
383 | 0 | php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); |
384 | 0 | zval *input, t; |
385 | 0 | zend_long num_req; |
386 | |
|
387 | 0 | ZEND_PARSE_PARAMETERS_START(2, 2); |
388 | 0 | Z_PARAM_ARRAY(input) |
389 | 0 | Z_PARAM_LONG(num_req) |
390 | 0 | ZEND_PARSE_PARAMETERS_END(); |
391 | | |
392 | 0 | if (!php_array_pick_keys( |
393 | 0 | randomizer->engine, |
394 | 0 | input, |
395 | 0 | num_req, |
396 | 0 | return_value, |
397 | 0 | false) |
398 | 0 | ) { |
399 | 0 | RETURN_THROWS(); |
400 | 0 | } |
401 | | |
402 | | /* Keep compatibility, But the result is always an array */ |
403 | 0 | if (Z_TYPE_P(return_value) != IS_ARRAY) { |
404 | 0 | ZVAL_COPY_VALUE(&t, return_value); |
405 | 0 | array_init(return_value); |
406 | 0 | zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &t); |
407 | 0 | } |
408 | 0 | } |
409 | | /* }}} */ |
410 | | |
411 | | /* {{{ Get Random Bytes for String */ |
412 | | PHP_METHOD(Random_Randomizer, getBytesFromString) |
413 | 0 | { |
414 | 0 | php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); |
415 | 0 | php_random_algo_with_state engine = randomizer->engine; |
416 | |
|
417 | 0 | zend_long user_length; |
418 | 0 | zend_string *source, *retval; |
419 | 0 | size_t total_size = 0; |
420 | |
|
421 | 0 | ZEND_PARSE_PARAMETERS_START(2, 2); |
422 | 0 | Z_PARAM_STR(source) |
423 | 0 | Z_PARAM_LONG(user_length) |
424 | 0 | ZEND_PARSE_PARAMETERS_END(); |
425 | | |
426 | 0 | const size_t source_length = ZSTR_LEN(source); |
427 | 0 | const size_t max_offset = source_length - 1; |
428 | |
|
429 | 0 | if (source_length < 1) { |
430 | 0 | zend_argument_must_not_be_empty_error(1); |
431 | 0 | RETURN_THROWS(); |
432 | 0 | } |
433 | | |
434 | 0 | if (user_length < 1) { |
435 | 0 | zend_argument_value_error(2, "must be greater than 0"); |
436 | 0 | RETURN_THROWS(); |
437 | 0 | } |
438 | | |
439 | 0 | size_t length = (size_t)user_length; |
440 | 0 | retval = zend_string_alloc(length, 0); |
441 | |
|
442 | 0 | if (max_offset > 0xff) { |
443 | 0 | while (total_size < length) { |
444 | 0 | uint64_t offset = engine.algo->range(engine.state, 0, max_offset); |
445 | |
|
446 | 0 | if (EG(exception)) { |
447 | 0 | zend_string_efree(retval); |
448 | 0 | RETURN_THROWS(); |
449 | 0 | } |
450 | | |
451 | 0 | ZSTR_VAL(retval)[total_size++] = ZSTR_VAL(source)[offset]; |
452 | 0 | } |
453 | 0 | } else { |
454 | 0 | uint64_t mask = max_offset; |
455 | | // Copy the top-most bit into all lower bits. |
456 | | // Shifting by 4 is sufficient, because max_offset |
457 | | // is guaranteed to fit in an 8-bit integer at this |
458 | | // point. |
459 | 0 | mask |= mask >> 1; |
460 | 0 | mask |= mask >> 2; |
461 | 0 | mask |= mask >> 4; |
462 | | // Expand the lowest byte into all bytes. |
463 | 0 | mask *= 0x0101010101010101; |
464 | |
|
465 | 0 | int failures = 0; |
466 | 0 | while (total_size < length) { |
467 | 0 | php_random_result result = engine.algo->generate(engine.state); |
468 | 0 | if (EG(exception)) { |
469 | 0 | zend_string_efree(retval); |
470 | 0 | RETURN_THROWS(); |
471 | 0 | } |
472 | | |
473 | 0 | uint64_t offsets = result.result & mask; |
474 | 0 | for (size_t i = 0; i < result.size; i++) { |
475 | 0 | uint64_t offset = offsets & 0xff; |
476 | 0 | offsets >>= 8; |
477 | |
|
478 | 0 | if (offset > max_offset) { |
479 | 0 | if (++failures > PHP_RANDOM_RANGE_ATTEMPTS) { |
480 | 0 | zend_string_efree(retval); |
481 | 0 | zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS); |
482 | 0 | RETURN_THROWS(); |
483 | 0 | } |
484 | | |
485 | 0 | continue; |
486 | 0 | } |
487 | | |
488 | 0 | failures = 0; |
489 | |
|
490 | 0 | ZSTR_VAL(retval)[total_size++] = ZSTR_VAL(source)[offset]; |
491 | 0 | if (total_size >= length) { |
492 | 0 | break; |
493 | 0 | } |
494 | 0 | } |
495 | 0 | } |
496 | 0 | } |
497 | | |
498 | 0 | ZSTR_VAL(retval)[length] = '\0'; |
499 | 0 | RETURN_NEW_STR(retval); |
500 | 0 | } |
501 | | /* }}} */ |
502 | | |
503 | | /* {{{ Random\Randomizer::__serialize() */ |
504 | | PHP_METHOD(Random_Randomizer, __serialize) |
505 | 0 | { |
506 | 0 | php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); |
507 | 0 | zval t; |
508 | |
|
509 | 0 | ZEND_PARSE_PARAMETERS_NONE(); |
510 | | |
511 | 0 | array_init(return_value); |
512 | 0 | ZVAL_ARR(&t, zend_std_get_properties(&randomizer->std)); |
513 | 0 | Z_TRY_ADDREF(t); |
514 | 0 | zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &t); |
515 | 0 | } |
516 | | /* }}} */ |
517 | | |
518 | | /* {{{ Random\Randomizer::__unserialize() */ |
519 | | PHP_METHOD(Random_Randomizer, __unserialize) |
520 | 3 | { |
521 | 3 | php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); |
522 | 3 | HashTable *d; |
523 | 3 | zval *members_zv; |
524 | 3 | zval *zengine; |
525 | | |
526 | 9 | ZEND_PARSE_PARAMETERS_START(1, 1) |
527 | 12 | Z_PARAM_ARRAY_HT(d); |
528 | 3 | ZEND_PARSE_PARAMETERS_END(); |
529 | | |
530 | | /* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */ |
531 | 3 | if (zend_hash_num_elements(d) != 1) { |
532 | 1 | zend_throw_exception(NULL, "Invalid serialization data for Random\\Randomizer object", 0); |
533 | 1 | RETURN_THROWS(); |
534 | 1 | } |
535 | | |
536 | 2 | members_zv = zend_hash_index_find(d, 0); |
537 | 2 | if (!members_zv || Z_TYPE_P(members_zv) != IS_ARRAY) { |
538 | 2 | zend_throw_exception(NULL, "Invalid serialization data for Random\\Randomizer object", 0); |
539 | 2 | RETURN_THROWS(); |
540 | 2 | } |
541 | 0 | object_properties_load(&randomizer->std, Z_ARRVAL_P(members_zv)); |
542 | 0 | if (EG(exception)) { |
543 | 0 | zend_throw_exception(NULL, "Invalid serialization data for Random\\Randomizer object", 0); |
544 | 0 | RETURN_THROWS(); |
545 | 0 | } |
546 | | |
547 | 0 | zengine = zend_read_property(randomizer->std.ce, &randomizer->std, "engine", strlen("engine"), 1, NULL); |
548 | 0 | if (Z_TYPE_P(zengine) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(zengine), random_ce_Random_Engine)) { |
549 | 0 | zend_throw_exception(NULL, "Invalid serialization data for Random\\Randomizer object", 0); |
550 | 0 | RETURN_THROWS(); |
551 | 0 | } |
552 | | |
553 | 0 | randomizer_common_init(randomizer, Z_OBJ_P(zengine)); |
554 | 0 | } |
555 | | /* }}} */ |