Overview

Packages

  • application
    • commands
    • components
      • actions
      • filters
      • leftWidget
      • permissions
      • sortableWidget
      • util
      • webupdater
      • x2flow
        • actions
        • triggers
      • X2GridView
      • X2Settings
    • controllers
    • models
      • embedded
    • modules
      • accounts
        • controllers
        • models
      • actions
        • controllers
        • models
      • calendar
        • controllers
        • models
      • charts
        • models
      • contacts
        • controllers
        • models
      • docs
        • components
        • controllers
        • models
      • groups
        • controllers
        • models
      • marketing
        • components
        • controllers
        • models
      • media
        • controllers
        • models
      • mobile
        • components
      • opportunities
        • controllers
        • models
      • products
        • controllers
        • models
      • quotes
        • controllers
        • models
      • services
        • controllers
        • models
      • template
        • models
      • users
        • controllers
        • models
      • workflow
        • controllers
        • models
      • x2Leads
        • controllers
        • models
  • Net
  • None
  • PHP
  • system
    • base
    • caching
      • dependencies
    • collections
    • console
    • db
      • ar
      • schema
        • cubrid
        • mssql
        • mysql
        • oci
        • pgsql
        • sqlite
    • i18n
      • gettext
    • logging
    • test
    • utils
    • validators
    • web
      • actions
      • auth
      • filters
      • form
      • helpers
      • renderers
      • services
      • widgets
        • captcha
        • pagers
  • Text
    • Highlighter
  • zii
    • behaviors
    • widgets
      • grid
      • jui

