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 | http://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: Andi Gutmans <andi@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include "php.h"
22
23 #ifdef HAVE_BCMATH
24
25 #include "php_ini.h"
26 #include "zend_exceptions.h"
27 #include "bcmath_arginfo.h"
28 #include "ext/standard/info.h"
29 #include "php_bcmath.h"
30 #include "libbcmath/src/bcmath.h"
31
32 ZEND_DECLARE_MODULE_GLOBALS(bcmath)
33 static PHP_GINIT_FUNCTION(bcmath);
34 static PHP_GSHUTDOWN_FUNCTION(bcmath);
35
36 zend_module_entry bcmath_module_entry = {
37 STANDARD_MODULE_HEADER,
38 "bcmath",
39 ext_functions,
40 PHP_MINIT(bcmath),
41 PHP_MSHUTDOWN(bcmath),
42 NULL,
43 NULL,
44 PHP_MINFO(bcmath),
45 PHP_BCMATH_VERSION,
46 PHP_MODULE_GLOBALS(bcmath),
47 PHP_GINIT(bcmath),
48 PHP_GSHUTDOWN(bcmath),
49 NULL,
50 STANDARD_MODULE_PROPERTIES_EX
51 };
52
53 #ifdef COMPILE_DL_BCMATH
54 #ifdef ZTS
55 ZEND_TSRMLS_CACHE_DEFINE()
56 #endif
ZEND_GET_MODULE(bcmath)57 ZEND_GET_MODULE(bcmath)
58 #endif
59
60 ZEND_INI_MH(OnUpdateScale)
61 {
62 int *p;
63 zend_long tmp;
64
65 tmp = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
66 if (tmp < 0 || tmp > INT_MAX) {
67 return FAILURE;
68 }
69
70 p = (int *) ZEND_INI_GET_ADDR();
71 *p = (int) tmp;
72
73 return SUCCESS;
74 }
75
76 /* {{{ PHP_INI */
77 PHP_INI_BEGIN()
78 STD_PHP_INI_ENTRY("bcmath.scale", "0", PHP_INI_ALL, OnUpdateScale, bc_precision, zend_bcmath_globals, bcmath_globals)
PHP_INI_END()79 PHP_INI_END()
80 /* }}} */
81
82 /* {{{ PHP_GINIT_FUNCTION */
83 static PHP_GINIT_FUNCTION(bcmath)
84 {
85 #if defined(COMPILE_DL_BCMATH) && defined(ZTS)
86 ZEND_TSRMLS_CACHE_UPDATE();
87 #endif
88 bcmath_globals->bc_precision = 0;
89 bc_init_numbers();
90 }
91 /* }}} */
92
93 /* {{{ PHP_GSHUTDOWN_FUNCTION */
PHP_GSHUTDOWN_FUNCTION(bcmath)94 static PHP_GSHUTDOWN_FUNCTION(bcmath)
95 {
96 _bc_free_num_ex(&bcmath_globals->_zero_, 1);
97 _bc_free_num_ex(&bcmath_globals->_one_, 1);
98 _bc_free_num_ex(&bcmath_globals->_two_, 1);
99 }
100 /* }}} */
101
102 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(bcmath)103 PHP_MINIT_FUNCTION(bcmath)
104 {
105 REGISTER_INI_ENTRIES();
106
107 return SUCCESS;
108 }
109 /* }}} */
110
111 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(bcmath)112 PHP_MSHUTDOWN_FUNCTION(bcmath)
113 {
114 UNREGISTER_INI_ENTRIES();
115
116 return SUCCESS;
117 }
118 /* }}} */
119
120 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(bcmath)121 PHP_MINFO_FUNCTION(bcmath)
122 {
123 php_info_print_table_start();
124 php_info_print_table_row(2, "BCMath support", "enabled");
125 php_info_print_table_end();
126 DISPLAY_INI_ENTRIES();
127 }
128 /* }}} */
129
130 /* {{{ php_str2num
131 Convert to bc_num detecting scale */
php_str2num(bc_num * num,char * str)132 static zend_result php_str2num(bc_num *num, char *str)
133 {
134 char *p;
135
136 if (!(p = strchr(str, '.'))) {
137 if (!bc_str2num(num, str, 0)) {
138 return FAILURE;
139 }
140
141 return SUCCESS;
142 }
143
144 if (!bc_str2num(num, str, strlen(p+1))) {
145 return FAILURE;
146 }
147
148 return SUCCESS;
149 }
150 /* }}} */
151
152 /* {{{ Returns the sum of two arbitrary precision numbers */
PHP_FUNCTION(bcadd)153 PHP_FUNCTION(bcadd)
154 {
155 zend_string *left, *right;
156 zend_long scale_param;
157 zend_bool scale_param_is_null = 1;
158 bc_num first, second, result;
159 int scale;
160
161 ZEND_PARSE_PARAMETERS_START(2, 3)
162 Z_PARAM_STR(left)
163 Z_PARAM_STR(right)
164 Z_PARAM_OPTIONAL
165 Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
166 ZEND_PARSE_PARAMETERS_END();
167
168 if (scale_param_is_null) {
169 scale = BCG(bc_precision);
170 } else if (scale_param < 0 || scale_param > INT_MAX) {
171 zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
172 RETURN_THROWS();
173 } else {
174 scale = (int) scale_param;
175 }
176
177 bc_init_num(&first);
178 bc_init_num(&second);
179 bc_init_num(&result);
180
181 if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
182 zend_argument_value_error(1, "is not well-formed");
183 goto cleanup;
184 }
185
186 if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
187 zend_argument_value_error(2, "is not well-formed");
188 goto cleanup;
189 }
190
191 bc_add (first, second, &result, scale);
192
193 RETVAL_STR(bc_num2str_ex(result, scale));
194
195 cleanup: {
196 bc_free_num(&first);
197 bc_free_num(&second);
198 bc_free_num(&result);
199 };
200 }
201 /* }}} */
202
203 /* {{{ Returns the difference between two arbitrary precision numbers */
PHP_FUNCTION(bcsub)204 PHP_FUNCTION(bcsub)
205 {
206 zend_string *left, *right;
207 zend_long scale_param;
208 zend_bool scale_param_is_null = 1;
209 bc_num first, second, result;
210 int scale;
211
212 ZEND_PARSE_PARAMETERS_START(2, 3)
213 Z_PARAM_STR(left)
214 Z_PARAM_STR(right)
215 Z_PARAM_OPTIONAL
216 Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
217 ZEND_PARSE_PARAMETERS_END();
218
219 if (scale_param_is_null) {
220 scale = BCG(bc_precision);
221 } else if (scale_param < 0 || scale_param > INT_MAX) {
222 zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
223 RETURN_THROWS();
224 } else {
225 scale = (int) scale_param;
226 }
227
228 bc_init_num(&first);
229 bc_init_num(&second);
230 bc_init_num(&result);
231
232 if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
233 zend_argument_value_error(1, "is not well-formed");
234 goto cleanup;
235 }
236
237 if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
238 zend_argument_value_error(2, "is not well-formed");
239 goto cleanup;
240 }
241
242 bc_sub (first, second, &result, scale);
243
244 RETVAL_STR(bc_num2str_ex(result, scale));
245
246 cleanup: {
247 bc_free_num(&first);
248 bc_free_num(&second);
249 bc_free_num(&result);
250 };
251 }
252 /* }}} */
253
254 /* {{{ Returns the multiplication of two arbitrary precision numbers */
PHP_FUNCTION(bcmul)255 PHP_FUNCTION(bcmul)
256 {
257 zend_string *left, *right;
258 zend_long scale_param;
259 zend_bool scale_param_is_null = 1;
260 bc_num first, second, result;
261 int scale;
262
263 ZEND_PARSE_PARAMETERS_START(2, 3)
264 Z_PARAM_STR(left)
265 Z_PARAM_STR(right)
266 Z_PARAM_OPTIONAL
267 Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
268 ZEND_PARSE_PARAMETERS_END();
269
270 if (scale_param_is_null) {
271 scale = BCG(bc_precision);
272 } else if (scale_param < 0 || scale_param > INT_MAX) {
273 zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
274 RETURN_THROWS();
275 } else {
276 scale = (int) scale_param;
277 }
278
279 bc_init_num(&first);
280 bc_init_num(&second);
281 bc_init_num(&result);
282
283 if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
284 zend_argument_value_error(1, "is not well-formed");
285 goto cleanup;
286 }
287
288 if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
289 zend_argument_value_error(2, "is not well-formed");
290 goto cleanup;
291 }
292
293 bc_multiply (first, second, &result, scale);
294
295 RETVAL_STR(bc_num2str_ex(result, scale));
296
297 cleanup: {
298 bc_free_num(&first);
299 bc_free_num(&second);
300 bc_free_num(&result);
301 };
302 }
303 /* }}} */
304
305 /* {{{ Returns the quotient of two arbitrary precision numbers (division) */
PHP_FUNCTION(bcdiv)306 PHP_FUNCTION(bcdiv)
307 {
308 zend_string *left, *right;
309 zend_long scale_param;
310 zend_bool scale_param_is_null = 1;
311 bc_num first, second, result;
312 int scale = BCG(bc_precision);
313
314 ZEND_PARSE_PARAMETERS_START(2, 3)
315 Z_PARAM_STR(left)
316 Z_PARAM_STR(right)
317 Z_PARAM_OPTIONAL
318 Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
319 ZEND_PARSE_PARAMETERS_END();
320
321 if (scale_param_is_null) {
322 scale = BCG(bc_precision);
323 } else if (scale_param < 0 || scale_param > INT_MAX) {
324 zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
325 RETURN_THROWS();
326 } else {
327 scale = (int) scale_param;
328 }
329
330 bc_init_num(&first);
331 bc_init_num(&second);
332 bc_init_num(&result);
333
334 if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
335 zend_argument_value_error(1, "is not well-formed");
336 goto cleanup;
337 }
338
339 if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
340 zend_argument_value_error(2, "is not well-formed");
341 goto cleanup;
342 }
343
344 switch (bc_divide(first, second, &result, scale)) {
345 case 0: /* OK */
346 RETVAL_STR(bc_num2str_ex(result, scale));
347 break;
348 case -1: /* division by zero */
349 zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
350 break;
351 }
352
353 cleanup: {
354 bc_free_num(&first);
355 bc_free_num(&second);
356 bc_free_num(&result);
357 };
358 }
359 /* }}} */
360
361 /* {{{ Returns the modulus of the two arbitrary precision operands */
PHP_FUNCTION(bcmod)362 PHP_FUNCTION(bcmod)
363 {
364 zend_string *left, *right;
365 zend_long scale_param;
366 zend_bool scale_param_is_null = 1;
367 bc_num first, second, result;
368 int scale = BCG(bc_precision);
369
370 ZEND_PARSE_PARAMETERS_START(2, 3)
371 Z_PARAM_STR(left)
372 Z_PARAM_STR(right)
373 Z_PARAM_OPTIONAL
374 Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
375 ZEND_PARSE_PARAMETERS_END();
376
377 if (scale_param_is_null) {
378 scale = BCG(bc_precision);
379 } else if (scale_param < 0 || scale_param > INT_MAX) {
380 zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
381 RETURN_THROWS();
382 } else {
383 scale = (int) scale_param;
384 }
385
386 bc_init_num(&first);
387 bc_init_num(&second);
388 bc_init_num(&result);
389
390 if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
391 zend_argument_value_error(1, "is not well-formed");
392 goto cleanup;
393 }
394
395 if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
396 zend_argument_value_error(2, "is not well-formed");
397 goto cleanup;
398 }
399
400 switch (bc_modulo(first, second, &result, scale)) {
401 case 0:
402 RETVAL_STR(bc_num2str_ex(result, scale));
403 break;
404 case -1:
405 zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Modulo by zero");
406 break;
407 }
408
409 cleanup: {
410 bc_free_num(&first);
411 bc_free_num(&second);
412 bc_free_num(&result);
413 };
414 }
415 /* }}} */
416
417 /* {{{ Returns the value of an arbitrary precision number raised to the power of another reduced by a modulus */
PHP_FUNCTION(bcpowmod)418 PHP_FUNCTION(bcpowmod)
419 {
420 zend_string *left, *right, *modulus;
421 zend_long scale_param;
422 zend_bool scale_param_is_null = 1;
423 bc_num first, second, mod, result;
424 int scale = BCG(bc_precision);
425
426 ZEND_PARSE_PARAMETERS_START(3, 4)
427 Z_PARAM_STR(left)
428 Z_PARAM_STR(right)
429 Z_PARAM_STR(modulus)
430 Z_PARAM_OPTIONAL
431 Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
432 ZEND_PARSE_PARAMETERS_END();
433
434 if (scale_param_is_null) {
435 scale = BCG(bc_precision);
436 } else if (scale_param < 0 || scale_param > INT_MAX) {
437 zend_argument_value_error(4, "must be between 0 and %d", INT_MAX);
438 RETURN_THROWS();
439 } else {
440 scale = (int) scale_param;
441 }
442
443 bc_init_num(&first);
444 bc_init_num(&second);
445 bc_init_num(&mod);
446 bc_init_num(&result);
447
448 if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
449 zend_argument_value_error(1, "is not well-formed");
450 goto cleanup;
451 }
452
453 if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
454 zend_argument_value_error(2, "is not well-formed");
455 goto cleanup;
456 }
457
458 if (php_str2num(&mod, ZSTR_VAL(modulus)) == FAILURE) {
459 zend_argument_value_error(3, "is not well-formed");
460 goto cleanup;
461 }
462
463 if (bc_raisemod(first, second, mod, &result, scale) == SUCCESS) {
464 RETVAL_STR(bc_num2str_ex(result, scale));
465 }
466
467 cleanup: {
468 bc_free_num(&first);
469 bc_free_num(&second);
470 bc_free_num(&mod);
471 bc_free_num(&result);
472 };
473 }
474 /* }}} */
475
476 /* {{{ Returns the value of an arbitrary precision number raised to the power of another */
PHP_FUNCTION(bcpow)477 PHP_FUNCTION(bcpow)
478 {
479 zend_string *left, *right;
480 zend_long scale_param;
481 zend_bool scale_param_is_null = 1;
482 bc_num first, second, result;
483 int scale = BCG(bc_precision);
484
485 ZEND_PARSE_PARAMETERS_START(2, 3)
486 Z_PARAM_STR(left)
487 Z_PARAM_STR(right)
488 Z_PARAM_OPTIONAL
489 Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
490 ZEND_PARSE_PARAMETERS_END();
491
492 if (scale_param_is_null) {
493 scale = BCG(bc_precision);
494 } else if (scale_param < 0 || scale_param > INT_MAX) {
495 zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
496 RETURN_THROWS();
497 } else {
498 scale = (int) scale_param;
499 }
500
501 bc_init_num(&first);
502 bc_init_num(&second);
503 bc_init_num(&result);
504
505 if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
506 zend_argument_value_error(1, "is not well-formed");
507 goto cleanup;
508 }
509
510 if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
511 zend_argument_value_error(2, "is not well-formed");
512 goto cleanup;
513 }
514
515 bc_raise (first, second, &result, scale);
516
517 RETVAL_STR(bc_num2str_ex(result, scale));
518
519 cleanup: {
520 bc_free_num(&first);
521 bc_free_num(&second);
522 bc_free_num(&result);
523 };
524 }
525 /* }}} */
526
527 /* {{{ Returns the square root of an arbitrary precision number */
PHP_FUNCTION(bcsqrt)528 PHP_FUNCTION(bcsqrt)
529 {
530 zend_string *left;
531 zend_long scale_param;
532 zend_bool scale_param_is_null = 1;
533 bc_num result;
534 int scale = BCG(bc_precision);
535
536 ZEND_PARSE_PARAMETERS_START(1, 2)
537 Z_PARAM_STR(left)
538 Z_PARAM_OPTIONAL
539 Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
540 ZEND_PARSE_PARAMETERS_END();
541
542 if (scale_param_is_null) {
543 scale = BCG(bc_precision);
544 } else if (scale_param < 0 || scale_param > INT_MAX) {
545 zend_argument_value_error(2, "must be between 0 and %d", INT_MAX);
546 RETURN_THROWS();
547 } else {
548 scale = (int) scale_param;
549 }
550
551 bc_init_num(&result);
552
553 if (php_str2num(&result, ZSTR_VAL(left)) == FAILURE) {
554 zend_argument_value_error(1, "is not well-formed");
555 goto cleanup;
556 }
557
558 if (bc_sqrt (&result, scale) != 0) {
559 RETVAL_STR(bc_num2str_ex(result, scale));
560 } else {
561 zend_argument_value_error(1, "must be greater than or equal to 0");
562 }
563
564 cleanup: {
565 bc_free_num(&result);
566 };
567 }
568 /* }}} */
569
570 /* {{{ Compares two arbitrary precision numbers */
PHP_FUNCTION(bccomp)571 PHP_FUNCTION(bccomp)
572 {
573 zend_string *left, *right;
574 zend_long scale_param;
575 zend_bool scale_param_is_null = 1;
576 bc_num first, second;
577 int scale = BCG(bc_precision);
578
579 ZEND_PARSE_PARAMETERS_START(2, 3)
580 Z_PARAM_STR(left)
581 Z_PARAM_STR(right)
582 Z_PARAM_OPTIONAL
583 Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
584 ZEND_PARSE_PARAMETERS_END();
585
586 if (scale_param_is_null) {
587 scale = BCG(bc_precision);
588 } else if (scale_param < 0 || scale_param > INT_MAX) {
589 zend_argument_value_error(3, "must be between 0 and %d", INT_MAX);
590 RETURN_THROWS();
591 } else {
592 scale = (int) scale_param;
593 }
594
595 bc_init_num(&first);
596 bc_init_num(&second);
597
598 if (!bc_str2num(&first, ZSTR_VAL(left), scale)) {
599 zend_argument_value_error(1, "is not well-formed");
600 goto cleanup;
601 }
602
603 if (!bc_str2num(&second, ZSTR_VAL(right), scale)) {
604 zend_argument_value_error(2, "is not well-formed");
605 goto cleanup;
606 }
607
608 RETVAL_LONG(bc_compare(first, second));
609
610 cleanup: {
611 bc_free_num(&first);
612 bc_free_num(&second);
613 };
614 }
615 /* }}} */
616
617 /* {{{ Sets default scale parameter for all bc math functions */
PHP_FUNCTION(bcscale)618 PHP_FUNCTION(bcscale)
619 {
620 zend_long old_scale, new_scale;
621 zend_bool new_scale_is_null = 1;
622
623 ZEND_PARSE_PARAMETERS_START(0, 1)
624 Z_PARAM_OPTIONAL
625 Z_PARAM_LONG_OR_NULL(new_scale, new_scale_is_null)
626 ZEND_PARSE_PARAMETERS_END();
627
628 old_scale = BCG(bc_precision);
629
630 if (!new_scale_is_null) {
631 if (new_scale < 0 || new_scale > INT_MAX) {
632 zend_argument_value_error(1, "must be between 0 and %d", INT_MAX);
633 RETURN_THROWS();
634 }
635
636 zend_string *ini_name = zend_string_init("bcmath.scale", sizeof("bcmath.scale") - 1, 0);
637 zend_string *new_scale_str = zend_long_to_str(new_scale);
638 zend_alter_ini_entry(ini_name, new_scale_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
639 zend_string_release(new_scale_str);
640 zend_string_release(ini_name);
641 }
642
643 RETURN_LONG(old_scale);
644 }
645 /* }}} */
646
647
648 #endif
649