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 ); 23 24 /** 25 * Output the stored data 26 * 27 * @param string|int $value the value stored in the database 28 * @param \Doku_Renderer $R the renderer currently used to render the data 29 * @param string $mode The mode the output is rendered in (eg. XHTML) 30 * @return bool true if $mode could be satisfied 31 */ 32 public function renderValue($value, \Doku_Renderer $R, $mode) { 33 if($this->config['roundto'] == -1) { 34 $value = $this->formatWithoutRounding( 35 $value, 36 $this->config['decpoint'], 37 $this->config['thousands'] 38 ); 39 } else { 40 $value = floatval($value); 41 $value = number_format( 42 $value, 43 $this->config['roundto'], 44 $this->config['decpoint'], 45 $this->config['thousands'] 46 ); 47 } 48 if($this->config['trimzeros'] && (strpos($value, $this->config['decpoint']) !== false)) { 49 $value = rtrim($value, '0'); 50 $value = rtrim($value, $this->config['decpoint']); 51 } 52 53 $R->cdata($value); 54 return true; 55 } 56 57 /** 58 * @param int|string $value 59 * @return int|string 60 * @throws ValidationException 61 */ 62 public function validate($value) { 63 $value = parent::validate($value); 64 $value = str_replace(',', '.', $value); // we accept both 65 66 if((string) $value != (string) floatval($value)) { 67 throw new ValidationException('Decimal needed'); 68 } 69 70 if($this->config['min'] !== '' && floatval($value) <= floatval($this->config['min'])) { 71 throw new ValidationException('Decimal min', floatval($this->config['min'])); 72 } 73 74 if($this->config['max'] !== '' && floatval($value) >= floatval($this->config['max'])) { 75 throw new ValidationException('Decimal max', floatval($this->config['max'])); 76 } 77 78 return $value; 79 } 80 81 /** 82 * Works like number_format but keeps the decimals as is 83 * 84 * @link http://php.net/manual/en/function.number-format.php#91047 85 * @author info at daniel-marschall dot de 86 * @param float $number 87 * @param string $dec_point 88 * @param string $thousands_sep 89 * @return string 90 */ 91 function formatWithoutRounding($number, $dec_point, $thousands_sep) { 92 $was_neg = $number < 0; // Because +0 == -0 93 94 $tmp = explode('.', $number); 95 $out = number_format(abs(floatval($tmp[0])), 0, $dec_point, $thousands_sep); 96 if(isset($tmp[1])) $out .= $dec_point . $tmp[1]; 97 98 if($was_neg) $out = "-$out"; 99 100 return $out; 101 } 102 103} 104