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