233 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			233 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * Money Formatter
 | |
|  *
 | |
|  * Formats an amount of money in various ways, according to the provided currency.
 | |
|  *
 | |
|  * @package   easy-digital-downloads
 | |
|  * @copyright Copyright (c) 2021, Sandhills Development, LLC
 | |
|  * @license   GPL2+
 | |
|  * @since     3.0
 | |
|  */
 | |
| 
 | |
| namespace EDD\Currency;
 | |
| 
 | |
| class Money_Formatter {
 | |
| 
 | |
| 	/**
 | |
| 	 * @var int|float Current working amount.
 | |
| 	 */
 | |
| 	public $amount;
 | |
| 
 | |
| 	/**
 | |
| 	 * @var float Typed amount.
 | |
| 	 */
 | |
| 	public $typed_amount;
 | |
| 
 | |
| 	/**
 | |
| 	 * @var int|float Original, unmodified amount passed in via constructor.
 | |
| 	 */
 | |
| 	private $original_amount;
 | |
| 
 | |
| 	/**
 | |
| 	 * @var Currency
 | |
| 	 */
 | |
| 	private $currency;
 | |
| 
 | |
| 	/**
 | |
| 	 * Money_Formatter constructor.
 | |
| 	 *
 | |
| 	 * @param          $amount
 | |
| 	 * @param Currency $currency
 | |
| 	 */
 | |
| 	public function __construct( $amount, Currency $currency ) {
 | |
| 		$this->original_amount = $amount;
 | |
| 		$this->amount          = $amount;
 | |
| 		$this->typed_amount    = $amount;
 | |
| 		$this->currency        = $currency;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Un-formats an amount.
 | |
| 	 * This ensures the amount is put into a state where we can perform mathematical
 | |
| 	 * operations on it --- that means using `.` as the decimal separator and no
 | |
| 	 * thousands separator.
 | |
| 	 *
 | |
| 	 * @return float|int
 | |
| 	 */
 | |
| 	private function unformat() {
 | |
| 		$amount = $this->original_amount;
 | |
| 
 | |
| 		$sep_found = strpos( $amount, $this->currency->decimal_separator );
 | |
| 		if ( ',' === $this->currency->decimal_separator && false !== $sep_found ) {
 | |
| 			$whole  = substr( $amount, 0, $sep_found );
 | |
| 			$part   = substr( $amount, $sep_found + 1, ( strlen( $amount ) - 1 ) );
 | |
| 			$amount = $whole . '.' . $part;
 | |
| 		}
 | |
| 
 | |
| 		// Strip "," and " " from the amount (if set as the thousands separator).
 | |
| 		foreach ( array( ',', ' ' ) as $thousands_separator ) {
 | |
| 			if ( $thousands_separator === $this->currency->thousands_separator && false !== strpos( $amount, $this->currency->thousands_separator ) ) {
 | |
| 				$amount = str_replace( $thousands_separator, '', $amount );
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return $amount;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns the number of decimals ot use for the formatted amount.
 | |
| 	 *
 | |
| 	 * Based on the currency code used when instantiating the class, determines how many
 | |
| 	 * decimal points the value should have once formatted.
 | |
| 	 *
 | |
| 	 * @param bool  $decimals If we should include decimals or not in the formatted amount.
 | |
| 	 * @param float $amount   The amount to format.
 | |
| 	 *
 | |
| 	 * @return int  The number of decimals places to use when formatting the amount.
 | |
| 	 */
 | |
| 	private function get_decimals( $decimals, $amount ) {
 | |
| 		/**
 | |
| 		 * Filter number of decimals to use for formatted amount
 | |
| 		 *
 | |
| 		 * @since unknown
 | |
| 		 *
 | |
| 		 * @param int        $number        Default 2. Number of decimals.
 | |
| 		 * @param int|string $amount        Amount being formatted.
 | |
| 		 * @param string     $currency_code Currency code being formatted.
 | |
| 		 */
 | |
| 		return apply_filters( 'edd_format_amount_decimals', $decimals ? $this->currency->number_decimals : 0, $amount, $this->currency->code );
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Formats the amount for display.
 | |
| 	 * Does not apply the currency code.
 | |
| 	 *
 | |
| 	 * @since 3.0
 | |
| 	 *
 | |
| 	 * @param bool $decimals If we should include decimal places or not when formatting.
 | |
| 	 *
 | |
| 	 * @return Money_Formatter
 | |
| 	 */
 | |
| 	public function format_for_display( $decimals = true ) {
 | |
| 		$amount = $this->unformat();
 | |
| 
 | |
| 		if ( empty( $amount ) ) {
 | |
| 			$amount = 0;
 | |
| 		}
 | |
| 
 | |
| 		$decimals = $this->get_decimals( $decimals, $amount );
 | |
| 
 | |
| 		// Format amount using decimals and separators (also rounds up or down).
 | |
| 		$formatted = number_format( (float) $amount, $decimals, $this->currency->decimal_separator, $this->currency->thousands_separator );
 | |
| 
 | |
| 		/**
 | |
| 		 * Filter the formatted amount before returning
 | |
| 		 *
 | |
| 		 * @since unknown
 | |
| 		 *
 | |
| 		 * @param mixed  $formatted           Formatted amount.
 | |
| 		 * @param mixed  $amount              Original amount.
 | |
| 		 * @param int    $decimals            Default 2. Number of decimals.
 | |
| 		 * @param string $decimal_separator   Default '.'. Decimal separator.
 | |
| 		 * @param string $thousands_separator Default ','. Thousands separator.
 | |
| 		 * @param string $currency_code       Currency used for formatting.
 | |
| 		 */
 | |
| 		$this->amount = apply_filters( 'edd_format_amount', $formatted, $amount, $decimals, $this->currency->decimal_separator, $this->currency->thousands_separator, $this->currency->code );
 | |
| 
 | |
| 		return $this;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Formats the amount for typed data returns.
 | |
| 	 * Does not apply the currency code and returns a foat instead of a string.
 | |
| 	 *
 | |
| 	 * @since 3.0
 | |
| 	 *
 | |
| 	 * @param bool $decimals If we should include decimal places or not when formatting.
 | |
| 	 *
 | |
| 	 * @return Money_Formatter
 | |
| 	 */
 | |
| 	public function format_for_typed( $decimals = true ) {
 | |
| 		$amount = $this->unformat();
 | |
| 
 | |
| 		if ( empty( $amount ) ) {
 | |
| 			$amount = 0;
 | |
| 		}
 | |
| 
 | |
| 		$decimals = $this->get_decimals( $decimals, $amount );
 | |
| 
 | |
| 		/**
 | |
| 		 * Since we want to return a float value here, intentionally only supply a decimal separator.
 | |
| 		 *
 | |
| 		 * The separators here are hard coded intentionally as we're looking to get truncated, raw format of float
 | |
| 		 * which requires '.' for decimal separators and no thousands separator.
 | |
| 		 *
 | |
| 		 * This is also intentionally not filtered for the time being.
 | |
| 		 */
 | |
| 		$formatted = floatval( number_format( (float) $amount, $decimals, '.', '' ) );
 | |
| 
 | |
| 		// Set the amount to $this->amount.
 | |
| 		$this->typed_amount = $formatted;
 | |
| 
 | |
| 		return $this;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Applies the currency prefix/suffix to the amount.
 | |
| 	 *
 | |
| 	 * @since 3.0
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	public function apply_symbol() {
 | |
| 		$amount      = $this->amount;
 | |
| 		$is_negative = is_numeric( $this->amount ) && $this->amount < 0;
 | |
| 
 | |
| 		// Remove "-" from start.
 | |
| 		if ( $is_negative ) {
 | |
| 			$amount = substr( $amount, 1 );
 | |
| 		}
 | |
| 
 | |
| 		$formatted = '';
 | |
| 		if ( ! empty( $this->currency->prefix ) ) {
 | |
| 			$formatted .= $this->currency->prefix;
 | |
| 		}
 | |
| 
 | |
| 		$formatted .= $amount;
 | |
| 
 | |
| 		if ( ! empty( $this->currency->suffix ) ) {
 | |
| 			$formatted .= $this->currency->suffix;
 | |
| 		}
 | |
| 
 | |
| 		if ( ! empty( $this->currency->prefix ) ) {
 | |
| 			/**
 | |
| 			 * Filters the output with a prefix.
 | |
| 			 *
 | |
| 			 * @param string $formatted
 | |
| 			 * @param string $currency_code
 | |
| 			 * @param string $amount
 | |
| 			 */
 | |
| 			$formatted = apply_filters( 'edd_' . strtolower( $this->currency->code ) . '_currency_filter_before', $formatted, $this->currency->code, $amount );
 | |
| 		}
 | |
| 
 | |
| 		if ( ! empty( $this->currency->suffix ) ) {
 | |
| 			/**
 | |
| 			 * Filters the output with a suffix.
 | |
| 			 *
 | |
| 			 * @param string $formatted
 | |
| 			 * @param string $currency_code
 | |
| 			 * @param string $amount
 | |
| 			 */
 | |
| 			$formatted = apply_filters( 'edd_' . strtolower( $this->currency->code ) . '_currency_filter_after', $formatted, $this->currency->code, $amount );
 | |
| 		}
 | |
| 
 | |
| 		// Add the "-" sign back to the start of the string.
 | |
| 		if ( $is_negative ) {
 | |
| 			$formatted = '-' . $formatted;
 | |
| 		}
 | |
| 
 | |
| 		return $formatted;
 | |
| 	}
 | |
| 
 | |
| }
 |