File indexing completed on 2024-12-22 05:36:50

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Locale
0017  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0019  * @version    $Id$
0020  */
0021 
0022 
0023 /**
0024  * Utility class for proxying math function to bcmath functions, if present,
0025  * otherwise to PHP builtin math operators, with limited detection of overflow conditions.
0026  * Sampling of PHP environments and platforms suggests that at least 80% to 90% support bcmath.
0027  * Thus, this file should be as light as possible.
0028  *
0029  * @category   Zend
0030  * @package    Zend_Locale
0031  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0032  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0033  */
0034 
0035 class Zend_Locale_Math
0036 {
0037     // support unit testing without using bcmath functions
0038     public static $_bcmathDisabled = false;
0039 
0040     public static $add   = array('Zend_Locale_Math', 'Add');
0041     public static $sub   = array('Zend_Locale_Math', 'Sub');
0042     public static $pow   = array('Zend_Locale_Math', 'Pow');
0043     public static $mul   = array('Zend_Locale_Math', 'Mul');
0044     public static $div   = array('Zend_Locale_Math', 'Div');
0045     public static $comp  = array('Zend_Locale_Math', 'Comp');
0046     public static $sqrt  = array('Zend_Locale_Math', 'Sqrt');
0047     public static $mod   = array('Zend_Locale_Math', 'Mod');
0048     public static $scale = 'bcscale';
0049 
0050     public static function isBcmathDisabled()
0051     {
0052         return self::$_bcmathDisabled;
0053     }
0054 
0055     /**
0056      * Surprisingly, the results of this implementation of round()
0057      * prove better than the native PHP round(). For example, try:
0058      *   round(639.795, 2);
0059      *   round(267.835, 2);
0060      *   round(0.302515, 5);
0061      *   round(0.36665, 4);
0062      * then try:
0063      *   Zend_Locale_Math::round('639.795', 2);
0064      */
0065     public static function round($op1, $precision = 0)
0066     {
0067         if (self::$_bcmathDisabled) {
0068             $op1 = round($op1, $precision);
0069             if (strpos((string) $op1, 'E') === false) {
0070                 return self::normalize(round($op1, $precision));
0071             }
0072         }
0073 
0074         if (strpos($op1, 'E') !== false) {
0075             $op1 = self::floatalize($op1);
0076         }
0077 
0078         $op1    = trim(self::normalize($op1));
0079         $length = strlen($op1);
0080         if (($decPos = strpos($op1, '.')) === false) {
0081             $op1 .= '.0';
0082             $decPos = $length;
0083             $length += 2;
0084         }
0085         if ($precision < 0 && abs($precision) > $decPos) {
0086             return '0';
0087         }
0088 
0089         $digitsBeforeDot = $length - ($decPos + 1);
0090         if ($precision >= ($length - ($decPos + 1))) {
0091             return $op1;
0092         }
0093 
0094         if ($precision === 0) {
0095             $triggerPos = 1;
0096             $roundPos   = -1;
0097         } elseif ($precision > 0) {
0098             $triggerPos = $precision + 1;
0099             $roundPos   = $precision;
0100         } else {
0101             $triggerPos = $precision;
0102             $roundPos   = $precision -1;
0103         }
0104 
0105         $triggerDigit = $op1[$triggerPos + $decPos];
0106         if ($precision < 0) {
0107             // zero fill digits to the left of the decimal place
0108             $op1 = substr($op1, 0, $decPos + $precision) . str_pad('', abs($precision), '0');
0109         }
0110 
0111         if ($triggerDigit >= '5') {
0112             if ($roundPos + $decPos == -1) {
0113                 return str_pad('1', $decPos + 1, '0');
0114             }
0115 
0116             $roundUp = str_pad('', $length, '0');
0117             $roundUp[$decPos] = '.';
0118             $roundUp[$roundPos + $decPos] = '1';
0119 
0120             if ($op1 > 0) {
0121                 if (self::$_bcmathDisabled) {
0122                     return Zend_Locale_Math_PhpMath::Add($op1, $roundUp, $precision);
0123                 }
0124                 return self::Add($op1, $roundUp, $precision);
0125             } else {
0126                 if (self::$_bcmathDisabled) {
0127                     return Zend_Locale_Math_PhpMath::Sub($op1, $roundUp, $precision);
0128                 }
0129                 return self::Sub($op1, $roundUp, $precision);
0130             }
0131         } elseif ($precision >= 0) {
0132             return substr($op1, 0, $decPos + ($precision ? $precision + 1: 0));
0133         }
0134 
0135         return (string) $op1;
0136     }
0137 
0138     /**
0139      * Convert a scientific notation to float
0140      * Additionally fixed a problem with PHP <= 5.2.x with big integers
0141      *
0142      * @param string $value
0143      */
0144     public static function floatalize($value)
0145     {
0146         $value = strtoupper($value);
0147         if (strpos($value, 'E') === false) {
0148             return $value;
0149         }
0150 
0151         $number = substr($value, 0, strpos($value, 'E'));
0152         if (strpos($number, '.') !== false) {
0153             $post   = strlen(substr($number, strpos($number, '.') + 1));
0154             $mantis = substr($value, strpos($value, 'E') + 1);
0155             if ($mantis < 0) {
0156                 $post += abs((int) $mantis);
0157             }
0158 
0159             $value = number_format($value, $post, '.', '');
0160         } else {
0161             $value = number_format($value, 0, '.', '');
0162         }
0163 
0164         return $value;
0165     }
0166 
0167     /**
0168      * Normalizes an input to standard english notation
0169      * Fixes a problem of BCMath with setLocale which is PHP related
0170      *
0171      * @param   integer  $value  Value to normalize
0172      * @return  string           Normalized string without BCMath problems
0173      */
0174     public static function normalize($value)
0175     {
0176         $convert = localeconv();
0177         $value = str_replace($convert['thousands_sep'], "",(string) $value);
0178         $value = str_replace($convert['positive_sign'], "", $value);
0179         $value = str_replace($convert['decimal_point'], ".",$value);
0180         if (!empty($convert['negative_sign']) and (strpos($value, $convert['negative_sign']))) {
0181             $value = str_replace($convert['negative_sign'], "", $value);
0182             $value = "-" . $value;
0183         }
0184 
0185         return $value;
0186     }
0187 
0188     /**
0189      * Localizes an input from standard english notation
0190      * Fixes a problem of BCMath with setLocale which is PHP related
0191      *
0192      * @param   integer  $value  Value to normalize
0193      * @return  string           Normalized string without BCMath problems
0194      */
0195     public static function localize($value)
0196     {
0197         $convert = localeconv();
0198         $value = str_replace(".", $convert['decimal_point'], (string) $value);
0199         if (!empty($convert['negative_sign']) and (strpos($value, "-"))) {
0200             $value = str_replace("-", $convert['negative_sign'], $value);
0201         }
0202         return $value;
0203     }
0204 
0205     /**
0206      * Changes exponential numbers to plain string numbers
0207      * Fixes a problem of BCMath with numbers containing exponents
0208      *
0209      * @param integer $value Value to erase the exponent
0210      * @param integer $scale (Optional) Scale to use
0211      * @return string
0212      */
0213     public static function exponent($value, $scale = null)
0214     {
0215         if (!extension_loaded('bcmath')) {
0216             return $value;
0217         }
0218 
0219         $split = explode('e', $value);
0220         if (count($split) == 1) {
0221             $split = explode('E', $value);
0222         }
0223 
0224         if (count($split) > 1) {
0225             $value = bcmul($split[0], bcpow(10, $split[1], $scale), $scale);
0226         }
0227 
0228         return $value;
0229     }
0230 
0231     /**
0232      * BCAdd - fixes a problem of BCMath and exponential numbers
0233      *
0234      * @param  string  $op1
0235      * @param  string  $op2
0236      * @param  integer $scale
0237      * @return string
0238      */
0239     public static function Add($op1, $op2, $scale = null)
0240     {
0241         $op1 = self::exponent($op1, $scale);
0242         $op2 = self::exponent($op2, $scale);
0243 
0244         return bcadd($op1, $op2, $scale);
0245     }
0246 
0247     /**
0248      * BCSub - fixes a problem of BCMath and exponential numbers
0249      *
0250      * @param  string  $op1
0251      * @param  string  $op2
0252      * @param  integer $scale
0253      * @return string
0254      */
0255     public static function Sub($op1, $op2, $scale = null)
0256     {
0257         $op1 = self::exponent($op1, $scale);
0258         $op2 = self::exponent($op2, $scale);
0259         return bcsub($op1, $op2, $scale);
0260     }
0261 
0262     /**
0263      * BCPow - fixes a problem of BCMath and exponential numbers
0264      *
0265      * @param  string  $op1
0266      * @param  string  $op2
0267      * @param  integer $scale
0268      * @return string
0269      */
0270     public static function Pow($op1, $op2, $scale = null)
0271     {
0272         $op1 = self::exponent($op1, $scale);
0273         $op2 = self::exponent($op2, $scale);
0274         return bcpow($op1, $op2, $scale);
0275     }
0276 
0277     /**
0278      * BCMul - fixes a problem of BCMath and exponential numbers
0279      *
0280      * @param  string  $op1
0281      * @param  string  $op2
0282      * @param  integer $scale
0283      * @return string
0284      */
0285     public static function Mul($op1, $op2, $scale = null)
0286     {
0287         $op1 = self::exponent($op1, $scale);
0288         $op2 = self::exponent($op2, $scale);
0289         return bcmul($op1, $op2, $scale);
0290     }
0291 
0292     /**
0293      * BCDiv - fixes a problem of BCMath and exponential numbers
0294      *
0295      * @param  string  $op1
0296      * @param  string  $op2
0297      * @param  integer $scale
0298      * @return string
0299      */
0300     public static function Div($op1, $op2, $scale = null)
0301     {
0302         $op1 = self::exponent($op1, $scale);
0303         $op2 = self::exponent($op2, $scale);
0304         return bcdiv($op1, $op2, $scale);
0305     }
0306 
0307     /**
0308      * BCSqrt - fixes a problem of BCMath and exponential numbers
0309      *
0310      * @param  string  $op1
0311      * @param  integer $scale
0312      * @return string
0313      */
0314     public static function Sqrt($op1, $scale = null)
0315     {
0316         $op1 = self::exponent($op1, $scale);
0317         return bcsqrt($op1, $scale);
0318     }
0319 
0320     /**
0321      * BCMod - fixes a problem of BCMath and exponential numbers
0322      *
0323      * @param  string  $op1
0324      * @param  string  $op2
0325      * @return string
0326      */
0327     public static function Mod($op1, $op2)
0328     {
0329         $op1 = self::exponent($op1);
0330         $op2 = self::exponent($op2);
0331         return bcmod($op1, $op2);
0332     }
0333 
0334     /**
0335      * BCComp - fixes a problem of BCMath and exponential numbers
0336      *
0337      * @param  string  $op1
0338      * @param  string  $op2
0339      * @param  integer $scale
0340      * @return string
0341      */
0342     public static function Comp($op1, $op2, $scale = null)
0343     {
0344         $op1 = self::exponent($op1, $scale);
0345         $op2 = self::exponent($op2, $scale);
0346         return bccomp($op1, $op2, $scale);
0347     }
0348 }
0349 
0350 if (!extension_loaded('bcmath')
0351     || (defined('TESTS_ZEND_LOCALE_BCMATH_ENABLED') && !TESTS_ZEND_LOCALE_BCMATH_ENABLED)
0352 ) {
0353     // require_once 'Zend/Locale/Math/PhpMath.php';
0354     Zend_Locale_Math_PhpMath::disable();
0355 }