? 3.0.193+ * */ class InputfieldFloat extends InputfieldInteger { public static function getModuleInfo() { return array( 'title' => __('Float', __FILE__), // Module Title 'summary' => __('Floating point number with precision', __FILE__), // Module Summary 'version' => 105, 'permanent' => true, ); } /** * Construct * */ public function __construct() { $this->set('precision', 2); $this->set('digits', 0); $this->set('noE', 0); parent::__construct(); } /** * Module init * */ public function init() { parent::init(); $this->attr('step', 'any'); // HTML5 attr required to support decimals with 'number' types } /** * Get configured precision setting, or if given a value, precision of the value * * @param float|string|null $value * @return int|string Returns integer of precision or blank string if none defined * */ protected function getPrecision($value = null) { if($value !== null) return FieldtypeFloat::getPrecision($value); $precision = $this->precision; return $precision === null || $precision === '' || $precision < 0 ? '' : (int) $precision; } /** * Sanitize value * * @param float|string $value * @return float|string * */ protected function sanitizeValue($value) { if(!strlen("$value")) { $value = ''; } else if($this->digits > 0) { $value = (string) $value; if(!is_numeric("$value")) { $value = $this->wire()->sanitizer->float($value, array( 'precision' => (int) $this->precision, 'getString' => 'F', 'blankValue' => '', )); } } else if(!is_float($value) && !is_int($value)) { $value = $this->wire()->sanitizer->float($value, array('blankValue' => '')); if(!strlen("$value")) $value = ''; } else { $precision = $this->precision; if($precision === null || $precision === '') $precision = $this->getPrecision($value); $value = is_int($precision) && $precision > 0 ? round((float) $value, $precision) : $value; } return $value; } /** * Typecast value to float, override from InputfieldInteger * * @param string|int|float $value * @return int * */ protected function typeValue($value) { return (float) $value; } /** * Does the value have an E in it like ”123E-3” or "123E3” ? * * @param string $value * @return bool * @since 3.0.193 * */ public function hasE($value) { $value = strtoupper((string) $value); if(strpos($value, 'E') === false) return false; $value = str_replace(array('-', '.', ',', ' '), '', $value); list($a, $b) = explode('E', $value, 2); $b = trim($b, '+-'); return ctype_digit("$a$b"); } /** * Override method from Inputfield to convert locale specific decimals for input[type=number] * * @param array $attributes * @return string * */ public function getAttributesString(array $attributes = null) { if(is_null($attributes)) $attributes = $this->getAttributes(); if($attributes['type'] === 'number') { $value = isset($attributes['value']) ? $attributes['value'] : null; if(is_float($value) || (is_string($value) && strlen($value))) { // the HTML5 number input type requires "." as the decimal $value = $this->localeConvertValue($value); $attributes['value'] = $value; } if(empty($attributes['step']) || $attributes['step'] === 'any') { $precision = (int) $this->precision; if($precision < 1 && $value !== null) $precision = $this->getPrecision($value); if($precision > 0) { $attributes['step'] = '.' . ($precision > 1 ? str_repeat('0', $precision - 1) : '') . '1'; } } } else if($this->digits > 0 && empty($attributes['inputmode'])) { $attributes['inputmode'] = 'decimal'; } if(!empty($attributes['value']) && $this->noE && $this->hasE($attributes['value'])) { $attributes['value'] = $this->wire()->sanitizer->float($attributes['value'], array('getString' => true)); } if($this->precision > 0 && $this->digits > 0) { if(isset($attributes['value']) && strlen("$attributes[value]")) { $f = $attributes['type'] === 'number' ? 'F' : 'f'; // F=non-locale aware, f=locale aware $attributes['value'] = sprintf("%.{$this->precision}$f", (float) $attributes['value']); } } return parent::getAttributesString($attributes); } /** * Convert floats with non "." decimal points to use "." decimal point according to locale * * @param float|string $value * @return string|float Returns string representation of float when value was converted * */ protected function localeConvertValue($value) { if(!strlen("$value")) return $value; if(ctype_digit(str_replace(array('.', 'E', 'e', '-', '+'), '', "$value"))) return $value; $locale = localeconv(); $decimal = $locale['decimal_point']; if($decimal === '.' || strpos($value, $decimal) === false) return $value; $parts = explode($decimal, $value, 2); $value = implode('.', $parts); return $value; } /** * Inputfield config * * @return InputfieldWrapper * */ public function getConfigInputfields() { $inputfields = parent::getConfigInputfields(); if($this->hasFieldtype === false) { // when used without FieldtypeFloat /** @var InputfieldInteger $f */ $f = $this->wire()->modules->get('InputfieldInteger'); $f->attr('name', 'precision'); $f->label = $this->_('Number of decimal digits to round to'); $f->description = $this->_('Or use a negative number like `-1` to disable rounding.'); $f->attr('value', $this->precision); $f->attr('size', 8); $inputfields->add($f); } else { // precision is configured with FieldtypeFloat } // @todo anyone other than me want a config setting for $noE ? return $inputfields; } }