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