File "Abstract_Value.php"
Full Path: /home/romayxjt/public_html/wp-content/plugins/the-events-calendar/common/src/Tribe/Values/Abstract_Value.php
File size: 10.9 KB
MIME-type: text/x-php
Charset: utf-8
<?php
namespace Tribe\Values;
abstract class Abstract_Value implements Value_Interface {
use Value_Calculation;
use Value_Update;
/**
* Holds the initial value passed to the constructor. This variable does not change.
*
* @since 4.14.9
*
* @var mixed
*/
private $initial_value;
/**
* Holds the value normalized value calculated when instantiating an object or setting new values.
*
* @since 4.14.9
*
* @var float
*/
private $normalized_amount;
/**
* The integer representation of the amount. By default, this is the float value, rounded to the object precision
* places and multiplied by (10^precision).
*
* @since 4.14.9
*
* @var int
*/
private $integer = 0;
/**
* The float representation of the amount. By default, this is the same as $normalized_amount
*
* @since 4.14.9
*
* @var float
*/
private $float = 0.0;
/**
* The decimal precision to use in calculations.
*
* @since 4.14.9
*
* @var int
*/
private $precision = 2;
/**
* The class type representation to use when firing scoped filters
*
* @since 4.14.9
*
* @var string
*/
public $value_type;
/**
* Initialize object
*
* @since 4.14.9
*
* @param mixed $amount the value to set initially
*/
public function __construct( $amount = 0 ) {
$this->set_initial_representation( $amount );
$this->set_normalized_amount( $amount );
$this->update();
}
/**
* @inheritDoc
*/
public static function create( $value = 0 ) {
$class = get_called_class();
return new $class( $value );
}
/**
* @inheritDoc
*/
public function set_value( $amount ) {
$this->set_normalized_amount( $amount );
$this->update();
}
/**
* @inheritDoc
*/
public function set_precision( $amount ) {
$this->precision = $amount;
}
/**
* @inheritDoc
*/
public function get_integer() {
/**
* Filter the value returned for get_integer() when implemented in a specific class name
*
* @since 4.14.9
*
* @param int $integer the integer representation of the value
* @param Abstract_Value the object instance
*
* @return int
*/
$integer = apply_filters( "tec_common_value_{$this->get_value_type()}_get_integer", $this->integer, $this );
/**
* Filter the value returned for get_integer() when implemented in any class
*
* @since 4.14.9
*
* @param int $integer the integer representation of the value
* @param Abstract_Value the object instance
*
* @return int
*/
return apply_filters( 'tec_common_value_get_integer', $integer, $this );
}
/**
* @inheritDoc
*/
public function get_float() {
/**
* Filter the value returned for get_float() when implemented in a specific class name
*
* @since 4.14.9
*
* @param float $float the float representation of the value
* @param Abstract_Value the object instance
*
* @return float
*/
$float = apply_filters( "tec_common_value_{$this->get_value_type()}_get_float", $this->float, $this );
/**
* Filter the value returned for get_float() when implemented in any class
*
* @since 4.14.9
*
* @param float $float the float representation of the value
* @param Abstract_Value the object instance
*
* @return float
*/
return apply_filters( 'tec_common_value_get_float', $float, $this );
}
/**
* @inheritDoc
*/
public function get_precision() {
/**
* Filter the value returned for get_precision() when implemented in a specific class name
*
* @since 4.14.9
*
* @param int $precision the precision to which values will be calculated
* @param Abstract_Value the object instance
*
* @return int
*/
$precision = apply_filters( "tec_common_value_{$this->get_value_type()}_get_precision", $this->precision, $this );
/**
* Filter the value returned for get_precision() when implemented in any class
*
* @since 4.14.9
*
* @param int $precision the precision to which values will be calculated
* @param Abstract_Value the object instance
*
* @return int
*/
return (int) apply_filters( 'tec_common_value_get_precision', $precision, $this );
}
/**
* @inheritDoc
*/
public function get_normalized_value() {
return $this->normalized_amount;
}
/**
* @inheritDoc
*/
public function get_initial_representation() {
return $this->initial_value;
}
/**
* @inheritDoc
*/
public function get_value_type() {
return $this->value_type;
}
/**
* @inheritDoc
*/
public function normalize( $value ) {
if ( is_numeric( $value ) ) {
return (float) $value;
}
if ( $this->is_character_block( $value ) ) {
return (float) 0;
}
$value = $this->remove_character_blocks( $value );
$value = $this->remove_html( $value );
// Get all non-digits from the amount
preg_match_all( '/[^\d]/', $value, $non_digits );
// if the string is all digits, it is numeric
if ( empty( $non_digits[0] ) ) {
return (float) $value;
}
$pieces = $this->remove_non_digits( $value, $non_digits );
return (float) $this->assemble_normalized_value( $pieces );
}
/**
* Removes any blocks composed of all non-digit characters from the numeric string. These will usually represent
* the currency code and any other pieces of text that may have been sent with the value.
*
* This is specially important in case the currency unit contains the same characters as the decimal/thousands
* separators such as in Moroccan Dirham (1,234.56 .د.م.) or Danish Krone (kr. 1.234,56)
*
* @since 4.14.9
*
* @param string $value the numeric string being normalized
*
* @return string
*/
private function remove_character_blocks( $value ) {
foreach ( explode( ' ', $value ) as $block ) {
if ( ! $this->is_character_block( $block ) ) {
continue;
}
$value = str_replace( $block, '', $value );
}
return $value;
}
/**
* Removes all html tags and html entities from the value string
*
* @since 4.14.9
*
* @param string $value the value being normalized
*
* @return string
*/
private function remove_html( $value ) {
return wp_strip_all_tags( preg_replace( '/&[^;]+;/', '', trim( $value ) ) );
}
/**
* Takes the value string and a list of non-digit characters and removes any of those characters. If the character
* is found to be a decimal separator, normalize it to a dot, so the number translates to a float.
*
* @since 4.14.9
*
* @param string $value the value being normalized
* @param string[] $non_digits a list of non-digit characters present in $value
* @param string $separator a default separator to use when splitting the string
*
* @return string[]
*/
private function remove_non_digits( $value, $non_digits, $separator = '>>>' ) {
$tokens = array_unique( $non_digits[0] );
foreach ( $tokens as $token ) {
if ( $this->is_decimal_separator( $token, $value ) ) {
$separator = $token;
continue;
}
$value = str_replace( $token, '', $value );
}
return explode( $separator, $value );
}
/**
* Re-assemble the normalized value to store.
*
* @since 4.14.9
*
* @param int[] $pieces the normalized value split in an array.
*
* @return float
*/
private function assemble_normalized_value( $pieces ) {
// If the initial amount did not have decimals specified, $pieces will be an array of a single
// numeric value, so we just return it as a float.
if ( 1 === count( $pieces ) && is_numeric( reset( $pieces ) ) ) {
return (float) reset( $pieces );
}
$decimal = array_pop( $pieces );
return (float) implode( '', array_merge( $pieces, [ '.', $decimal ] ) );
}
/**
* Private setter for the initial value the object was created with. This value cannot be changed during the object
* lifecycle.
*
* @since 4.14.9
*
* To set a new value discard the original object and create a new one.
*/
private function set_initial_representation( $amount ) {
if ( empty( $this->initial_value ) ) {
$this->initial_value = $amount;
}
}
/**
* Private setter for the normalized amount extracted from the initial value.
*
* @since 4.14.9
*
* To set a new value use the public setter `$obj->set_value( $amount )`
*/
private function set_normalized_amount( $amount ) {
$normalized_value = $this->normalize( $amount );
/**
* Filter the value to be set as $normalized_amount for a specific implementation.
*
* @since 4.14.9
*
* @param float $normalized_value the normalized value
* @param Abstract_Value the object instance
*
* @return float
*/
$normalized_value = (float) apply_filters( "tec_common_{$this->get_value_type()}_value_normalized", $normalized_value, $this );
/**
* Filter the value to be set as $normalized_amount for all implementations.
*
* @since 4.14.9
*
* @param float $normalized_value the normalized value
* @param Abstract_Value the object instance
*
* @return float
*/
$normalized_value = (float) apply_filters( "tec_common_value_normalized", $normalized_value, $this );
/**
* Fire action right before setting the normalized value
*
* @since 4.14.9
*
* @param float $normalized_value the normalized value
* @param Abstract_Value the object instance
*/
do_action( 'tec_common_value_normalized', $normalized_value, $this );
$this->normalized_amount = $normalized_value;
}
/**
* Private setter for the integer representation of the object amount.
*
* @since 4.14.9
*
* To set a new value use the public setter `$obj->set_value( $amount )`
*/
protected function set_integer_value() {
$this->integer = $this->to_integer( $this->normalized_amount );
}
/**
* Private setter for the floating point representation of the object amount.
*
* @since 4.14.9
*
* To set a new value use the public setter `$obj->set_value( $amount )`
*/
protected function set_float_value() {
$this->float = $this->normalized_amount;
}
/**
* Tries to determine if a token is serving as a decimal separator or something else
* in a string;
*
* The rule to determine a decimal is straightforward. It needs to exist only once
* in the string and the piece of the string after the separator cannot be longer
* than 2 digits. Anything else is serving another purpose.
*
* @since 4.14.9
*
* @param $separator string a separator token, like . or ,
* @param $value string a number formatted as a string
*
* @return bool
*/
private function is_decimal_separator( $separator, $value ) {
$pieces = array_filter( explode( $separator, $value ) );
foreach ( $pieces as $i => $block ) {
if ( $this->is_character_block( $block ) ) {
unset( $pieces[ $i ] );
}
}
if ( 2 === count( $pieces ) ) {
return strlen( array_pop( $pieces ) ) < 3;
}
return false;
}
/**
* Tests if a string is composed entirely of non-digit characters
*
* @since 4.14.9
* @since 6.4.1 Cast $block to string to avoid PHP 8.0 deprecation notice.
*
* @param string $block the string to check
*
* @return bool
*/
private function is_character_block( $block ) {
return empty( preg_replace( '/\D/', '', (string) $block ) );
}
}