aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/smarty/smarty/libs/plugins/function.math.php
blob: 34912d2393cf543bcfaaa673d17804c37f52becf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<?php
/**
 * Smarty plugin
 * This plugin is only for Smarty2 BC
 *
 * @package    Smarty
 * @subpackage PluginsFunction
 */
/**
 * Smarty {math} function plugin
 * Type:     function
 * Name:     math
 * Purpose:  handle math computations in template
 *
 * @link   https://www.smarty.net/manual/en/language.function.math.php {math}
 *           (Smarty online manual)
 * @author Monte Ohrt <monte at ohrt dot com>
 *
 * @param array                    $params   parameters
 * @param Smarty_Internal_Template $template template object
 *
 * @return string|null
 */
function smarty_function_math($params, $template)
{
    static $_allowed_funcs =
        array(
            'int'   => true,
            'abs'   => true,
            'ceil'  => true,
            'acos'   => true,
            'acosh'   => true,
            'cos'   => true,
            'cosh'   => true,
            'deg2rad'   => true,
            'rad2deg'   => true,
            'exp'   => true,
            'floor' => true,
            'log'   => true,
            'log10' => true,
            'max'   => true,
            'min'   => true,
            'pi'    => true,
            'pow'   => true,
            'rand'  => true,
            'round' => true,
            'asin'   => true,
            'asinh'   => true,
            'sin'   => true,
            'sinh'   => true,
            'sqrt'  => true,
            'srand' => true,
            'atan'   => true,
            'atanh'   => true,
            'tan'   => true,
            'tanh'   => true
        );

    // be sure equation parameter is present
    if (empty($params[ 'equation' ])) {
        trigger_error("math: missing equation parameter", E_USER_WARNING);
        return;
    }
    $equation = $params[ 'equation' ];

    // Remove whitespaces
    $equation = preg_replace('/\s+/', '', $equation);

    // Adapted from https://www.php.net/manual/en/function.eval.php#107377
    $number = '-?(?:\d+(?:[,.]\d+)?|pi|π)'; // What is a number
    $functionsOrVars = '((?:0x[a-fA-F0-9]+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))';
    $operators = '[,+\/*\^%-]'; // Allowed math operators
    $regexp = '/^(('.$number.'|'.$functionsOrVars.'|('.$functionsOrVars.'\s*\((?1)*\)|\((?1)*\)))(?:'.$operators.'(?1))?)+$/';

    if (!preg_match($regexp, $equation)) {
        trigger_error("math: illegal characters", E_USER_WARNING);
        return;
    }

    // make sure parenthesis are balanced
    if (substr_count($equation, '(') !== substr_count($equation, ')')) {
        trigger_error("math: unbalanced parenthesis", E_USER_WARNING);
        return;
    }

    // disallow backticks
    if (strpos($equation, '`') !== false) {
        trigger_error("math: backtick character not allowed in equation", E_USER_WARNING);
        return;
    }

    // also disallow dollar signs
    if (strpos($equation, '$') !== false) {
        trigger_error("math: dollar signs not allowed in equation", E_USER_WARNING);
        return;
    }
    foreach ($params as $key => $val) {
        if ($key !== 'equation' && $key !== 'format' && $key !== 'assign') {
            // make sure value is not empty
            if (strlen($val) === 0) {
                trigger_error("math: parameter '{$key}' is empty", E_USER_WARNING);
                return;
            }
            if (!is_numeric($val)) {
                trigger_error("math: parameter '{$key}' is not numeric", E_USER_WARNING);
                return;
            }
        }
    }
    // match all vars in equation, make sure all are passed
    preg_match_all('!(?:0x[a-fA-F0-9]+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)!', $equation, $match);
    foreach ($match[ 1 ] as $curr_var) {
        if ($curr_var && !isset($params[ $curr_var ]) && !isset($_allowed_funcs[ $curr_var ])) {
            trigger_error(
                "math: function call '{$curr_var}' not allowed, or missing parameter '{$curr_var}'",
                E_USER_WARNING
            );
            return;
        }
    }
    foreach ($params as $key => $val) {
        if ($key !== 'equation' && $key !== 'format' && $key !== 'assign') {
            $equation = preg_replace("/\b$key\b/", " \$params['$key'] ", $equation);
        }
    }
    $smarty_math_result = null;
    eval("\$smarty_math_result = " . $equation . ";");

    if (empty($params[ 'format' ])) {
        if (empty($params[ 'assign' ])) {
            return $smarty_math_result;
        } else {
            $template->assign($params[ 'assign' ], $smarty_math_result);
        }
    } else {
        if (empty($params[ 'assign' ])) {
            printf($params[ 'format' ], $smarty_math_result);
        } else {
            $template->assign($params[ 'assign' ], sprintf($params[ 'format' ], $smarty_math_result));
        }
    }
}