350 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			350 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * Formatting functions for taking care of proper number formats and such
 | |
|  *
 | |
|  * @package     EDD
 | |
|  * @subpackage  Functions/Formatting
 | |
|  * @copyright   Copyright (c) 2018, Easy Digital Downloads, LLC
 | |
|  * @license     http://opensource.org/licenses/gpl-2.0.php GNU Public License
 | |
|  * @since       1.2
 | |
| */
 | |
| 
 | |
| // Exit if accessed directly
 | |
| defined( 'ABSPATH' ) || exit;
 | |
| 
 | |
| /**
 | |
|  * Sanitize a numeric value.
 | |
|  *
 | |
|  * Use this function to "unformat" a previously formatted numeric value.
 | |
|  *
 | |
|  * (Most commonly, this is when accepting input from a form field where the
 | |
|  * value is likely to derived from the site or user preferences.)
 | |
|  *
 | |
|  * @since 1.0
 | |
|  *
 | |
|  * @param mixed $amount Default 0. Numeric amount to sanitize.
 | |
|  *
 | |
|  * @return string $amount Newly sanitized amount.
 | |
|  */
 | |
| function edd_sanitize_amount( $amount = 0 ) {
 | |
| 
 | |
| 	// Get separators
 | |
| 	$decimal_sep   = edd_get_option( 'decimal_separator',   '.' );
 | |
| 	$thousands_sep = edd_get_option( 'thousands_separator', ',' );
 | |
| 
 | |
| 	// Look for separators in amount
 | |
| 	$found_decimal   = strpos( $amount, $decimal_sep   );
 | |
| 	$found_thousands = strpos( $amount, $thousands_sep );
 | |
| 
 | |
| 	// Amount contains comma as decimal separator
 | |
| 	if ( ( $decimal_sep === ',' ) && ( false !== $found_decimal ) ) {
 | |
| 
 | |
| 		// Amount contains period or space as thousands separator
 | |
| 		if ( in_array( $thousands_sep, array( '.', ' ' ), true ) && ( false !== $found_thousands ) ) {
 | |
| 			$amount = str_replace( $thousands_sep, '', $amount );
 | |
| 
 | |
| 		// Amount contains period
 | |
| 		} elseif ( empty( $thousands_sep ) && ( false !== strpos( $amount, '.' ) ) ) {
 | |
| 			$amount = str_replace( '.', '', $amount );
 | |
| 		}
 | |
| 
 | |
| 		$amount = str_replace( $decimal_sep, '.', $amount );
 | |
| 
 | |
| 	// Amount contains comma as thousands separator
 | |
| 	} elseif ( ( $thousands_sep === ',' ) && ( false !== $found_thousands ) ) {
 | |
| 		$amount = str_replace( $thousands_sep, '', $amount );
 | |
| 	}
 | |
| 
 | |
| 	// Remove anything that's not a number, period, or negative sign.
 | |
| 	$amount = preg_replace( '/[^0-9\.\-]/', '', $amount );
 | |
| 
 | |
| 	// Check if negative.
 | |
| 	$negative_exponent = ( $amount < 0 )
 | |
| 		? -1
 | |
| 		: 1;
 | |
| 
 | |
| 	// Cast the amount to an absolute value.
 | |
| 	$amount = '' === $amount ? 0 : abs( (float) $amount );
 | |
| 
 | |
| 	/**
 | |
| 	 * Filter number of decimals to use for sanitized amount
 | |
| 	 *
 | |
| 	 * @since unknown
 | |
| 	 *
 | |
| 	 * @param int        $number Default 2. Number of decimals.
 | |
| 	 * @param int|string $amount Amount being sanitized.
 | |
| 	 */
 | |
| 	$decimals = apply_filters( 'edd_sanitize_amount_decimals', 2, $amount );
 | |
| 
 | |
| 	// Flip back to negative
 | |
| 	$sanitized = $amount * $negative_exponent;
 | |
| 
 | |
| 	// Format amount using decimals and a period for the decimal separator
 | |
| 	// (no thousands separator; also rounds up or down)
 | |
| 	$sanitized = number_format( (float) $sanitized, $decimals, '.', '' );
 | |
| 
 | |
| 	/**
 | |
| 	 * Filter the sanitized amount before returning
 | |
| 	 *
 | |
| 	 * @since unknown
 | |
| 	 *
 | |
| 	 * @param mixed  $sanitized     Sanitized amount.
 | |
| 	 * @param mixed  $amount        Original amount.
 | |
| 	 * @param int    $decimals      Default 2. Number of decimals.
 | |
| 	 * @param string $decimal_sep   Default '.'. Decimal separator.
 | |
| 	 * @param string $thousands_sep Default ','. Thousands separator.
 | |
| 	 */
 | |
| 	return apply_filters( 'edd_sanitize_amount', $sanitized, $amount, $decimals, $decimal_sep, $thousands_sep );
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Format a numeric value.
 | |
|  *
 | |
|  * Uses the decimal & thousands separator settings, and the number of decimals,
 | |
|  * to format any numeric value.
 | |
|  *
 | |
|  * (Most commonly, this is used to apply site or user preferences to a numeric
 | |
|  * value for output to the page.)
 | |
|  *
 | |
|  * @since 1.0
 | |
|  * @since 3.0 Added `$currency` parameter.
 | |
|  *
 | |
|  * @param mixed  $amount   Default 0. Numeric amount to format.
 | |
|  * @param string $decimals Default true. Whether or not to use decimals. Useful when set to false for non-currency numbers.
 | |
|  * @param string $currency Currency code to format the amount for. This determines how many decimals are used.
 | |
|  *                         If omitted, site-wide currency is used.
 | |
|  * @param string $context  Defines the context in which we are formatting the data (formatted), for display or for data useage like API (typed).
 | |
|  *
 | |
|  * @return string $amount Newly formatted amount or Price Not Available
 | |
|  */
 | |
| function edd_format_amount( $amount = 0, $decimals = true, $currency = '', $context = 'display' ) {
 | |
| 	if ( empty( $currency ) ) {
 | |
| 		$currency = edd_get_currency();
 | |
| 	}
 | |
| 
 | |
| 	$formatter = new \EDD\Currency\Money_Formatter( $amount, new \EDD\Currency\Currency( $currency ) );
 | |
| 
 | |
| 	switch ( $context ) {
 | |
| 		case 'typed':
 | |
| 			$return_value = $formatter->format_for_typed( $decimals )->typed_amount;
 | |
| 			break;
 | |
| 		case 'display':
 | |
| 		default:
 | |
| 			$return_value = $formatter->format_for_display( $decimals )->amount;
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	return $return_value;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Formats the currency display
 | |
|  *
 | |
|  * @since 1.0
 | |
|  *
 | |
|  * @param string $price    Price. This should already be formatted.
 | |
|  * @param string $currency Currency code. When this function is used on an order's amount, the order's currency
 | |
|  *                         should always be provided here. If omitted, the store currency is used instead.
 | |
|  *                         But to ensure immutability with orders, the currency should always be explicitly provided
 | |
|  *                         if known and tied to an existing order.
 | |
|  *
 | |
|  * @return string $currency Currencies displayed correctly
 | |
|  */
 | |
| function edd_currency_filter( $price = '', $currency = '' ) {
 | |
| 
 | |
| 	// Fallback to default currency
 | |
| 	if ( empty( $currency ) ) {
 | |
| 		$currency = edd_get_currency();
 | |
| 	}
 | |
| 
 | |
| 	$currency = new \EDD\Currency\Currency( $currency );
 | |
| 	if ( '' === $price ) {
 | |
| 		return $currency->symbol;
 | |
| 	}
 | |
| 
 | |
| 	$formatter = new \EDD\Currency\Money_Formatter( $price, $currency );
 | |
| 
 | |
| 	return $formatter->apply_symbol();
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Set the number of decimal places per currency
 | |
|  *
 | |
|  * @since 1.4.2
 | |
|  * @since 3.0 Updated to allow currency to be passed in.
 | |
|  *
 | |
|  * @param int    $decimals Number of decimal places.
 | |
|  * @param string $currency Currency.
 | |
|  *
 | |
|  * @return int $decimals Number of decimal places for currency.
 | |
| */
 | |
| function edd_currency_decimal_filter( $decimals = 2, $currency = '' ) {
 | |
| 	$currency = empty( $currency )
 | |
| 		? edd_get_currency()
 | |
| 		: $currency;
 | |
| 
 | |
| 	switch ( $currency ) {
 | |
| 		case 'RIAL' :
 | |
| 		case 'JPY' :
 | |
| 		case 'TWD' :
 | |
| 		case 'HUF' :
 | |
| 			$decimals = 0;
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	return apply_filters( 'edd_currency_decimal_count', $decimals, $currency );
 | |
| }
 | |
| add_filter( 'edd_sanitize_amount_decimals', 'edd_currency_decimal_filter' );
 | |
| add_filter( 'edd_format_amount_decimals',   'edd_currency_decimal_filter', 10, 2 );
 | |
| 
 | |
| /**
 | |
|  * Sanitizes a string key for EDD Settings
 | |
|  *
 | |
|  * Keys are used as internal identifiers. Alphanumeric characters, dashes,
 | |
|  * underscores, stops, colons and slashes are allowed.
 | |
|  *
 | |
|  * This differs from `sanitize_key()` in that it allows uppercase letters,
 | |
|  * stops, colons, and slashes.
 | |
|  *
 | |
|  * @since  2.5.8
 | |
|  * @param  string $key String key
 | |
|  * @return string Sanitized key
 | |
|  */
 | |
| function edd_sanitize_key( $key = '' ) {
 | |
| 	$raw_key = $key;
 | |
| 	$key     = preg_replace( '/[^a-zA-Z0-9_\-\.\:\/]/', '', $key );
 | |
| 
 | |
| 	/**
 | |
| 	 * Filter a sanitized key string.
 | |
| 	 *
 | |
| 	 * @since 2.5.8
 | |
| 	 * @param string $key     Sanitized key.
 | |
| 	 * @param string $raw_key The key prior to sanitization.
 | |
| 	 */
 | |
| 	return apply_filters( 'edd_sanitize_key', $key, $raw_key );
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Never let a numeric value be less than zero.
 | |
|  *
 | |
|  * Adapted from bbPress.
 | |
|  *
 | |
|  * @since 3.0
 | |
|  *
 | |
|  * @param int $number Default 0.
 | |
|  * @return int.
 | |
|  */
 | |
| function edd_number_not_negative( $number = 0 ) {
 | |
| 
 | |
| 	// Protect against formatted strings
 | |
| 	if ( is_string( $number ) ) {
 | |
| 		$number = strip_tags( $number );                    // No HTML
 | |
| 		$number = preg_replace( '/[^0-9-]/', '', $number ); // No number-format
 | |
| 		// Protect against objects, arrays, scalars, etc...
 | |
| 	} elseif ( ! is_numeric( $number ) ) {
 | |
| 		$number = 0;
 | |
| 	}
 | |
| 
 | |
| 	// Make the number an integer
 | |
| 	$casted_number = is_float( $number )
 | |
| 		? floatval( $number )
 | |
| 		: intval( $number );
 | |
| 
 | |
| 	$max_value = is_float( $number )
 | |
| 		? 0.00
 | |
| 		: 0;
 | |
| 
 | |
| 	// Pick the maximum value, never less than zero
 | |
| 	$not_less_than_zero = max( $max_value, $casted_number );
 | |
| 
 | |
| 	// Filter & return
 | |
| 	return (int) apply_filters( 'edd_number_not_negative', $not_less_than_zero, $casted_number, $number );
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Return array of allowed HTML tags.
 | |
|  *
 | |
|  * Used with wp_kses() to filter unsafe HTML out of settings and notes.
 | |
|  *
 | |
|  * @since 3.0
 | |
|  *
 | |
|  * @return array
 | |
|  */
 | |
| function edd_get_allowed_tags() {
 | |
| 	return (array) apply_filters( 'edd_allowed_html_tags', array(
 | |
| 		'p'      => array(
 | |
| 			'class' => array(),
 | |
| 			'id'    => array(),
 | |
| 		),
 | |
| 		'span'   => array(
 | |
| 			'class' => array(),
 | |
| 			'id'    => array(),
 | |
| 		),
 | |
| 		'a' => array(
 | |
| 			'href'   => array(),
 | |
| 			'target' => array(),
 | |
| 			'title'  => array(),
 | |
| 			'class'  => array(),
 | |
| 			'id'     => array(),
 | |
| 		),
 | |
| 		'code'   => array(),
 | |
| 		'strong' => array(),
 | |
| 		'em'     => array(),
 | |
| 		'br'     => array(),
 | |
| 		'img'    => array(
 | |
| 			'src'   => array(),
 | |
| 			'title' => array(),
 | |
| 			'alt'   => array(),
 | |
| 			'id'    => array(),
 | |
| 		),
 | |
| 		'div'    => array(
 | |
| 			'class' => array(),
 | |
| 			'id'    => array(),
 | |
| 		),
 | |
| 		'ul'     => array(
 | |
| 			'class' => array(),
 | |
| 			'id'    => array(),
 | |
| 		),
 | |
| 		'li'     => array(
 | |
| 			'class' => array(),
 | |
| 			'id'    => array(),
 | |
| 		),
 | |
| 	) );
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Return a translatable and display ready string for an address type.
 | |
|  *
 | |
|  * @since 3.0
 | |
|  * @param string $address_type The type of address to get the display label for.
 | |
|  *
 | |
|  * @return string              The translatable string for the display type, in lowercase.
 | |
|  */
 | |
| function edd_get_address_type_label( $address_type = 'billing' ) {
 | |
| 
 | |
| 	// Core default address types and their labels.
 | |
| 	$address_type_labels = array(
 | |
| 		'billing' => __( 'Billing', 'easy-digital-downloads' ),
 | |
| 	);
 | |
| 
 | |
| 	/**
 | |
| 	 * Physical address type labels.
 | |
| 	 *
 | |
| 	 * A key/value array of billing types found in the 'type' column of the customer address table, and their translatable
 | |
| 	 * strings for output.
 | |
| 	 *
 | |
| 	 * @since 3.0
 | |
| 	 * @param array $address_type_labels
 | |
| 	 *     Array of the address type labels, in key/value form. The key should match the database entry for the
 | |
| 	 *         wp_edd_customer_addresses table in the 'type' column. The value of each array entry should be a translatable
 | |
| 	 *         string for output in the UI.
 | |
| 	 */
 | |
| 	$address_type_labels = apply_filters( 'edd_address_type_labels', $address_type_labels );
 | |
| 
 | |
| 	// Fallback to just applying an upper case to any words not in the filter.
 | |
| 	return array_key_exists( $address_type, $address_type_labels ) ?
 | |
| 		$address_type_labels[ $address_type ] :
 | |
| 		$address_type;
 | |
| 
 | |
| }
 |