1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-15 03:05:26 +02:00

Additional updates, additions and fixes to $sanitizer->float(), InputfieldFloat and FieldtypeFloat per processwire/processwire-issues#1502

This commit is contained in:
Ryan Cramer
2022-01-11 09:51:52 -05:00
parent 062f2293d0
commit 6c29350918
3 changed files with 73 additions and 29 deletions

View File

@@ -3880,6 +3880,15 @@ class Sanitizer extends Wire {
/**
* Sanitize to floating point value
*
* Values for `getString` argument:
*
* - `false` (bool): do not return string value (default). 3.0.171+
* - `true` (bool): locale aware floating point number string. 3.0.171+
* - `f` (string): locale aware floating point number string (same as true). 3.0.193+
* - `F` (string): non-locale aware floating point number string. 3.0.193+
* - `e` (string): lowercase scientific notation (e.g. 1.2e+2). 3.0.193+
* - `E` (string): uppercase scientific notation (e.g. 1.2E+2). 3.0.193+
*
* #pw-group-numbers
*
* @param float|string|int $value
@@ -3889,8 +3898,8 @@ class Sanitizer extends Wire {
* - `blankValue` (null|int|string|float): Value to return (whether float or non-float) if provided $value is an empty non-float (default=0.0)
* - `min` (float|null): Minimum allowed value, excluding blankValue (default=null)
* - `max` (float|null): Maximum allowed value, excluding blankValue (default=null)
* - `getString (bool): Return a string rather than float value? (default=false) added 3.0.171
* @return float
* - `getString (bool|string): Return a string rather than float value? 3.0.171+ (default=false). See value options in method description.
* @return float|string
*
*/
public function float($value, array $options = array()) {
@@ -3901,7 +3910,7 @@ class Sanitizer extends Wire {
'blankValue' => 0.0, // Value to return (whether float or non-float) if provided $value is an empty non-float (default=0.0)
'min' => null, // Minimum allowed value (excluding blankValue)
'max' => null, // Maximum allowed value (excluding blankValue)
'getString' => false, // Return a string rather than float value?
'getString' => false, // Return a string rather than float value? bool or f, F, e, E
);
$options = array_merge($defaults, $options);
@@ -3926,10 +3935,11 @@ class Sanitizer extends Wire {
$prepend = '-';
$str = ltrim($str, '-');
}
if((stripos($str, 'E-') || stripos($str, 'E+')) && preg_match('/^([0-9., ]+\d)(E[-+]\d+)/i', $str, $m)) {
if(stripos($str, 'E') && preg_match('/^([0-9., ]*\d)(E[-+]?\d+)/i', $str, $m)) {
$str = $m[1];
$append = $m[2];
if($options['precision'] === null) $options['precision'] = ((int) ltrim($append, '-+eE'));
}
if(!strlen($str)) return $options['blankValue'];
@@ -3975,7 +3985,13 @@ class Sanitizer extends Wire {
$value = $prepend . $value . $append;
if(!$options['getString']) $value = floatval($value);
}
} else if(is_float($value)) {
if($options['precision'] === null) {
$str = strtoupper("$value");
if(strpos($str, 'E')) $options['precision'] = (int) ltrim(stristr("$value", 'E'), 'E-+');
}
}
if(!$options['getString'] && !is_float($value)) $value = (float) $value;
if(!is_null($options['min']) && ((float) $value) < ((float) $options['min'])) $value = $options['min'];
@@ -3983,11 +3999,14 @@ class Sanitizer extends Wire {
if(!is_null($options['precision'])) $value = round((float) $value, (int) $options['precision'], (int) $options['mode']);
if($options['getString']) {
$f = $options['getString'];
$f = is_string($f) && in_array($f, array('f', 'F', 'e', 'E')) ? $f : 'f';
if($options['precision'] === null) {
$value = strpos($value, 'E-') || strpos($value, 'E+') ? rtrim(sprintf('%.20f', (float) $value), '0') : "$value";
$value = stripos("$value", 'E') ? rtrim(sprintf("%.15$f", (float) $value), '0') : "$value";
} else {
$value = sprintf('%.' . $options['precision'] . 'f', (float) $value);
$value = sprintf("%.$options[precision]$f", (float) $value);
}
$value = rtrim($value, '.');
}
return $value;

View File

@@ -8,7 +8,7 @@
* For documentation about the fields used in this class, please see:
* /wire/core/Fieldtype.php
*
* ProcessWire 3.x, Copyright 2020 by Ryan Cramer
* ProcessWire 3.x, Copyright 2022 by Ryan Cramer
* https://processwire.com
*
*/
@@ -19,7 +19,7 @@ class FieldtypeFloat extends Fieldtype {
return array(
'title' => __('Float', __FILE__),
'summary' => __('Field that stores a floating point number', __FILE__),
'version' => 106,
'version' => 107,
'permanent' => true,
);
}
@@ -86,7 +86,7 @@ class FieldtypeFloat extends Fieldtype {
$value = $this->wire()->sanitizer->float((string) $value, array('blankValue' => ''));
}
$precision = $field->get('precision');
if($precision === null || $precision === '') {
if($precision === null || $precision === '' || $precision < 0) {
$value = (float) $value;
} else {
$value = round((float) $value, $precision);
@@ -121,9 +121,13 @@ class FieldtypeFloat extends Fieldtype {
*/
public function ___sleepValue(Page $page, Field $field, $value) {
$precision = $field->get('precision');
if(is_null($precision) || $precision === '') $precision = self::getPrecision($value);
if(!is_string($value)) $value = number_format($value, $precision, '.', '');
return $value;
if(is_null($precision) || $precision === '' || $precision < 0) {
$precision = self::getPrecision($value);
}
if(!is_string($value)) {
$value = number_format($value, $precision, '.', '');
}
return $value;
}
/**
@@ -135,8 +139,13 @@ class FieldtypeFloat extends Fieldtype {
*/
public static function getPrecision($value) {
$value = (float) $value;
$remainder = ceil($value) - $value;
$precision = strlen(ltrim($remainder, '0., '));
if(stripos("$value", 'E')) {
list(,$precision) = explode('E', strtoupper("$value"), 2);
$precision = (int) ltrim($precision, '+-');
} else {
$remainder = ceil($value) - $value;
$precision = strlen(ltrim("$remainder", '0., '));
}
if(!$precision) $precision = 1;
return $precision;
}
@@ -170,6 +179,7 @@ class FieldtypeFloat extends Fieldtype {
$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.');
if($precision !== '') $f->val($precision);
$f->attr('size', 8);
$inputfields->append($f);

View File

@@ -6,7 +6,7 @@
* ProcessWire 3.x, Copyright 2022 by Ryan Cramer
* https://processwire.com
*
* @property int $precision Decimals precision
* @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
@@ -25,7 +25,7 @@ class InputfieldFloat extends InputfieldInteger {
return array(
'title' => __('Float', __FILE__), // Module Title
'summary' => __('Floating point number with precision', __FILE__), // Module Summary
'version' => 104,
'version' => 105,
'permanent' => true,
);
}
@@ -59,7 +59,7 @@ class InputfieldFloat extends InputfieldInteger {
protected function getPrecision($value = null) {
if($value !== null) return FieldtypeFloat::getPrecision($value);
$precision = $this->precision;
return $precision === null || $precision === '' ? '' : (int) $precision;
return $precision === null || $precision === '' || $precision < 0 ? '' : (int) $precision;
}
/**
@@ -78,10 +78,8 @@ class InputfieldFloat extends InputfieldInteger {
if(!strlen("$value")) return '';
}
$precision = $this->precision;
if($precision === null || $precision === '') {
$precision = FieldtypeFloat::getPrecision($value);
}
return round((float) $value, $precision);
if($precision === null || $precision === '') $precision = $this->getPrecision($value);
return is_int($precision) && $precision > 0 ? round((float) $value, $precision) : $value;
}
/**
@@ -95,6 +93,23 @@ class InputfieldFloat extends InputfieldInteger {
return (float) $value;
}
/**
* Is value in scientific notation?
*
* @param string $value
* @return bool
* @since 3.0.193
*
*/
public function isScientific($value) {
$value = strtoupper((string) $value);
if(strpos($value, 'E') === false) return false;
$value = str_replace('.', '', $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]
*
@@ -105,8 +120,8 @@ class InputfieldFloat extends InputfieldInteger {
public function getAttributesString(array $attributes = null) {
if($attributes && $attributes['type'] === 'number') {
$value = isset($attributes['value']) ? $attributes['value'] : null;
if(is_float($value) || is_string($value)) {
if(strlen("$value") && !ctype_digit(str_replace('.', '', ltrim($value, '-')))) {
if(is_float($value) || (is_string($value) && strlen($value))) {
if(!$this->isScientific($value) && !ctype_digit(str_replace('.', '', ltrim($value, '-')))) {
// float value is using a non "." as decimal point, needs conversion because
// the HTML5 number input type requires "." as the decimal
$attributes['value'] = $this->localeConvertValue($value);
@@ -117,10 +132,8 @@ class InputfieldFloat extends InputfieldInteger {
$attributes['step'] = '.' . ($precision > 1 ? str_repeat('0', $precision - 1) : '') . '1';
}
}
if($attributes && isset($attributes['value']) && stripos($attributes['value'], 'E')) {
$attributes['value'] = $this->wire()->sanitizer->float($attributes['value'], array(
'getString' => true,
));
if($attributes && isset($attributes['value']) && $this->isScientific($attributes['value'])) {
$attributes['value'] = $this->wire()->sanitizer->float($attributes['value']);
}
return parent::getAttributesString($attributes);
}
@@ -152,10 +165,12 @@ class InputfieldFloat extends InputfieldInteger {
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);