Rev 22 | Blame | Compare with Previous | Last modification | View Log | Download
<?php namespace ProcessWire;/*** Inputfield for floating point numbers** ProcessWire 3.x, Copyright 2022 by Ryan Cramer* https://processwire.com** @property int $precision Decimals precision (or -1 to disable rounding in 3.0.193+)* @property int $digits Total digits, for when used in decimal mode (default=0)* @property string $inputType Input type to use, one of "text" or "number"* @property int|float $min* @property int|float $max* @property int|float|string $step* @property int $size* @property string $placeholder* @property int|float $initValue Initial/default value (when used as independent Inputfield)* @property int|float|string $defaultValue Initial/default value (when used with FieldtypeInteger)* @property bool|int $noE Convert “123E-3” and “123E3” type numbers to real numbers in the <input>? 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;}}