├── .github └── workflows │ └── ci.yml ├── LICENSE.md ├── README.md ├── composer.json ├── lib └── bcmath.php └── src └── BCMath.php /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | 4 | permissions: 5 | contents: read # to fetch code (actions/checkout) 6 | 7 | jobs: 8 | tests: 9 | name: Tests 10 | timeout-minutes: 10 11 | runs-on: ${{ matrix.os }} 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | - name: Setup PHP 16 | uses: shivammathur/setup-php@v2 17 | with: 18 | php-version: ${{ matrix.php-version }} 19 | - name: Composer Install 20 | run: composer install --no-interaction --no-cache 21 | - name: PHPUnit 22 | run: vendor/bin/phpunit 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | os: [ubuntu-latest, windows-latest, macos-latest] 27 | php-version: ['8.1', '8.2', '8.3'] 28 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2019 terrafrost 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bcmath_compat 2 | 3 | [![Software License][ico-license]](LICENSE.md) 4 | [![CI Status](https://github.com/phpseclib/bcmath_compat/actions/workflows/ci.yml/badge.svg?branch=master&event=push "CI Status")](https://github.com/phpseclib/bcmath_compat/actions/workflows/ci.yml?query=branch%3Amaster) 5 | 6 | PHP 5.x-8.x polyfill for bcmath extension 7 | 8 | ## Installation 9 | 10 | With [Composer](https://getcomposer.org/): 11 | 12 | ```bash 13 | $ composer require phpseclib/bcmath_compat 14 | ``` 15 | 16 | ## Limitations 17 | 18 | - `extension_loaded('bcmath')` 19 | 20 | bcmath_compat cannot make this return true. The recommended remediation is to not do this. 21 | 22 | - `ini_set('bcmath.scale', ...)` 23 | 24 | You cannot set configuration options for extensions that are not installed. If you do `ini_set('bcmath.scale', 5)` on a system without bcmath installed then `ini_get('bcmath.scale')` will return `false`. It's similar to what happens when you do `ini_set('zzz', 5)` and then `ini_get('zzz')`. You'll get `false` back. 25 | 26 | The recommended remediation to doing `ini_set('bcmath.scale', ...)` is to do `bcscale(...)`. The recommended remediation for doing `ini_get` is (if you're using PHP >= 7.3.0) to do `bcscale()` or (if you're using PHP < 7.3.0) to do `max(0, strlen(bcadd('0', '0')) - 2)`. 27 | 28 | Note that `ini_get` always returns a string whereas the recommended remediations return integers. 29 | 30 | [ico-version]: https://img.shields.io/packagist/v/phpseclib/bcmath_compat.svg?style=flat-square 31 | [ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square 32 | [ico-travis]: https://img.shields.io/travis/phpseclib/bcmath_compat/master.svg?style=flat-square 33 | [ico-scrutinizer]: https://img.shields.io/scrutinizer/coverage/g/phpseclib/bcmath_compat.svg?style=flat-square 34 | [ico-code-quality]: https://img.shields.io/scrutinizer/g/phpseclib/bcmath_compat.svg?style=flat-square 35 | [ico-downloads]: https://img.shields.io/packagist/dt/phpseclib/bcmath_compat.svg?style=flat-square 36 | 37 | [link-packagist]: https://packagist.org/packages/phpseclib/bcmath_compat 38 | [link-travis]: https://travis-ci.org/phpseclib/bcmath_compat 39 | [link-scrutinizer]: https://scrutinizer-ci.com/g/phpseclib/bcmath_compat/code-structure 40 | [link-code-quality]: https://scrutinizer-ci.com/g/phpseclib/bcmath_compat 41 | [link-downloads]: https://packagist.org/packages/phpseclib/bcmath_compat 42 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phpseclib/bcmath_compat", 3 | "description": "PHP 5.x-8.x polyfill for bcmath extension", 4 | "keywords": [ 5 | "bcmath", 6 | "math", 7 | "biginteger", 8 | "bigdecimal", 9 | "polyfill" 10 | ], 11 | "license": "MIT", 12 | "type": "library", 13 | "authors": [ 14 | { 15 | "name": "Jim Wigginton", 16 | "email": "terrafrost@php.net", 17 | "homepage": "http://phpseclib.sourceforge.net" 18 | } 19 | ], 20 | "support": { 21 | "issues": "https://github.com/phpseclib/bcmath_compat/issues", 22 | "email": "terrafrost@php.net", 23 | "source": "https://github.com/phpseclib/bcmath_compat" 24 | }, 25 | "require": { 26 | "phpseclib/phpseclib": "dev-master" 27 | }, 28 | "require-dev": { 29 | "phpunit/phpunit": "^10.5|^11.1", 30 | "squizlabs/php_codesniffer": "^3.0" 31 | }, 32 | "suggest": { 33 | "ext-gmp": "Will enable faster math operations" 34 | }, 35 | "autoload": { 36 | "files": ["lib/bcmath.php"], 37 | "psr-4": { 38 | "bcmath_compat\\": "src" 39 | } 40 | }, 41 | "scripts": { 42 | "test": "phpunit", 43 | "check-style": "phpcs src tests", 44 | "fix-style": "phpcbf src tests" 45 | }, 46 | "provide": { 47 | "ext-bcmath": "8.1.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/bcmath.php: -------------------------------------------------------------------------------- 1 | 27 | * @copyright 2019 Jim Wigginton 28 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 29 | * @link http://phpseclib.sourceforge.net 30 | */ 31 | 32 | use bcmath_compat\BCMath; 33 | 34 | if (!function_exists('bcadd')) { 35 | /** 36 | * Add two arbitrary precision numbers 37 | * 38 | * @var string $left_operand 39 | * @var string $right_operand 40 | * @var int $scale optional 41 | */ 42 | function bcadd($left_operand, $right_operand, $scale = 0) 43 | { 44 | return BCMath::add($left_operand, $right_operand, $scale); 45 | } 46 | 47 | /** 48 | * Compare two arbitrary precision numbers 49 | * 50 | * @var string $left_operand 51 | * @var string $right_operand 52 | * @var int $scale optional 53 | */ 54 | function bccomp($left_operand, $right_operand, $scale = 0) 55 | { 56 | return BCMath::comp($left_operand, $right_operand, $scale); 57 | } 58 | 59 | /** 60 | * Divide two arbitrary precision numbers 61 | * 62 | * @var string $dividend 63 | * @var string $divisor 64 | * @var int $scale optional 65 | */ 66 | function bcdiv($dividend, $divisor, $scale = 0) 67 | { 68 | return BCMath::div($dividend, $divisor, $scale); 69 | } 70 | 71 | /** 72 | * Get modulus of an arbitrary precision number 73 | * 74 | * @var string $dividend 75 | * @var string $divisor 76 | * @var int $scale optional 77 | */ 78 | function bcmod($dividend, $divisor, $scale = 0) 79 | { 80 | return BCMath::mod($dividend, $divisor, $scale); 81 | } 82 | 83 | /** 84 | * Multiply two arbitrary precision numbers 85 | * 86 | * @var string $left_operand 87 | * @var string $right_operand 88 | * @var int $scale optional 89 | */ 90 | function bcmul($dividend, $divisor, $scale = 0) 91 | { 92 | return BCMath::mul($dividend, $divisor, $scale); 93 | } 94 | 95 | /** 96 | * Raise an arbitrary precision number to another 97 | * 98 | * @var string $base 99 | * @var string $exponent 100 | * @var int $scale optional 101 | */ 102 | function bcpow($base, $exponent, $scale = 0) 103 | { 104 | return BCMath::pow($base, $exponent, $scale); 105 | } 106 | 107 | /** 108 | * Raise an arbitrary precision number to another, reduced by a specified modulus 109 | * 110 | * @var string $base 111 | * @var string $exponent 112 | * @var string $modulus 113 | * @var int $scale optional 114 | */ 115 | function bcpowmod($base, $exponent, $modulus, $scale = 0) 116 | { 117 | return BCMath::powmod($base, $exponent, $modulus, $scale); 118 | } 119 | 120 | /** 121 | * Set or get default scale parameter for all bc math functions 122 | * 123 | * @var int $scale 124 | */ 125 | function bcscale($scale = null) 126 | { 127 | return BCMath::scale($scale); 128 | } 129 | 130 | /** 131 | * Get the square root of an arbitrary precision number 132 | * 133 | * @var string $operand 134 | * @var int $scale optional 135 | */ 136 | function bcsqrt($operand, $scale = 0) 137 | { 138 | return BCMath::sqrt($operand, $scale); 139 | } 140 | 141 | /** 142 | * Subtract one arbitrary precision number from another 143 | * 144 | * @var string $left_operand 145 | * @var string $right_operand 146 | * @var int $scale optional 147 | */ 148 | function bcsub($left_operand, $right_operand, $scale = 0) 149 | { 150 | return BCMath::sub($left_operand, $right_operand, $scale); 151 | } 152 | } 153 | 154 | // the following were introduced in PHP 7.0.0 155 | if (!class_exists('Error')) { 156 | class Error extends Exception 157 | { 158 | } 159 | 160 | class ArithmeticError extends Error 161 | { 162 | } 163 | 164 | class DivisionByZeroError extends ArithmeticError 165 | { 166 | } 167 | 168 | class TypeError extends Error 169 | { 170 | } 171 | } 172 | 173 | // the following was introduced in PHP 7.1.0 174 | if (!class_exists('ArgumentCountError')) { 175 | class ArgumentCountError extends TypeError 176 | { 177 | } 178 | } 179 | 180 | // the following was introduced in PHP 8.0.0 181 | if (!class_exists('ValueError')) { 182 | class ValueError extends Error 183 | { 184 | } 185 | } -------------------------------------------------------------------------------- /src/BCMath.php: -------------------------------------------------------------------------------- 1 | 9 | * @copyright 2019 Jim Wigginton 10 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 11 | */ 12 | 13 | namespace bcmath_compat; 14 | 15 | use phpseclib3\Math\BigInteger; 16 | 17 | /** 18 | * BCMath Emulation Class 19 | * 20 | * @author Jim Wigginton 21 | * @access public 22 | */ 23 | abstract class BCMath 24 | { 25 | /** 26 | * Default scale parameter for all bc math functions 27 | */ 28 | private static $scale; 29 | 30 | /** 31 | * Set or get default scale parameter for all bc math functions 32 | * 33 | * Uses the PHP 7.3+ behavior 34 | * 35 | * @var int $scale optional 36 | */ 37 | private static function scale($scale = null) 38 | { 39 | if (isset($scale)) { 40 | self::$scale = (int) $scale; 41 | } 42 | return self::$scale; 43 | } 44 | 45 | /** 46 | * Formats numbers 47 | * 48 | * Places the decimal place at the appropriate place, adds trailing 0's as appropriate, etc 49 | * 50 | * @var string $x 51 | * @var int $scale 52 | * @var int $pad 53 | * @var boolean $trim 54 | */ 55 | private static function format($x, $scale, $pad) 56 | { 57 | $sign = self::isNegative($x) ? '-' : ''; 58 | $x = str_replace('-', '', $x); 59 | 60 | if (strlen($x) != $pad) { 61 | $x = str_pad($x, $pad, '0', STR_PAD_LEFT); 62 | } 63 | $temp = $pad ? substr_replace($x, '.', -$pad, 0) : $x; 64 | $temp = explode('.', $temp); 65 | if ($temp[0] == '') { 66 | $temp[0] = '0'; 67 | } 68 | if (isset($temp[1])) { 69 | $temp[1] = substr($temp[1], 0, $scale); 70 | $temp[1] = str_pad($temp[1], $scale, '0'); 71 | } elseif ($scale) { 72 | $temp[1] = str_repeat('0', $scale); 73 | } 74 | return $sign . rtrim(implode('.', $temp), '.'); 75 | } 76 | 77 | /** 78 | * Negativity Test 79 | * 80 | * @var BigInteger $x 81 | */ 82 | private static function isNegative($x) 83 | { 84 | return $x->compare(new BigInteger()) < 0; 85 | } 86 | 87 | /** 88 | * Add two arbitrary precision numbers 89 | * 90 | * @var string $x 91 | * @var string $y 92 | * @var int $scale 93 | * @var int $pad 94 | */ 95 | private static function add($x, $y, $scale, $pad) 96 | { 97 | $z = $x->add($y); 98 | 99 | return self::format($z, $scale, $pad); 100 | } 101 | 102 | /** 103 | * Subtract one arbitrary precision number from another 104 | * 105 | * @var string $x 106 | * @var string $y 107 | * @var int $scale 108 | * @var int $pad 109 | */ 110 | private static function sub($x, $y, $scale, $pad) 111 | { 112 | $z = $x->subtract($y); 113 | 114 | return self::format($z, $scale, $pad); 115 | } 116 | 117 | /** 118 | * Multiply two arbitrary precision numbers 119 | * 120 | * @var string $x 121 | * @var string $y 122 | * @var int $scale 123 | * @var int $pad 124 | */ 125 | private static function mul($x, $y, $scale, $pad) 126 | { 127 | if ($x == '0' || $y == '0') { 128 | $r = '0'; 129 | if ($scale) { 130 | $r.= '.' . str_repeat('0', $scale); 131 | } 132 | return $r; 133 | } 134 | 135 | $z = $x->abs()->multiply($y->abs()); 136 | $sign = (self::isNegative($x) ^ self::isNegative($y)) ? '-' : ''; 137 | 138 | return $sign . self::format($z, $scale, 2 * $pad); 139 | } 140 | 141 | /** 142 | * Divide two arbitrary precision numbers 143 | * 144 | * @var string $x 145 | * @var string $y 146 | * @var int $scale 147 | * @var int $pad 148 | */ 149 | private static function div($x, $y, $scale, $pad) 150 | { 151 | if ($y == '0') { 152 | // < PHP 8.0 triggered a warning 153 | // >= PHP 8.0 throws an exception 154 | throw new \DivisionByZeroError('Division by zero'); 155 | } 156 | 157 | $temp = '1' . str_repeat('0', $scale); 158 | $temp = new BigInteger($temp); 159 | list($q) = $x->multiply($temp)->divide($y); 160 | 161 | return self::format($q, $scale, $scale); 162 | } 163 | 164 | /** 165 | * Get modulus of an arbitrary precision number 166 | * 167 | * Uses the PHP 7.2+ behavior 168 | * 169 | * @var string $x 170 | * @var string $y 171 | * @var int $scale 172 | * @var int $pad 173 | */ 174 | private static function mod($x, $y, $scale, $pad) 175 | { 176 | if ($y == '0') { 177 | // < PHP 8.0 triggered a warning 178 | // >= PHP 8.0 throws an exception 179 | throw new \DivisionByZeroError('Division by zero'); 180 | } 181 | 182 | list($q) = $x->divide($y); 183 | $z = $y->multiply($q); 184 | $z = $x->subtract($z); 185 | 186 | return self::format($z, $scale, $pad); 187 | } 188 | 189 | /** 190 | * Compare two arbitrary precision numbers 191 | * 192 | * @var string $x 193 | * @var string $y 194 | * @var int $scale 195 | * @var int $pad 196 | */ 197 | private static function comp($x, $y, $scale, $pad) 198 | { 199 | $x = new BigInteger($x[0] . substr($x[1], 0, $scale)); 200 | $y = new BigInteger($y[0] . substr($y[1], 0, $scale)); 201 | 202 | return $x->compare($y); 203 | } 204 | 205 | /** 206 | * Raise an arbitrary precision number to another 207 | * 208 | * Uses the PHP 7.2+ behavior 209 | * 210 | * @var string $x 211 | * @var string $y 212 | * @var int $scale 213 | * @var int $pad 214 | */ 215 | private static function pow($x, $y, $scale, $pad) 216 | { 217 | if ($y == '0') { 218 | $r = '1'; 219 | if ($scale) { 220 | $r.= '.' . str_repeat('0', $scale); 221 | } 222 | return $r; 223 | } 224 | 225 | $min = defined('PHP_INT_MIN') ? PHP_INT_MIN : ~PHP_INT_MAX; 226 | if (bccomp($y, PHP_INT_MAX) > 0 || bccomp($y, $min) <= 0) { 227 | throw new \ValueError('bcpow(): Argument #2 ($exponent) is too large'); 228 | } 229 | 230 | $sign = self::isNegative($x) ? '-' : ''; 231 | $x = $x->abs(); 232 | 233 | $r = new BigInteger(1); 234 | 235 | for ($i = 0; $i < abs($y); $i++) { 236 | $r = $r->multiply($x); 237 | } 238 | 239 | if ($y < 0) { 240 | $temp = '1' . str_repeat('0', $scale + $pad * abs($y)); 241 | $temp = new BigInteger($temp); 242 | list($r) = $temp->divide($r); 243 | $pad = $scale; 244 | } else { 245 | $pad*= abs($y); 246 | } 247 | 248 | return $sign . self::format($r, $scale, $pad); 249 | } 250 | 251 | /** 252 | * Raise an arbitrary precision number to another, reduced by a specified modulus 253 | * 254 | * @var string $x 255 | * @var string $e 256 | * @var string $n 257 | * @var int $scale 258 | * @var int $pad 259 | */ 260 | private static function powmod($x, $e, $n, $scale, $pad) 261 | { 262 | if ($e[0] == '-' || $n == '0') { 263 | // < PHP 8.0 returned false 264 | // >= PHP 8.0 throws an exception 265 | throw new \ValueError('bcpowmod(): Argument #2 ($exponent) must be greater than or equal to 0'); 266 | } 267 | if ($n[0] == '-') { 268 | $n = substr($n, 1); 269 | } 270 | if ($e == '0') { 271 | return $scale ? 272 | '1.' . str_repeat('0', $scale) : 273 | '1'; 274 | } 275 | 276 | $x = new BigInteger($x); 277 | $e = new BigInteger($e); 278 | $n = new BigInteger($n); 279 | 280 | $z = $x->powMod($e, $n); 281 | 282 | return $scale ? 283 | "$z." . str_repeat('0', $scale) : 284 | "$z"; 285 | } 286 | 287 | /** 288 | * Get the square root of an arbitrary precision number 289 | * 290 | * @var string $n 291 | * @var int $scale 292 | * @var int $pad 293 | */ 294 | private static function sqrt($n, $scale, $pad) 295 | { 296 | // the following is based off of the following URL: 297 | // https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Decimal_(base_10) 298 | 299 | if (!is_numeric($n)) { 300 | return '0'; 301 | } 302 | $temp = explode('.', $n); 303 | $decStart = ceil(strlen($temp[0]) / 2); 304 | $n = implode('', $temp); 305 | if (strlen($n) % 2) { 306 | $n = "0$n"; 307 | } 308 | $parts = str_split($n, 2); 309 | $parts = array_map('intval', $parts); 310 | $i = 0; 311 | $p = 0; // for the first step, p = 0 312 | $c = $parts[$i]; 313 | $result = ''; 314 | while (true) { 315 | // determine the greatest digit x such that x(20p+x) <= c 316 | for ($x = 1; $x <= 10; $x++) { 317 | if ($x * (20 * $p + $x) > $c) { 318 | $x--; 319 | break; 320 | } 321 | } 322 | $result.= $x; 323 | $y = $x * (20 * $p + $x); 324 | $p = 10 * $p + $x; 325 | $c = 100 * ($c - $y); 326 | if (isset($parts[++$i])) { 327 | $c+= $parts[$i]; 328 | } 329 | if ((!$c && $i >= $decStart) || $i - $decStart == $scale) { 330 | break; 331 | } 332 | if ($decStart == $i) { 333 | $result.= '.'; 334 | } 335 | } 336 | 337 | $result = explode('.', $result); 338 | if (isset($result[1])) { 339 | $result[1] = str_pad($result[1], $scale, '0'); 340 | } elseif ($scale) { 341 | $result[1] = str_repeat('0', $scale); 342 | } 343 | return implode('.', $result); 344 | } 345 | 346 | /** 347 | * __callStatic Magic Method 348 | * 349 | * @var string $name 350 | * @var array $arguments 351 | */ 352 | public static function __callStatic($name, $arguments) 353 | { 354 | static $params = [ 355 | 'add' => 3, 356 | 'comp' => 3, 357 | 'div' => 3, 358 | 'mod' => 3, 359 | 'mul' => 3, 360 | 'pow' => 3, 361 | 'powmod' => 4, 362 | 'scale' => 1, 363 | 'sqrt' => 2, 364 | 'sub' => 3 365 | ]; 366 | $cnt = count($arguments); 367 | if ($cnt < $params[$name] - 1) { 368 | $min = $params[$name] - 1; 369 | throw new \ArgumentCountError("bc$name() expects at least $min parameters, " . $cnt . " given"); 370 | } 371 | if ($cnt > $params[$name]) { 372 | $str = "bc$name() expects at most {$params[$name]} parameters, " . $cnt . " given"; 373 | throw new \ArgumentCountError($str); 374 | } 375 | $numbers = array_slice($arguments, 0, $params[$name] - 1); 376 | 377 | $ints = []; 378 | switch ($name) { 379 | case 'pow': 380 | $ints = array_slice($numbers, count($numbers) - 1); 381 | $numbers = array_slice($numbers, 0, count($numbers) - 1); 382 | $names = ['exponent']; 383 | break; 384 | case 'powmod': 385 | $ints = $numbers; 386 | $numbers = []; 387 | $names = ['base', 'exponent', 'modulus']; 388 | break; 389 | case 'sqrt': 390 | $names = ['num']; 391 | break; 392 | default: 393 | $names = ['num1', 'num2']; 394 | } 395 | foreach ($ints as $i => &$int) { 396 | if (!is_numeric($int)) { 397 | $int = '0'; 398 | } 399 | $pos = strpos($int, '.'); 400 | if ($pos !== false) { 401 | $int = substr($int, 0, $pos); 402 | throw new \ValueError("bc$name(): Argument #2 (\$$names[$i]) cannot have a fractional part"); 403 | } 404 | } 405 | foreach ($numbers as $i => $arg) { 406 | $num = $i + 1; 407 | switch (true) { 408 | case is_bool($arg): 409 | case is_numeric($arg): 410 | case is_string($arg): 411 | case is_object($arg) && method_exists($arg, '__toString'): 412 | if (!is_bool($arg) && !is_numeric("$arg")) { 413 | throw new \ValueError("bc$name: bcmath function argument is not well-formed"); 414 | } 415 | break; 416 | // PHP >= 8.1 has deprecated the passing of nulls to string parameters 417 | case is_null($arg): 418 | $error = "bc$name(): Passing null to parameter #$num (\$$names[$i]) of type string is deprecated"; 419 | trigger_error($error, E_USER_DEPRECATED); 420 | break; 421 | default: 422 | $type = is_object($arg) ? get_class($arg) : gettype($arg); 423 | $error = "bc$name(): Argument #$num (\$$names[$i]) must be of type string, $type given"; 424 | throw new \TypeError($error); 425 | } 426 | } 427 | if (!isset(self::$scale)) { 428 | $scale = ini_get('bcmath.scale'); 429 | self::$scale = $scale !== false ? max(intval($scale), 0) : 0; 430 | } 431 | $scale = isset($arguments[$params[$name] - 1]) ? $arguments[$params[$name] - 1] : self::$scale; 432 | switch (true) { 433 | case is_bool($scale): 434 | case is_numeric($scale): 435 | case is_string($scale) && preg_match('#0-9\.#', $scale[0]): 436 | break; 437 | default: 438 | $type = is_object($arg) ? get_class($arg) : gettype($arg); 439 | $str = "bc$name(): Argument #$params[$name] (\$scale) must be of type ?int, string given"; 440 | throw new \TypeError($str); 441 | } 442 | $scale = (int) $scale; 443 | if ($scale < 0) { 444 | throw new \ValueError("bc$name(): Argument #$params[$name] (\$scale) must be between 0 and 2147483647"); 445 | } 446 | 447 | $pad = 0; 448 | foreach ($numbers as &$num) { 449 | if (is_bool($num)) { 450 | $num = $num ? '1' : '0'; 451 | } elseif (!is_numeric($num)) { 452 | $num = '0'; 453 | } 454 | $num = explode('.', $num); 455 | if (isset($num[1])) { 456 | $pad = max($pad, strlen($num[1])); 457 | } 458 | } 459 | switch ($name) { 460 | case 'add': 461 | case 'sub': 462 | case 'mul': 463 | case 'div': 464 | case 'mod': 465 | case 'pow': 466 | foreach ($numbers as &$num) { 467 | if (!isset($num[1])) { 468 | $num[1] = ''; 469 | } 470 | $num[1] = str_pad($num[1], $pad, '0'); 471 | $num = new BigInteger($num[0] . $num[1]); 472 | } 473 | break; 474 | case 'comp': 475 | foreach ($numbers as &$num) { 476 | if (!isset($num[1])) { 477 | $num[1] = ''; 478 | } 479 | $num[1] = str_pad($num[1], $pad, '0'); 480 | } 481 | break; 482 | case 'sqrt': 483 | $numbers = [$arguments[0]]; 484 | } 485 | 486 | $arguments = array_merge($numbers, $ints, [$scale, $pad]); 487 | $result = call_user_func_array(self::class . "::$name", $arguments); 488 | return preg_match('#^-0\.?0*$#', $result) ? substr($result, 1) : $result; 489 | } 490 | } 491 | --------------------------------------------------------------------------------