1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Andi Gutmans <andi@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "php.h"
24
25 #if HAVE_BCMATH
26
27 #include "php_ini.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 /* {{{ arginfo */
37 ZEND_BEGIN_ARG_INFO_EX(arginfo_bcadd, 0, 0, 2)
38 ZEND_ARG_INFO(0, left_operand)
39 ZEND_ARG_INFO(0, right_operand)
40 ZEND_ARG_INFO(0, scale)
41 ZEND_END_ARG_INFO()
42
43 ZEND_BEGIN_ARG_INFO_EX(arginfo_bcsub, 0, 0, 2)
44 ZEND_ARG_INFO(0, left_operand)
45 ZEND_ARG_INFO(0, right_operand)
46 ZEND_ARG_INFO(0, scale)
47 ZEND_END_ARG_INFO()
48
49 ZEND_BEGIN_ARG_INFO_EX(arginfo_bcmul, 0, 0, 2)
50 ZEND_ARG_INFO(0, left_operand)
51 ZEND_ARG_INFO(0, right_operand)
52 ZEND_ARG_INFO(0, scale)
53 ZEND_END_ARG_INFO()
54
55 ZEND_BEGIN_ARG_INFO_EX(arginfo_bcdiv, 0, 0, 2)
56 ZEND_ARG_INFO(0, left_operand)
57 ZEND_ARG_INFO(0, right_operand)
58 ZEND_ARG_INFO(0, scale)
59 ZEND_END_ARG_INFO()
60
61 ZEND_BEGIN_ARG_INFO_EX(arginfo_bcmod, 0, 0, 2)
62 ZEND_ARG_INFO(0, left_operand)
63 ZEND_ARG_INFO(0, right_operand)
64 ZEND_ARG_INFO(0, scale)
65 ZEND_END_ARG_INFO()
66
67 ZEND_BEGIN_ARG_INFO_EX(arginfo_bcpowmod, 0, 0, 3)
68 ZEND_ARG_INFO(0, x)
69 ZEND_ARG_INFO(0, y)
70 ZEND_ARG_INFO(0, mod)
71 ZEND_ARG_INFO(0, scale)
72 ZEND_END_ARG_INFO()
73
74 ZEND_BEGIN_ARG_INFO_EX(arginfo_bcpow, 0, 0, 2)
75 ZEND_ARG_INFO(0, x)
76 ZEND_ARG_INFO(0, y)
77 ZEND_ARG_INFO(0, scale)
78 ZEND_END_ARG_INFO()
79
80 ZEND_BEGIN_ARG_INFO_EX(arginfo_bcsqrt, 0, 0, 1)
81 ZEND_ARG_INFO(0, operand)
82 ZEND_ARG_INFO(0, scale)
83 ZEND_END_ARG_INFO()
84
85 ZEND_BEGIN_ARG_INFO_EX(arginfo_bccomp, 0, 0, 2)
86 ZEND_ARG_INFO(0, left_operand)
87 ZEND_ARG_INFO(0, right_operand)
88 ZEND_ARG_INFO(0, scale)
89 ZEND_END_ARG_INFO()
90
91 ZEND_BEGIN_ARG_INFO_EX(arginfo_bcscale, 0, 0, 0)
92 ZEND_ARG_INFO(0, scale)
93 ZEND_END_ARG_INFO()
94
95 /* }}} */
96
97 static const zend_function_entry bcmath_functions[] = {
98 PHP_FE(bcadd, arginfo_bcadd)
99 PHP_FE(bcsub, arginfo_bcsub)
100 PHP_FE(bcmul, arginfo_bcmul)
101 PHP_FE(bcdiv, arginfo_bcdiv)
102 PHP_FE(bcmod, arginfo_bcmod)
103 PHP_FE(bcpow, arginfo_bcpow)
104 PHP_FE(bcsqrt, arginfo_bcsqrt)
105 PHP_FE(bcscale, arginfo_bcscale)
106 PHP_FE(bccomp, arginfo_bccomp)
107 PHP_FE(bcpowmod, arginfo_bcpowmod)
108 PHP_FE_END
109 };
110
111 zend_module_entry bcmath_module_entry = {
112 STANDARD_MODULE_HEADER,
113 "bcmath",
114 bcmath_functions,
115 PHP_MINIT(bcmath),
116 PHP_MSHUTDOWN(bcmath),
117 NULL,
118 NULL,
119 PHP_MINFO(bcmath),
120 PHP_BCMATH_VERSION,
121 PHP_MODULE_GLOBALS(bcmath),
122 PHP_GINIT(bcmath),
123 PHP_GSHUTDOWN(bcmath),
124 NULL,
125 STANDARD_MODULE_PROPERTIES_EX
126 };
127
128 #ifdef COMPILE_DL_BCMATH
129 #ifdef ZTS
130 ZEND_TSRMLS_CACHE_DEFINE()
131 #endif
ZEND_GET_MODULE(bcmath)132 ZEND_GET_MODULE(bcmath)
133 #endif
134
135 /* {{{ PHP_INI */
136 PHP_INI_BEGIN()
137 STD_PHP_INI_ENTRY("bcmath.scale", "0", PHP_INI_ALL, OnUpdateLongGEZero, bc_precision, zend_bcmath_globals, bcmath_globals)
138 PHP_INI_END()
139 /* }}} */
140
141 /* {{{ PHP_GINIT_FUNCTION
142 */
143 static PHP_GINIT_FUNCTION(bcmath)
144 {
145 #if defined(COMPILE_DL_BCMATH) && defined(ZTS)
146 ZEND_TSRMLS_CACHE_UPDATE();
147 #endif
148 bcmath_globals->bc_precision = 0;
149 bc_init_numbers();
150 }
151 /* }}} */
152
153 /* {{{ PHP_GSHUTDOWN_FUNCTION
154 */
PHP_GSHUTDOWN_FUNCTION(bcmath)155 static PHP_GSHUTDOWN_FUNCTION(bcmath)
156 {
157 _bc_free_num_ex(&bcmath_globals->_zero_, 1);
158 _bc_free_num_ex(&bcmath_globals->_one_, 1);
159 _bc_free_num_ex(&bcmath_globals->_two_, 1);
160 }
161 /* }}} */
162
163 /* {{{ PHP_MINIT_FUNCTION
164 */
PHP_MINIT_FUNCTION(bcmath)165 PHP_MINIT_FUNCTION(bcmath)
166 {
167 REGISTER_INI_ENTRIES();
168
169 return SUCCESS;
170 }
171 /* }}} */
172
173 /* {{{ PHP_MSHUTDOWN_FUNCTION
174 */
PHP_MSHUTDOWN_FUNCTION(bcmath)175 PHP_MSHUTDOWN_FUNCTION(bcmath)
176 {
177 UNREGISTER_INI_ENTRIES();
178
179 return SUCCESS;
180 }
181 /* }}} */
182
183 /* {{{ PHP_MINFO_FUNCTION
184 */
PHP_MINFO_FUNCTION(bcmath)185 PHP_MINFO_FUNCTION(bcmath)
186 {
187 php_info_print_table_start();
188 php_info_print_table_row(2, "BCMath support", "enabled");
189 php_info_print_table_end();
190 DISPLAY_INI_ENTRIES();
191 }
192 /* }}} */
193
194 /* {{{ php_str2num
195 Convert to bc_num detecting scale */
php_str2num(bc_num * num,char * str)196 static void php_str2num(bc_num *num, char *str)
197 {
198 char *p;
199
200 if (!(p = strchr(str, '.'))) {
201 if (!bc_str2num(num, str, 0)) {
202 php_error_docref(NULL, E_WARNING, "bcmath function argument is not well-formed");
203 }
204 return;
205 }
206
207 if (!bc_str2num(num, str, strlen(p+1))) {
208 php_error_docref(NULL, E_WARNING, "bcmath function argument is not well-formed");
209 }
210 }
211 /* }}} */
212
213 /* {{{ proto string bcadd(string left_operand, string right_operand [, int scale])
214 Returns the sum of two arbitrary precision numbers */
PHP_FUNCTION(bcadd)215 PHP_FUNCTION(bcadd)
216 {
217 zend_string *left, *right;
218 zend_long scale_param = 0;
219 bc_num first, second, result;
220 int scale = (int)BCG(bc_precision);
221
222 ZEND_PARSE_PARAMETERS_START(2, 3)
223 Z_PARAM_STR(left)
224 Z_PARAM_STR(right)
225 Z_PARAM_OPTIONAL
226 Z_PARAM_LONG(scale_param)
227 ZEND_PARSE_PARAMETERS_END();
228
229 if (ZEND_NUM_ARGS() == 3) {
230 scale = (int) (scale_param < 0 ? 0 : scale_param);
231 }
232
233 bc_init_num(&first);
234 bc_init_num(&second);
235 bc_init_num(&result);
236 php_str2num(&first, ZSTR_VAL(left));
237 php_str2num(&second, ZSTR_VAL(right));
238 bc_add (first, second, &result, scale);
239
240 RETVAL_STR(bc_num2str_ex(result, scale));
241 bc_free_num(&first);
242 bc_free_num(&second);
243 bc_free_num(&result);
244 return;
245 }
246 /* }}} */
247
248 /* {{{ proto string bcsub(string left_operand, string right_operand [, int scale])
249 Returns the difference between two arbitrary precision numbers */
PHP_FUNCTION(bcsub)250 PHP_FUNCTION(bcsub)
251 {
252 zend_string *left, *right;
253 zend_long scale_param = 0;
254 bc_num first, second, result;
255 int scale = (int)BCG(bc_precision);
256
257 ZEND_PARSE_PARAMETERS_START(2, 3)
258 Z_PARAM_STR(left)
259 Z_PARAM_STR(right)
260 Z_PARAM_OPTIONAL
261 Z_PARAM_LONG(scale_param)
262 ZEND_PARSE_PARAMETERS_END();
263
264 if (ZEND_NUM_ARGS() == 3) {
265 scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
266 }
267
268 bc_init_num(&first);
269 bc_init_num(&second);
270 bc_init_num(&result);
271 php_str2num(&first, ZSTR_VAL(left));
272 php_str2num(&second, ZSTR_VAL(right));
273 bc_sub (first, second, &result, scale);
274
275 RETVAL_STR(bc_num2str_ex(result, scale));
276 bc_free_num(&first);
277 bc_free_num(&second);
278 bc_free_num(&result);
279 return;
280 }
281 /* }}} */
282
283 /* {{{ proto string bcmul(string left_operand, string right_operand [, int scale])
284 Returns the multiplication of two arbitrary precision numbers */
PHP_FUNCTION(bcmul)285 PHP_FUNCTION(bcmul)
286 {
287 zend_string *left, *right;
288 zend_long scale_param = 0;
289 bc_num first, second, result;
290 int scale = (int)BCG(bc_precision);
291
292 ZEND_PARSE_PARAMETERS_START(2, 3)
293 Z_PARAM_STR(left)
294 Z_PARAM_STR(right)
295 Z_PARAM_OPTIONAL
296 Z_PARAM_LONG(scale_param)
297 ZEND_PARSE_PARAMETERS_END();
298
299 if (ZEND_NUM_ARGS() == 3) {
300 scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
301 }
302
303 bc_init_num(&first);
304 bc_init_num(&second);
305 bc_init_num(&result);
306 php_str2num(&first, ZSTR_VAL(left));
307 php_str2num(&second, ZSTR_VAL(right));
308 bc_multiply (first, second, &result, scale);
309
310 RETVAL_STR(bc_num2str_ex(result, scale));
311 bc_free_num(&first);
312 bc_free_num(&second);
313 bc_free_num(&result);
314 return;
315 }
316 /* }}} */
317
318 /* {{{ proto string bcdiv(string left_operand, string right_operand [, int scale])
319 Returns the quotient of two arbitrary precision numbers (division) */
PHP_FUNCTION(bcdiv)320 PHP_FUNCTION(bcdiv)
321 {
322 zend_string *left, *right;
323 zend_long scale_param = 0;
324 bc_num first, second, result;
325 int scale = (int)BCG(bc_precision);
326
327 ZEND_PARSE_PARAMETERS_START(2, 3)
328 Z_PARAM_STR(left)
329 Z_PARAM_STR(right)
330 Z_PARAM_OPTIONAL
331 Z_PARAM_LONG(scale_param)
332 ZEND_PARSE_PARAMETERS_END();
333
334 if (ZEND_NUM_ARGS() == 3) {
335 scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
336 }
337
338 bc_init_num(&first);
339 bc_init_num(&second);
340 bc_init_num(&result);
341 php_str2num(&first, ZSTR_VAL(left));
342 php_str2num(&second, ZSTR_VAL(right));
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 php_error_docref(NULL, E_WARNING, "Division by zero");
350 break;
351 }
352
353 bc_free_num(&first);
354 bc_free_num(&second);
355 bc_free_num(&result);
356 return;
357 }
358 /* }}} */
359
360 /* {{{ proto string bcmod(string left_operand, string right_operand [, int scale])
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 = 0;
366 bc_num first, second, result;
367 int scale = (int)BCG(bc_precision);
368
369 ZEND_PARSE_PARAMETERS_START(2, 3)
370 Z_PARAM_STR(left)
371 Z_PARAM_STR(right)
372 Z_PARAM_OPTIONAL
373 Z_PARAM_LONG(scale_param)
374 ZEND_PARSE_PARAMETERS_END();
375
376 if (ZEND_NUM_ARGS() == 3) {
377 scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
378 }
379
380 bc_init_num(&first);
381 bc_init_num(&second);
382 bc_init_num(&result);
383 php_str2num(&first, ZSTR_VAL(left));
384 php_str2num(&second, ZSTR_VAL(right));
385
386 switch (bc_modulo(first, second, &result, scale)) {
387 case 0:
388 RETVAL_STR(bc_num2str_ex(result, scale));
389 break;
390 case -1:
391 php_error_docref(NULL, E_WARNING, "Division by zero");
392 break;
393 }
394
395 bc_free_num(&first);
396 bc_free_num(&second);
397 bc_free_num(&result);
398 return;
399 }
400 /* }}} */
401
402 /* {{{ proto string bcpowmod(string x, string y, string mod [, int scale])
403 Returns the value of an arbitrary precision number raised to the power of another reduced by a modulous */
PHP_FUNCTION(bcpowmod)404 PHP_FUNCTION(bcpowmod)
405 {
406 zend_string *left, *right, *modulous;
407 bc_num first, second, mod, result;
408 zend_long scale = BCG(bc_precision);
409 int scale_int;
410
411 ZEND_PARSE_PARAMETERS_START(3, 4)
412 Z_PARAM_STR(left)
413 Z_PARAM_STR(right)
414 Z_PARAM_STR(modulous)
415 Z_PARAM_OPTIONAL
416 Z_PARAM_LONG(scale)
417 ZEND_PARSE_PARAMETERS_END();
418
419 bc_init_num(&first);
420 bc_init_num(&second);
421 bc_init_num(&mod);
422 bc_init_num(&result);
423 php_str2num(&first, ZSTR_VAL(left));
424 php_str2num(&second, ZSTR_VAL(right));
425 php_str2num(&mod, ZSTR_VAL(modulous));
426
427 scale_int = (int) ((int)scale < 0 ? 0 : scale);
428
429 if (bc_raisemod(first, second, mod, &result, scale_int) != -1) {
430 RETVAL_STR(bc_num2str_ex(result, scale_int));
431 } else {
432 RETVAL_FALSE;
433 }
434
435 bc_free_num(&first);
436 bc_free_num(&second);
437 bc_free_num(&mod);
438 bc_free_num(&result);
439 return;
440 }
441 /* }}} */
442
443 /* {{{ proto string bcpow(string x, string y [, int scale])
444 Returns the value of an arbitrary precision number raised to the power of another */
PHP_FUNCTION(bcpow)445 PHP_FUNCTION(bcpow)
446 {
447 zend_string *left, *right;
448 zend_long scale_param = 0;
449 bc_num first, second, result;
450 int scale = (int)BCG(bc_precision);
451
452 ZEND_PARSE_PARAMETERS_START(2, 3)
453 Z_PARAM_STR(left)
454 Z_PARAM_STR(right)
455 Z_PARAM_OPTIONAL
456 Z_PARAM_LONG(scale_param)
457 ZEND_PARSE_PARAMETERS_END();
458
459 if (ZEND_NUM_ARGS() == 3) {
460 scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
461 }
462
463 bc_init_num(&first);
464 bc_init_num(&second);
465 bc_init_num(&result);
466 php_str2num(&first, ZSTR_VAL(left));
467 php_str2num(&second, ZSTR_VAL(right));
468 bc_raise (first, second, &result, scale);
469
470 RETVAL_STR(bc_num2str_ex(result, scale));
471 bc_free_num(&first);
472 bc_free_num(&second);
473 bc_free_num(&result);
474 return;
475 }
476 /* }}} */
477
478 /* {{{ proto string bcsqrt(string operand [, int scale])
479 Returns the square root of an arbitrary precision number */
PHP_FUNCTION(bcsqrt)480 PHP_FUNCTION(bcsqrt)
481 {
482 zend_string *left;
483 zend_long scale_param = 0;
484 bc_num result;
485 int scale = (int)BCG(bc_precision);
486
487 ZEND_PARSE_PARAMETERS_START(1, 2)
488 Z_PARAM_STR(left)
489 Z_PARAM_OPTIONAL
490 Z_PARAM_LONG(scale_param)
491 ZEND_PARSE_PARAMETERS_END();
492
493 if (ZEND_NUM_ARGS() == 2) {
494 scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
495 }
496
497 bc_init_num(&result);
498 php_str2num(&result, ZSTR_VAL(left));
499
500 if (bc_sqrt (&result, scale) != 0) {
501 RETVAL_STR(bc_num2str_ex(result, scale));
502 } else {
503 php_error_docref(NULL, E_WARNING, "Square root of negative number");
504 }
505
506 bc_free_num(&result);
507 return;
508 }
509 /* }}} */
510
511 /* {{{ proto int bccomp(string left_operand, string right_operand [, int scale])
512 Compares two arbitrary precision numbers */
PHP_FUNCTION(bccomp)513 PHP_FUNCTION(bccomp)
514 {
515 zend_string *left, *right;
516 zend_long scale_param = 0;
517 bc_num first, second;
518 int scale = (int)BCG(bc_precision);
519
520 ZEND_PARSE_PARAMETERS_START(2, 3)
521 Z_PARAM_STR(left)
522 Z_PARAM_STR(right)
523 Z_PARAM_OPTIONAL
524 Z_PARAM_LONG(scale_param)
525 ZEND_PARSE_PARAMETERS_END();
526
527 if (ZEND_NUM_ARGS() == 3) {
528 scale = (int) ((int)scale_param < 0 ? 0 : scale_param);
529 }
530
531 bc_init_num(&first);
532 bc_init_num(&second);
533
534 if (!bc_str2num(&first, ZSTR_VAL(left), scale)) {
535 php_error_docref(NULL, E_WARNING, "bcmath function argument is not well-formed");
536 }
537 if (!bc_str2num(&second, ZSTR_VAL(right), scale)) {
538 php_error_docref(NULL, E_WARNING, "bcmath function argument is not well-formed");
539 }
540 RETVAL_LONG(bc_compare(first, second));
541
542 bc_free_num(&first);
543 bc_free_num(&second);
544 return;
545 }
546 /* }}} */
547
548 /* {{{ proto int bcscale([int scale])
549 Sets default scale parameter for all bc math functions */
PHP_FUNCTION(bcscale)550 PHP_FUNCTION(bcscale)
551 {
552 zend_long old_scale, new_scale;
553
554 ZEND_PARSE_PARAMETERS_START(0, 1)
555 Z_PARAM_OPTIONAL
556 Z_PARAM_LONG(new_scale)
557 ZEND_PARSE_PARAMETERS_END();
558
559 old_scale = BCG(bc_precision);
560
561 if (ZEND_NUM_ARGS() == 1) {
562 BCG(bc_precision) = ((int)new_scale < 0) ? 0 : new_scale;
563 }
564
565 RETURN_LONG(old_scale);
566 }
567 /* }}} */
568
569
570 #endif
571