Merge pull request #22732 from braders/ui-45-contrib-participant-links
[civicrm-core.git] / Civi / Core / Format.php
index 52d85865a4baebc01e1330e3d3b10a47d6490454..582727cce57a1dfa81fd04bf16e7f2d52b038d35 100644 (file)
@@ -23,17 +23,22 @@ class Format {
   /**
    * Get formatted money
    *
-   * @param string $amount
+   * @param string|int|float|BigDecimal $amount
    * @param string|null $currency
    *   Currency, defaults to site currency if not provided.
    * @param string|null $locale
    *
    * @return string
    *
+   * @throws \CRM_Core_Exception
+   *
    * @noinspection PhpDocMissingThrowsInspection
    * @noinspection PhpUnhandledExceptionInspection
    */
-  public function money(string $amount, ?string $currency = NULL, ?string $locale = NULL): string {
+  public function money($amount, ?string $currency = NULL, ?string $locale = NULL): string {
+    if (($amount = $this->checkAndConvertAmount($amount)) === '') {
+      return '';
+    }
     if (!$currency) {
       $currency = Civi::settings()->get('defaultCurrency');
     }
@@ -58,11 +63,15 @@ class Format {
    *   add any padding.
    *
    * @return string
+   * @throws \CRM_Core_Exception
    */
   public function number($amount, ?string $locale = NULL, array $attributes = [
     NumberFormatter::MIN_FRACTION_DIGITS => 0,
     NumberFormatter::MAX_FRACTION_DIGITS => 8,
   ]): string {
+    if (($amount = $this->checkAndConvertAmount($amount)) === '') {
+      return '';
+    }
     $formatter = $this->getMoneyFormatter(NULL, $locale, NumberFormatter::DECIMAL, $attributes);
     return $formatter->format($amount);
   }
@@ -72,7 +81,7 @@ class Format {
    *
    * @param string|float|int $amount
    * @param string $currency
-   * @param $locale
+   * @param string|null $locale
    *
    * @return string
    *
@@ -80,6 +89,9 @@ class Format {
    * @noinspection PhpUnhandledExceptionInspection
    */
   public function moneyNumber($amount, string $currency, $locale): string {
+    if (($amount = $this->checkAndConvertAmount($amount)) === '') {
+      return '';
+    }
     $formatter = $this->getMoneyFormatter($currency, $locale, NumberFormatter::DECIMAL);
     $money = Money::of($amount, $currency, NULL, RoundingMode::HALF_UP);
     return $money->formatWith($formatter);
@@ -98,6 +110,9 @@ class Format {
    * @noinspection PhpUnhandledExceptionInspection
    */
   public function moneyLong($amount, ?string $currency, ?string $locale): string {
+    if (($amount = $this->checkAndConvertAmount($amount)) === '') {
+      return '';
+    }
     $formatter = $this->getMoneyFormatter($currency, $locale, NumberFormatter::CURRENCY, [
       NumberFormatter::MAX_FRACTION_DIGITS => 9,
     ]);
@@ -118,6 +133,9 @@ class Format {
    * @noinspection PhpUnhandledExceptionInspection
    */
   public function moneyNumberLong($amount, ?string $currency, ?string $locale): string {
+    if (($amount = $this->checkAndConvertAmount($amount)) === '') {
+      return '';
+    }
     $formatter = $this->getMoneyFormatter($currency, $locale, NumberFormatter::DECIMAL, [
       NumberFormatter::MAX_FRACTION_DIGITS => 9,
     ]);
@@ -181,4 +199,28 @@ class Format {
     return \Civi::$statics[$cacheKey];
   }
 
+  /**
+   * Since the input can be various data types and values, we need to handle
+   * them before passing on to the Brick libraries which would throw exceptions
+   * for ones that we are ok just converting to the empty string.
+   *
+   * @param string|int|float|BigDecimal $amount
+   * @return string
+   *   Either the empty string if an empty-ish value, or the original amount as a string.
+   * @throws \CRM_Core_Exception
+   */
+  private function checkAndConvertAmount($amount): string {
+    // Empty value => empty string
+    // FALSE should be an error but some smarty variables are filled with FALSE to avoid ENOTICES.
+    if (is_null($amount) || $amount === '' || $amount === FALSE) {
+      return '';
+    }
+    // Verify the amount is a number or numeric string/object.
+    // We cast to string because it can be a BigDecimal object.
+    if ($amount === TRUE || !is_numeric((string) $amount)) {
+      throw new \CRM_Core_Exception('Invalid value for type money');
+    }
+    return (string) $amount;
+  }
+
 }