xref: /plugin/struct/types/Decimal.php (revision 965c6ba8c648a63e93a7b78878cba835f8fc7d3e)
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