summaryrefslogtreecommitdiff
path: root/src/FunctionHandler/Math.php
blob: 23ef9253db87ebb695cd317bbbfc8ef714cc1952 (plain)
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
<?php

namespace Smarty\FunctionHandler;

use Smarty\Template;

/**
 * Smarty {math} function plugin
 * Type:     function
 * Name:     math
 * Purpose:  handle math computations in template
 *
 * @author Monte Ohrt <monte at ohrt dot com>
 *
 * @param array                    $params   parameters
 * @param Template $template template object
 *
 * @return string|null
 */
class Math extends Base {

	public function handle($params, Template $template) {
		static $_allowed_funcs =
			[
				'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));
			}
		}
	}
}