Merge pull request #22830 from kurund/entityRef-enhancements
[civicrm-core.git] / Civi / Core / Format.php
index 2ea33946ad2b7bf104f876dae0ed6eb6e81cc5aa..b3076d0e8dc6b6f314c941c47729ec54e4480fc0 100644 (file)
@@ -23,30 +23,27 @@ class Format {
   /**
    * Get formatted money
    *
-   * @param string|int|float $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($amount, ?string $currency = NULL, ?string $locale = NULL): string {
-    // Empty value => empty string
-    if (is_null($amount) || $amount === '' || $amount === FALSE) {
+    if (($amount = $this->checkAndConvertAmount($amount)) === '') {
       return '';
     }
-    // Verify the amount is a number or numeric string/object
-    elseif ($amount === TRUE || !is_numeric((string) $amount)) {
-      throw new \CRM_Core_Exception('Invalid value for type money');
-    }
     if (!$currency) {
       $currency = Civi::settings()->get('defaultCurrency');
     }
     if (!isset($locale)) {
-      $locale = CRM_Core_I18n::getLocale();
+      $locale = Civi::settings()->get('format_locale') ?? CRM_Core_I18n::getLocale();
     }
     $money = Money::of($amount, $currency, NULL, RoundingMode::HALF_UP);
     $formatter = $this->getMoneyFormatter($currency, $locale);
@@ -66,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);
   }
@@ -80,7 +81,7 @@ class Format {
    *
    * @param string|float|int $amount
    * @param string $currency
-   * @param $locale
+   * @param string|null $locale
    *
    * @return string
    *
@@ -88,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);
@@ -106,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,
     ]);
@@ -126,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,
     ]);
@@ -140,7 +150,7 @@ class Format {
    * we are looking at how to manage an 'opt in'
    */
   protected function isUseSeparatorSettings(): bool {
-    return !CRM_Utils_Constant::value('IGNORE_SEPARATOR_CONFIG');
+    return !Civi::settings()->get('format_locale') && !CRM_Utils_Constant::value('IGNORE_SEPARATOR_CONFIG');
   }
 
   /**
@@ -189,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;
+  }
+
 }