Classes

  • CChoiceFormat
  • CDateFormatter
  • CDbMessageSource
  • CGettextMessageSource
  • CLocale
  • CMessageSource
  • CMissingTranslationEvent
  • CNumberFormatter
  • CPhpMessageSource
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * CNumberFormatter class file.
  4:  *
  5:  * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
  6:  * @author Qiang Xue <qiang.xue@gmail.com>
  7:  * @link http://www.yiiframework.com/
  8:  * @copyright 2008-2013 Yii Software LLC
  9:  * @license http://www.yiiframework.com/license/
 10:  */
 11: 
 12: /**
 13:  * CNumberFormatter provides number localization functionalities.
 14:  *
 15:  * CNumberFormatter formats a number (integer or float) and outputs a string
 16:  * based on the specified format. A CNumberFormatter instance is associated with a locale,
 17:  * and thus generates the string representation of the number in a locale-dependent fashion.
 18:  *
 19:  * CNumberFormatter currently supports currency format, percentage format, decimal format,
 20:  * and custom format. The first three formats are specified in the locale data, while the custom
 21:  * format allows you to enter an arbitrary format string.
 22:  *
 23:  * A format string may consist of the following special characters:
 24:  * <ul>
 25:  * <li>dot (.): the decimal point. It will be replaced with the localized decimal point.</li>
 26:  * <li>comma (,): the grouping separator. It will be replaced with the localized grouping separator.</li>
 27:  * <li>zero (0): required digit. This specifies the places where a digit must appear (will pad 0 if not).</li>
 28:  * <li>hash (#): optional digit. This is mainly used to specify the location of decimal point and grouping separators.</li>
 29:  * <li>currency (¤): the currency placeholder. It will be replaced with the localized currency symbol.</li>
 30:  * <li>percentage (%): the percentage mark. If appearing, the number will be multiplied by 100 before being formatted.</li>
 31:  * <li>permillage (‰): the permillage mark. If appearing, the number will be multiplied by 1000 before being formatted.</li>
 32:  * <li>semicolon (;): the character separating positive and negative number sub-patterns.</li>
 33:  * </ul>
 34:  *
 35:  * Anything surrounding the pattern (or sub-patterns) will be kept.
 36:  *
 37:  * The followings are some examples:
 38:  * <pre>
 39:  * Pattern "#,##0.00" will format 12345.678 as "12,345.68".
 40:  * Pattern "#,#,#0.00" will format 12345.6 as "1,2,3,45.60".
 41:  * </pre>
 42:  * Note, in the first example, the number is rounded first before applying the formatting.
 43:  * And in the second example, the pattern specifies two grouping sizes.
 44:  *
 45:  * CNumberFormatter attempts to implement number formatting according to
 46:  * the {@link http://www.unicode.org/reports/tr35/ Unicode Technical Standard #35}.
 47:  * The following features are NOT implemented:
 48:  * <ul>
 49:  * <li>significant digit</li>
 50:  * <li>scientific format</li>
 51:  * <li>arbitrary literal characters</li>
 52:  * <li>arbitrary padding</li>
 53:  * </ul>
 54:  *
 55:  * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
 56:  * @author Qiang Xue <qiang.xue@gmail.com>
 57:  * @package system.i18n
 58:  * @since 1.0
 59:  */
 60: class CNumberFormatter extends CComponent
 61: {
 62:     private $_locale;
 63:     private $_formats=array();
 64: 
 65:     /**
 66:      * Constructor.
 67:      * @param mixed $locale locale ID (string) or CLocale instance
 68:      */
 69:     public function __construct($locale)
 70:     {
 71:         if(is_string($locale))
 72:             $this->_locale=CLocale::getInstance($locale);
 73:         else
 74:             $this->_locale=$locale;
 75:     }
 76: 
 77:     /**
 78:      * Formats a number based on the specified pattern.
 79:      * Note, if the format contains '%', the number will be multiplied by 100 first.
 80:      * If the format contains '‰', the number will be multiplied by 1000.
 81:      * If the format contains currency placeholder, it will be replaced by
 82:      * the specified localized currency symbol.
 83:      * @param string $pattern format pattern
 84:      * @param mixed $value the number to be formatted
 85:      * @param string $currency 3-letter ISO 4217 code. For example, the code "USD" represents the US Dollar and "EUR" represents the Euro currency.
 86:      * The currency placeholder in the pattern will be replaced with the currency symbol.
 87:      * If null, no replacement will be done.
 88:      * @return string the formatting result.
 89:      */
 90:     public function format(
 91:         $pattern,$value,$currency=null/* x2modstart */,$negativePrefix=null/* x2modend */)
 92:     {
 93:         $format=$this->parseFormat($pattern);
 94:         if ($negativePrefix !== null) {
 95:             $format['negativePrefix'] = $negativePrefix;
 96:             $format['negativeSuffix'] = '';
 97:         }
 98:         $result=$this->formatNumber($format,$value);
 99:         if($currency===null)
100:             return $result;
101:         elseif(($symbol=$this->_locale->getCurrencySymbol($currency))===null)
102:             $symbol=$currency;
103:         return str_replace('¤',$symbol,$result);
104:     }
105: 
106:     /**
107:      * Formats a number using the currency format defined in the locale.
108:      * @param mixed $value the number to be formatted
109:      * @param string $currency 3-letter ISO 4217 code. For example, the code "USD" represents the US Dollar and "EUR" represents the Euro currency.
110:      * The currency placeholder in the pattern will be replaced with the currency symbol.
111:      * @return string the formatting result.
112:      */
113:     public function formatCurrency(
114:         $value,$currency/* x2modstart */,$negativePrefix=null/* x2modend */)
115:     {
116:         return $this->format(
117:             $this->_locale->getCurrencyFormat(),$value,
118:             $currency/* x2modstart */,$negativePrefix/* x2modend */);
119:     }
120: 
121:     /**
122:      * Formats a number using the percentage format defined in the locale.
123:      * Note, if the percentage format contains '%', the number will be multiplied by 100 first.
124:      * If the percentage format contains '‰', the number will be multiplied by 1000.
125:      * @param mixed $value the number to be formatted
126:      * @return string the formatting result.
127:      */
128:     public function formatPercentage($value)
129:     {
130:         return $this->format($this->_locale->getPercentFormat(),$value);
131:     }
132: 
133:     /**
134:      * Formats a number using the decimal format defined in the locale.
135:      * @param mixed $value the number to be formatted
136:      * @return string the formatting result.
137:      */
138:     public function formatDecimal($value)
139:     {
140:         return $this->format($this->_locale->getDecimalFormat(),$value);
141:     }
142: 
143:     /**
144:      * Formats a number based on a format.
145:      * This is the method that does actual number formatting.
146:      * @param array $format format with the following structure:
147:      * <pre>
148:      * array(
149:      *  // number of required digits after the decimal point,
150:      *  // will be padded with 0 if not enough digits,
151:      *  // -1 means we should drop the decimal point
152:      *  'decimalDigits'=>2,
153:      *  // maximum number of digits after the decimal point,
154:      *  // additional digits will be truncated.
155:      *  'maxDecimalDigits'=>3,
156:      *  // number of required digits before the decimal point,
157:      *  // will be padded with 0 if not enough digits
158:      *  'integerDigits'=>1,
159:      *  // the primary grouping size, 0 means no grouping
160:      *  'groupSize1'=>3,
161:      *  // the secondary grouping size, 0 means no secondary grouping
162:      *  'groupSize2'=>0,
163:      *  'positivePrefix'=>'+',  // prefix to positive number
164:      *  'positiveSuffix'=>'',   // suffix to positive number
165:      *  'negativePrefix'=>'(',  // prefix to negative number
166:      *  'negativeSuffix'=>')',  // suffix to negative number
167:      *  'multiplier'=>1,        // 100 for percent, 1000 for per mille
168:      * );
169:      * </pre>
170:      * @param mixed $value the number to be formatted
171:      * @return string the formatted result
172:      */
173:     protected function formatNumber($format,$value)
174:     {
175:         $negative=$value<0;
176:         $value=abs($value*$format['multiplier']);
177:         if($format['maxDecimalDigits']>=0)
178:             $value=number_format($value,$format['maxDecimalDigits'],'.','');
179:         $value="$value";
180:         if(false !== $pos=strpos($value,'.'))
181:         {
182:             $integer=substr($value,0,$pos);
183:             $decimal=substr($value,$pos+1);
184:         }
185:         else
186:         {
187:             $integer=$value;
188:             $decimal='';
189:         }
190:         if($format['decimalDigits']>strlen($decimal))
191:             $decimal=str_pad($decimal,$format['decimalDigits'],'0');
192:         elseif($format['decimalDigits']<strlen($decimal))
193:         {
194:             $decimal_temp='';
195:             for($i=strlen($decimal)-1;$i>=0;$i--)
196:                 if($decimal[$i]!=='0' || strlen($decimal_temp)>0)
197:                     $decimal_temp=$decimal[$i].$decimal_temp;
198:             $decimal=$decimal_temp;
199:         }
200:         if(strlen($decimal)>0)
201:             $decimal=$this->_locale->getNumberSymbol('decimal').$decimal;
202: 
203:         $integer=str_pad($integer,$format['integerDigits'],'0',STR_PAD_LEFT);
204:         if($format['groupSize1']>0 && strlen($integer)>$format['groupSize1'])
205:         {
206:             $str1=substr($integer,0,-$format['groupSize1']);
207:             $str2=substr($integer,-$format['groupSize1']);
208:             $size=$format['groupSize2']>0?$format['groupSize2']:$format['groupSize1'];
209:             $str1=str_pad($str1,(int)((strlen($str1)+$size-1)/$size)*$size,' ',STR_PAD_LEFT);
210:             $integer=ltrim(implode($this->_locale->getNumberSymbol('group'),str_split($str1,$size))).$this->_locale->getNumberSymbol('group').$str2;
211:         }
212: 
213:         if($negative)
214:             $number=$format['negativePrefix'].$integer.$decimal.$format['negativeSuffix'];
215:         else
216:             $number=$format['positivePrefix'].$integer.$decimal.$format['positiveSuffix'];
217: 
218:         return strtr($number,array('%'=>$this->_locale->getNumberSymbol('percentSign'),'‰'=>$this->_locale->getNumberSymbol('perMille')));
219:     }
220: 
221:     /**
222:      * Parses a given string pattern.
223:      * @param string $pattern the pattern to be parsed
224:      * @return array the parsed pattern
225:      * @see formatNumber
226:      */
227:     protected function parseFormat($pattern)
228:     {
229:         if(isset($this->_formats[$pattern]))
230:             return $this->_formats[$pattern];
231: 
232:         $format=array();
233: 
234:         // find out prefix and suffix for positive and negative patterns
235:         $patterns=explode(';',$pattern);
236:         $format['positivePrefix']=$format['positiveSuffix']=$format['negativePrefix']=$format['negativeSuffix']='';
237:         if(preg_match('/^(.*?)[#,\.0]+(.*?)$/',$patterns[0],$matches))
238:         {
239:             $format['positivePrefix']=$matches[1];
240:             $format['positiveSuffix']=$matches[2];
241:         }
242: 
243:         if(isset($patterns[1]) && preg_match('/^(.*?)[#,\.0]+(.*?)$/',$patterns[1],$matches))  // with a negative pattern
244:         {
245:             $format['negativePrefix']=$matches[1];
246:             $format['negativeSuffix']=$matches[2];
247:         }
248:         else
249:         {
250:             $format['negativePrefix']=$this->_locale->getNumberSymbol('minusSign').$format['positivePrefix'];
251:             $format['negativeSuffix']=$format['positiveSuffix'];
252:         }
253:         $pat=$patterns[0];
254: 
255:         // find out multiplier
256:         if(strpos($pat,'%')!==false)
257:             $format['multiplier']=100;
258:         elseif(strpos($pat,'‰')!==false)
259:             $format['multiplier']=1000;
260:         else
261:             $format['multiplier']=1;
262: 
263:         // find out things about decimal part
264:         if(($pos=strpos($pat,'.'))!==false)
265:         {
266:             if(($pos2=strrpos($pat,'0'))>$pos)
267:                 $format['decimalDigits']=$pos2-$pos;
268:             else
269:                 $format['decimalDigits']=0;
270:             if(($pos3=strrpos($pat,'#'))>=$pos2)
271:                 $format['maxDecimalDigits']=$pos3-$pos;
272:             else
273:                 $format['maxDecimalDigits']=$format['decimalDigits'];
274:             $pat=substr($pat,0,$pos);
275:         }
276:         else   // no decimal part
277:         {
278:             $format['decimalDigits']=0;
279:             $format['maxDecimalDigits']=0;
280:         }
281: 
282:         // find out things about integer part
283:         $p=str_replace(',','',$pat);
284:         if(($pos=strpos($p,'0'))!==false)
285:             $format['integerDigits']=strrpos($p,'0')-$pos+1;
286:         else
287:             $format['integerDigits']=0;
288:         // find out group sizes. some patterns may have two different group sizes
289:         $p=str_replace('#','0',$pat);
290:         if(($pos=strrpos($pat,','))!==false)
291:         {
292:             $format['groupSize1']=strrpos($p,'0')-$pos;
293:             if(($pos2=strrpos(substr($p,0,$pos),','))!==false)
294:                 $format['groupSize2']=$pos-$pos2-1;
295:             else
296:                 $format['groupSize2']=0;
297:         }
298:         else
299:             $format['groupSize1']=$format['groupSize2']=0;
300: 
301:         return $this->_formats[$pattern]=$format;
302:     }
303: }
304: 
API documentation generated by ApiGen 2.8.0