1<?php 2namespace plugin\struct\types; 3 4use plugin\struct\meta\ValidationException; 5 6/** 7 * Class Decimal 8 * 9 * A field accepting decimal numbers 10 * 11 * @package plugin\struct\types 12 */ 13class Decimal extends AbstractMultiBaseType { 14 15 protected $config = array( 16 'min' => '', 17 'max' => '', 18 'roundto' => '-1', 19 'decpoint' => '.', 20 'thousands' => "\xE2\x80\xAF", // narrow no-break space 21 'trimzeros' => true, 22 'prefix' => '', 23 'postfix' => '' 24 ); 25 26 /** 27 * Output the stored data 28 * 29 * @param string|int $value the value stored in the database 30 * @param \Doku_Renderer $R the renderer currently used to render the data 31 * @param string $mode The mode the output is rendered in (eg. XHTML) 32 * @return bool true if $mode could be satisfied 33 */ 34 public function renderValue($value, \Doku_Renderer $R, $mode) { 35 if($this->config['roundto'] == -1) { 36 $value = $this->formatWithoutRounding( 37 $value, 38 $this->config['decpoint'], 39 $this->config['thousands'] 40 ); 41 } else { 42 $value = floatval($value); 43 $value = number_format( 44 $value, 45 $this->config['roundto'], 46 $this->config['decpoint'], 47 $this->config['thousands'] 48 ); 49 } 50 if($this->config['trimzeros'] && (strpos($value, $this->config['decpoint']) !== false)) { 51 $value = rtrim($value, '0'); 52 $value = rtrim($value, $this->config['decpoint']); 53 } 54 55 $R->cdata($this->config['prefix'] . $value . $this->config['postfix']); 56 return true; 57 } 58 59 /** 60 * @param int|string $value 61 * @return int|string 62 * @throws ValidationException 63 */ 64 public function validate($value) { 65 $value = parent::validate($value); 66 $value = str_replace(',', '.', $value); // we accept both 67 68 if((string) $value != (string) floatval($value)) { 69 throw new ValidationException('Decimal needed'); 70 } 71 72 if($this->config['min'] !== '' && floatval($value) <= floatval($this->config['min'])) { 73 throw new ValidationException('Decimal min', floatval($this->config['min'])); 74 } 75 76 if($this->config['max'] !== '' && floatval($value) >= floatval($this->config['max'])) { 77 throw new ValidationException('Decimal max', floatval($this->config['max'])); 78 } 79 80 return $value; 81 } 82 83 /** 84 * Works like number_format but keeps the decimals as is 85 * 86 * @link http://php.net/manual/en/function.number-format.php#91047 87 * @author info at daniel-marschall dot de 88 * @param float $number 89 * @param string $dec_point 90 * @param string $thousands_sep 91 * @return string 92 */ 93 function formatWithoutRounding($number, $dec_point, $thousands_sep) { 94 $was_neg = $number < 0; // Because +0 == -0 95 96 $tmp = explode('.', $number); 97 $out = number_format(abs(floatval($tmp[0])), 0, $dec_point, $thousands_sep); 98 if(isset($tmp[1])) $out .= $dec_point . $tmp[1]; 99 100 if($was_neg) $out = "-$out"; 101 102 return $out; 103 } 104 105} 106