[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/phortune/currency/ -> PhortuneCurrency.php (source)

   1  <?php
   2  
   3  final class PhortuneCurrency extends Phobject {
   4  
   5    private $value;
   6    private $currency;
   7  
   8    private function __construct() {
   9      // Intentionally private.
  10    }
  11  
  12    public static function getDefaultCurrency() {
  13      return 'USD';
  14    }
  15  
  16    public static function newEmptyCurrency() {
  17      return self::newFromString('0.00 USD');
  18    }
  19  
  20    public static function newFromUserInput(PhabricatorUser $user, $string) {
  21      // Eventually, this might select a default currency based on user settings.
  22      return self::newFromString($string, self::getDefaultCurrency());
  23    }
  24  
  25    public static function newFromString($string, $default = null) {
  26      $matches = null;
  27      $ok = preg_match(
  28        '/^([-$]*(?:\d+)?(?:[.]\d{0,2})?)(?:\s+([A-Z]+))?$/',
  29        trim($string),
  30        $matches);
  31  
  32      if (!$ok) {
  33        self::throwFormatException($string);
  34      }
  35  
  36      $value = $matches[1];
  37  
  38      if (substr_count($value, '-') > 1) {
  39        self::throwFormatException($string);
  40      }
  41  
  42      if (substr_count($value, '$') > 1) {
  43        self::throwFormatException($string);
  44      }
  45  
  46      $value = str_replace('$', '', $value);
  47      $value = (float)$value;
  48      $value = (int)round(100 * $value);
  49  
  50      $currency = idx($matches, 2, $default);
  51      switch ($currency) {
  52        case 'USD':
  53          break;
  54        default:
  55          throw new Exception("Unsupported currency '{$currency}'!");
  56      }
  57  
  58      return self::newFromValueAndCurrency($value, $currency);
  59    }
  60  
  61    public static function newFromValueAndCurrency($value, $currency) {
  62      $obj = new PhortuneCurrency();
  63  
  64      $obj->value = $value;
  65      $obj->currency = $currency;
  66  
  67      return $obj;
  68    }
  69  
  70    public static function newFromList(array $list) {
  71      assert_instances_of($list, 'PhortuneCurrency');
  72  
  73      if (!$list) {
  74        return PhortuneCurrency::newEmptyCurrency();
  75      }
  76  
  77      $total = null;
  78      foreach ($list as $item) {
  79        if ($total === null) {
  80          $total = $item;
  81        } else {
  82          $total = $total->add($item);
  83        }
  84      }
  85  
  86      return $total;
  87    }
  88  
  89    public function formatForDisplay() {
  90      $bare = $this->formatBareValue();
  91      return '$'.$bare.' '.$this->currency;
  92    }
  93  
  94    public function serializeForStorage() {
  95      return $this->formatBareValue().' '.$this->currency;
  96    }
  97  
  98    public function formatBareValue() {
  99      switch ($this->currency) {
 100        case 'USD':
 101          return sprintf('%.02f', $this->value / 100);
 102        default:
 103          throw new Exception(
 104            pht('Unsupported currency ("%s")!', $this->currency));
 105      }
 106    }
 107  
 108    public function getValue() {
 109      return $this->value;
 110    }
 111  
 112    public function getCurrency() {
 113      return $this->currency;
 114    }
 115  
 116    public function getValueInUSDCents() {
 117      if ($this->currency !== 'USD') {
 118        throw new Exception(pht('Unexpected currency!'));
 119      }
 120      return $this->value;
 121    }
 122  
 123    private static function throwFormatException($string) {
 124      throw new Exception("Invalid currency format ('{$string}').");
 125    }
 126  
 127    private function throwUnlikeCurrenciesException(PhortuneCurrency $other) {
 128      throw new Exception(
 129        pht(
 130          'Trying to operate on unlike currencies ("%s" and "%s")!',
 131          $this->currency,
 132          $other->currency));
 133    }
 134  
 135    public function add(PhortuneCurrency $other) {
 136      if ($this->currency !== $other->currency) {
 137        $this->throwUnlikeCurrenciesException($other);
 138      }
 139  
 140      $currency = new PhortuneCurrency();
 141  
 142      // TODO: This should check for integer overflows, etc.
 143      $currency->value = $this->value + $other->value;
 144      $currency->currency = $this->currency;
 145  
 146      return $currency;
 147    }
 148  
 149    public function subtract(PhortuneCurrency $other) {
 150      if ($this->currency !== $other->currency) {
 151        $this->throwUnlikeCurrenciesException($other);
 152      }
 153  
 154      $currency = new PhortuneCurrency();
 155  
 156      // TODO: This should check for integer overflows, etc.
 157      $currency->value = $this->value - $other->value;
 158      $currency->currency = $this->currency;
 159  
 160      return $currency;
 161    }
 162  
 163    public function isEqualTo(PhortuneCurrency $other) {
 164      if ($this->currency !== $other->currency) {
 165        $this->throwUnlikeCurrenciesException($other);
 166      }
 167  
 168      return ($this->value === $other->value);
 169    }
 170  
 171    public function negate() {
 172      $currency = new PhortuneCurrency();
 173      $currency->value = -$this->value;
 174      $currency->currency = $this->currency;
 175      return $currency;
 176    }
 177  
 178    public function isPositive() {
 179      return ($this->value > 0);
 180    }
 181  
 182    public function isGreaterThan(PhortuneCurrency $other) {
 183      if ($this->currency !== $other->currency) {
 184        $this->throwUnlikeCurrenciesException($other);
 185      }
 186      return $this->value > $other->value;
 187    }
 188  
 189    /**
 190     * Assert that a currency value lies within a range.
 191     *
 192     * Throws if the value is not between the minimum and maximum, inclusive.
 193     *
 194     * In particular, currency values can be negative (to represent a debt or
 195     * credit), so checking against zero may be useful to make sure a value
 196     * has the expected sign.
 197     *
 198     * @param string|null Currency string, or null to skip check.
 199     * @param string|null Currency string, or null to skip check.
 200     * @return this
 201     */
 202    public function assertInRange($minimum, $maximum) {
 203      if ($minimum !== null && $maximum !== null) {
 204        $min = PhortuneCurrency::newFromString($minimum);
 205        $max = PhortuneCurrency::newFromString($maximum);
 206        if ($min->value > $max->value) {
 207          throw new Exception(
 208            pht(
 209              'Range (%s - %s) is not valid!',
 210              $min->formatForDisplay(),
 211              $max->formatForDisplay()));
 212        }
 213      }
 214  
 215      if ($minimum !== null) {
 216        $min = PhortuneCurrency::newFromString($minimum);
 217        if ($min->value > $this->value) {
 218          throw new Exception(
 219            pht(
 220              'Minimum allowed amount is %s.',
 221              $min->formatForDisplay()));
 222        }
 223      }
 224  
 225      if ($maximum !== null) {
 226        $max = PhortuneCurrency::newFromString($maximum);
 227        if ($max->value < $this->value) {
 228          throw new Exception(
 229            pht(
 230              'Maximum allowed amount is %s.',
 231              $max->formatForDisplay()));
 232        }
 233      }
 234  
 235      return $this;
 236    }
 237  
 238  
 239  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1