| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- <?php
- /**
- * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
- * @link http://cakephp.org CakePHP(tm) Project
- * @since 0.10.0
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
- */
- namespace Cake\Utility;
- use Cake\Error\Exception;
- use NumberFormatter;
- /**
- * Number helper library.
- *
- * Methods to make numbers more readable.
- *
- * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html
- */
- class Number {
- /**
- * Currencies supported by the helper. You can add additional currency formats
- * with Cake\Utility\Number::addFormat
- *
- * @var array
- */
- protected static $_currencies = array(
- 'AUD' => array(
- 'wholeSymbol' => '$', 'wholePosition' => 'before', 'fractionSymbol' => 'c', 'fractionPosition' => 'after',
- 'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true,
- 'fractionExponent' => 2
- ),
- 'CAD' => array(
- 'wholeSymbol' => '$', 'wholePosition' => 'before', 'fractionSymbol' => 'c', 'fractionPosition' => 'after',
- 'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true,
- 'fractionExponent' => 2
- ),
- 'USD' => array(
- 'wholeSymbol' => '$', 'wholePosition' => 'before', 'fractionSymbol' => 'c', 'fractionPosition' => 'after',
- 'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true,
- 'fractionExponent' => 2
- ),
- 'EUR' => array(
- 'wholeSymbol' => '€', 'wholePosition' => 'before', 'fractionSymbol' => false, 'fractionPosition' => 'after',
- 'zero' => 0, 'places' => 2, 'thousands' => '.', 'decimals' => ',', 'negative' => '()', 'escape' => true,
- 'fractionExponent' => 0
- ),
- 'GBP' => array(
- 'wholeSymbol' => '£', 'wholePosition' => 'before', 'fractionSymbol' => 'p', 'fractionPosition' => 'after',
- 'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true,
- 'fractionExponent' => 2
- ),
- 'JPY' => array(
- 'wholeSymbol' => '¥', 'wholePosition' => 'before', 'fractionSymbol' => false, 'fractionPosition' => 'after',
- 'zero' => 0, 'places' => 2, 'thousands' => ',', 'decimals' => '.', 'negative' => '()', 'escape' => true,
- 'fractionExponent' => 0
- ),
- );
- /**
- * A list of number formatters indexed by locale
- *
- * @var array
- */
- protected static $_formatters = [];
- /**
- * A list of currency formatters indexed by locale
- *
- * @var array
- */
- protected static $_currencyFormatters = [];
- /**
- * Default currency used by Number::currency()
- *
- * @var string
- */
- protected static $_defaultCurrency;
- /**
- * Formats a number with a level of precision.
- *
- * @param float $value A floating point number.
- * @param int $precision The precision of the returned number.
- * @return float Formatted float.
- * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::precision
- */
- public static function precision($value, $precision = 3) {
- $locale = ini_get('intl.default_locale') ?: 'en_US';
- if (!isset(static::$_formatters[$locale])) {
- static::$_formatters[$locale] = new NumberFormatter($locale, NumberFormatter::DECIMAL);
- }
- $formatter = static::$_formatters[$locale];
- $formatter->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $precision);
- $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $precision);
- return $formatter->format($value);
- }
- /**
- * Returns a formatted-for-humans file size.
- *
- * @param int $size Size in bytes
- * @return string Human readable size
- * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::toReadableSize
- */
- public static function toReadableSize($size) {
- switch (true) {
- case $size < 1024:
- return __dn('cake', '{0,number,integer} Byte', '{0,number,integer} Bytes', $size, $size);
- case round($size / 1024) < 1024:
- return __d('cake', '{0,number,#,###.##} KB', $size / 1024);
- case round($size / 1024 / 1024, 2) < 1024:
- return __d('cake', '{0,number,#,###.##} MB', $size / 1024 / 1024);
- case round($size / 1024 / 1024 / 1024, 2) < 1024:
- return __d('cake', '{0,number,#,###.##} GB', $size / 1024 / 1024 / 1024);
- default:
- return __d('cake', '{0,number,#,###.##} TB', $size / 1024 / 1024 / 1024 / 1024);
- }
- }
- /**
- * Converts filesize from human readable string to bytes
- *
- * @param string $size Size in human readable string like '5MB', '5M', '500B', '50kb' etc.
- * @param mixed $default Value to be returned when invalid size was used, for example 'Unknown type'
- * @return mixed Number of bytes as integer on success, `$default` on failure if not false
- * @throws \Cake\Error\Exception On invalid Unit type.
- * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::fromReadableSize
- */
- public static function fromReadableSize($size, $default = false) {
- if (ctype_digit($size)) {
- return (int)$size;
- }
- $size = strtoupper($size);
- $l = -2;
- $i = array_search(substr($size, -2), array('KB', 'MB', 'GB', 'TB', 'PB'));
- if ($i === false) {
- $l = -1;
- $i = array_search(substr($size, -1), array('K', 'M', 'G', 'T', 'P'));
- }
- if ($i !== false) {
- $size = substr($size, 0, $l);
- return $size * pow(1024, $i + 1);
- }
- if (substr($size, -1) === 'B' && ctype_digit(substr($size, 0, -1))) {
- $size = substr($size, 0, -1);
- return (int)$size;
- }
- if ($default !== false) {
- return $default;
- }
- throw new Exception('No unit type.');
- }
- /**
- * Formats a number into a percentage string.
- *
- * Options:
- *
- * - `multiply`: Multiply the input value by 100 for decimal percentages.
- *
- * @param float $value A floating point number
- * @param int $precision The precision of the returned number
- * @param array $options Options
- * @return string Percentage string
- * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::toPercentage
- */
- public static function toPercentage($value, $precision = 2, array $options = array()) {
- $options += array('multiply' => false);
- if ($options['multiply']) {
- $value *= 100;
- }
- return static::precision($value, $precision) . '%';
- }
- /**
- * Formats a number into the correct locale format
- *
- * Options:
- *
- * - `places` - Minimim number or decimals to use, e.g 0
- * - `precision` - Maximum Number of decimal places to use, e.g. 2
- * - `locale` - The locale name to use for formatting the number, e.g. fr_FR
- * - `before` - The string to place before whole numbers, e.g. '['
- * - `after` - The string to place after decimal numbers, e.g. ']'
- * - `escape` - Set to false to prevent escaping
- *
- * @param float $value A floating point number.
- * @param array $options An array with options.
- * @return string Formatted number
- * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::format
- */
- public static function format($value, array $options = []) {
- $locale = isset($options['locale']) ? $options['locale'] : ini_get('intl.default_locale');
- if (!$locale) {
- $locale = 'en_US';
- }
- if (!isset(static::$_formatters[$locale])) {
- static::$_formatters[$locale] = new NumberFormatter($locale, NumberFormatter::DECIMAL);
- }
- $formatter = static::$_formatters[$locale];
- $map = [
- 'places' => NumberFormatter::MIN_FRACTION_DIGITS,
- 'precision' => NumberFormatter::MAX_FRACTION_DIGITS
- ];
- foreach ($map as $opt => $setting) {
- if (isset($options[$opt])) {
- $formatter->setAttribute($setting, $options[$opt]);
- }
- }
- $options += ['before' => '', 'after' => '', 'escape' => true];
- $out = $options['before'] . $formatter->format($value) . $options['after'];
- if (!empty($options['escape'])) {
- return h($out);
- }
- return $out;
- }
- /**
- * Formats a number into the correct locale format to show deltas (signed differences in value).
- *
- * ### Options
- *
- * - `places` - Minimim number or decimals to use, e.g 0
- * - `precision` - Maximum Number of decimal places to use, e.g. 2
- * - `locale` - The locale name to use for formatting the number, e.g. fr_FR
- * - `before` - The string to place before whole numbers, e.g. '['
- * - `after` - The string to place after decimal numbers, e.g. ']'
- * - `escape` - Set to false to prevent escaping
- *
- * @param float $value A floating point number
- * @param array $options Options list.
- * @return string formatted delta
- * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::formatDelta
- */
- public static function formatDelta($value, array $options = array()) {
- $options += ['places' => 0];
- $value = number_format($value, $options['places'], '.', '');
- $sign = $value > 0 ? '+' : '';
- $options['before'] = isset($options['before']) ? $options['before'] . $sign : $sign;
- return static::format($value, $options);
- }
- /**
- * Formats a number into a currency format.
- *
- * ### Options
- *
- * - `locale` - The locale name to use for formatting the number, e.g. fr_FR
- * - `fractionSymbol` - The currency symbol to use for fractional numbers.
- * - `fractionPosition` - The position the fraction symbol should be placed
- * valid options are 'before' & 'after'.
- * - `before` - Text to display before the rendered number
- * - `after` - Text to display after the rendered number
- * - `zero` - The text to use for zero values, can be a string or a number. e.g. 0, 'Free!'
- * - `places` - Number of decimal places to use. e.g. 2
- * - `precision` - Maximum Number of decimal places to use, e.g. 2
- * - `pattern` - An ICU number patter to use for formatting the number. e.g #,###.00
- *
- * @param float $value Value to format.
- * @param string $currency International currency name such as 'USD', 'EUR', 'JPY', 'CAD'
- * @param array $options Options list.
- * @return string Number formatted as a currency.
- */
- public static function currency($value, $currency = null, array $options = array()) {
- $value = (float)$value;
- $currency = $currency ?: static::defaultCurrency();
- if (isset($options['zero']) && !$value) {
- return $options['zero'];
- }
- $locale = isset($options['locale']) ? $options['locale'] : ini_get('intl.default_locale');
- if (!$locale) {
- $locale = 'en_US';
- }
- if (!isset(static::$_currencyFormatters[$locale])) {
- static::$_currencyFormatters[$locale] = new NumberFormatter(
- $locale,
- NumberFormatter::CURRENCY
- );
- }
- $formatter = static::$_currencyFormatters[$locale];
- $hasPlaces = isset($options['places']);
- $hasPrecision = isset($options['precision']);
- $hasPattern = !empty($options['pattern']);
- if ($hasPlaces || $hasPrecision || $hasPattern) {
- $formatter = clone $formatter;
- }
- if ($hasPlaces) {
- $formatter->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $options['places']);
- }
- if ($hasPrecision) {
- $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $options['precision']);
- }
- if ($hasPattern) {
- $formatter->setPattern($options['pattern']);
- }
- $abs = abs($value);
- if (!empty($options['fractionSymbol']) && $abs > 0 && $abs < 1) {
- $value = $value * 100;
- $pos = isset($options['fractionPosition']) ? $options['fractionPosition'] : 'after';
- return static::format($value, ['precision' => 0, $pos => $options['fractionSymbol']]);
- }
- $before = isset($options['before']) ? $options['before'] : null;
- $after = isset($options['after']) ? $options['after'] : null;
- return $before . $formatter->formatCurrency($value, $currency) . $after;
- }
- /**
- * Add a currency format to the Number helper. Makes reusing
- * currency formats easier.
- *
- * {{{ $number->addFormat('NOK', array('before' => 'Kr. ')); }}}
- *
- * You can now use `NOK` as a shortform when formatting currency amounts.
- *
- * {{{ $number->currency($value, 'NOK'); }}}
- *
- * Added formats are merged with the defaults defined in Cake\Utility\Number::$_currencyDefaults
- * See Cake\Utility\Number::currency() for more information on the various options and their function.
- *
- * @param string $formatName The format name to be used in the future.
- * @param array $options The array of options for this format.
- * @return void
- * @see NumberHelper::currency()
- * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/number.html#NumberHelper::addFormat
- */
- public static function addFormat($formatName, array $options) {
- static::$_currencies[$formatName] = $options + static::$_currencyDefaults;
- }
- /**
- * Getter/setter for default currency
- *
- * @param string|boolean $currency Default currency string to be used by currency()
- * if $currency argument is not provided. If boolean false is passed, it will clear the
- * currently stored value
- * @return string Currency
- */
- public static function defaultCurrency($currency = null) {
- if (!empty($currency)) {
- return self::$_defaultCurrency = $currency;
- }
- if ($currency === false) {
- self::$_defaultCurrency = null;
- }
- if (empty(self::$_defaultCurrency)) {
- $locale = ini_get('intl.default_locale') ?: 'en_US';
- $formatter = new NumberFormatter($locale, NumberFormatter::CURRENCY);
- self::$_defaultCurrency = $formatter->getTextAttribute(NumberFormatter::CURRENCY_CODE);
- }
- return self::$_defaultCurrency;
- }
- }
|