PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.83
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.83
51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 51.1.46 51.1.47 51.1.49 All 37 releases
king-addons / includes / widgets / Form_Builder / helpers / Formula.php

Formula.php in King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder 51.1.83, at includes/widgets/Form_Builder/helpers/Formula.php

162 lines 4.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Arithmetic evaluator shared by the calculation field and payments.
4 *
5 * @package King_Addons
6 */
7
8 namespace King_Addons;
9
10 if (!defined('ABSPATH')) {
11 exit;
12 }
13
14 /**
15 * Evaluates "1 + 2 * (3 - 4)" without eval().
16 *
17 * The browser has the same rules in script.js; this copy exists so a price can
18 * be worked out again on the server rather than taken from the request.
19 */
20 class Form_Formula
21 {
22 /**
23 * Work out a formula, substituting [field_id] with submitted values.
24 *
25 * @param string $formula Formula as the author wrote it.
26 * @param array<string,string> $values Field id => submitted value.
27 *
28 * @return float|null Result, or null when the formula is not valid.
29 */
30 public static function evaluate(string $formula, array $values): ?float
31 {
32 $resolved = preg_replace_callback(
33 '/\[([^\]]+)\]/',
34 static function ($matches) use ($values) {
35 $key = trim($matches[1]);
36 // Submitted names use resolve_field_key(); older payloads
37 // prefixed the same id with form_field-.
38 $raw = $values[$key] ?? $values['form_field-' . $key] ?? '0';
39 $number = (float) str_replace(',', '.', (string) $raw);
40
41 return '(' . $number . ')';
42 },
43 $formula
44 );
45
46 return self::compute((string) $resolved);
47 }
48
49 /**
50 * Evaluate an expression of numbers, + - * / and parentheses.
51 *
52 * @param string $expression Expression.
53 *
54 * @return float|null
55 */
56 public static function compute(string $expression): ?float
57 {
58 preg_match_all('/\d+\.?\d*|[-+*\/()]/', $expression, $matches);
59 $tokens = $matches[0];
60
61 if (empty($tokens)) {
62 return null;
63 }
64
65 // Anything the tokenizer skipped means the expression held something
66 // else - a letter, a symbol - and the whole thing is rejected.
67 if (implode('', $tokens) !== preg_replace('/\s+/', '', $expression)) {
68 return null;
69 }
70
71 $precedence = ['+' => 1, '-' => 1, '*' => 2, '/' => 2];
72 $output = [];
73 $operators = [];
74 $previous = null;
75
76 foreach ($tokens as $token) {
77 if (is_numeric($token)) {
78 $output[] = (float) $token;
79 } elseif ('(' === $token) {
80 $operators[] = $token;
81 } elseif (')' === $token) {
82 while (!empty($operators) && '(' !== end($operators)) {
83 $output[] = array_pop($operators);
84 }
85
86 if (empty($operators)) {
87 return null;
88 }
89
90 array_pop($operators);
91 } else {
92 // A leading minus is a sign, not an operator.
93 if ('-' === $token && (null === $previous || '(' === $previous || isset($precedence[$previous]))) {
94 $output[] = 0.0;
95 }
96
97 while (
98 !empty($operators)
99 && '(' !== end($operators)
100 && $precedence[end($operators)] >= $precedence[$token]
101 ) {
102 $output[] = array_pop($operators);
103 }
104
105 $operators[] = $token;
106 }
107
108 $previous = $token;
109 }
110
111 while (!empty($operators)) {
112 $last = array_pop($operators);
113 if ('(' === $last) {
114 return null;
115 }
116 $output[] = $last;
117 }
118
119 $stack = [];
120
121 foreach ($output as $item) {
122 if (is_float($item)) {
123 $stack[] = $item;
124 continue;
125 }
126
127 if (count($stack) < 2) {
128 return null;
129 }
130
131 $right = array_pop($stack);
132 $left = array_pop($stack);
133
134 switch ($item) {
135 case '+':
136 $stack[] = $left + $right;
137 break;
138 case '-':
139 $stack[] = $left - $right;
140 break;
141 case '*':
142 $stack[] = $left * $right;
143 break;
144 case '/':
145 if (0.0 === $right) {
146 return null;
147 }
148 $stack[] = $left / $right;
149 break;
150 default:
151 return null;
152 }
153 }
154
155 if (1 !== count($stack) || !is_finite($stack[0])) {
156 return null;
157 }
158
159 return (float) $stack[0];
160 }
161 }
162