PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 4.8.0
WooCommerce v4.8.0
10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / includes / libraries / class-wc-eval-math.php
woocommerce / includes / libraries Last commit date
class-wc-eval-math.php 6 years ago wp-async-request.php 8 years ago wp-background-process.php 6 years ago
class-wc-eval-math.php
406 lines
1 <?php
2
3 use Automattic\Jetpack\Constants;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit;
7 }
8
9 if ( ! class_exists( 'WC_Eval_Math', false ) ) {
10 /**
11 * Class WC_Eval_Math. Supports basic math only (removed eval function).
12 *
13 * Based on EvalMath by Miles Kaufman Copyright (C) 2005 Miles Kaufmann http://www.twmagic.com/.
14 */
15 class WC_Eval_Math {
16
17 /**
18 * Last error.
19 *
20 * @var string
21 */
22 public static $last_error = null;
23
24 /**
25 * Variables (and constants).
26 *
27 * @var array
28 */
29 public static $v = array( 'e' => 2.71, 'pi' => 3.14 );
30
31 /**
32 * User-defined functions.
33 *
34 * @var array
35 */
36 public static $f = array();
37
38 /**
39 * Constants.
40 *
41 * @var array
42 */
43 public static $vb = array( 'e', 'pi' );
44
45 /**
46 * Built-in functions.
47 *
48 * @var array
49 */
50 public static $fb = array();
51
52 /**
53 * Evaluate maths string.
54 *
55 * @param string $expr
56 * @return mixed
57 */
58 public static function evaluate( $expr ) {
59 self::$last_error = null;
60 $expr = trim( $expr );
61 if ( substr( $expr, -1, 1 ) == ';' ) {
62 $expr = substr( $expr, 0, strlen( $expr ) -1 ); // strip semicolons at the end
63 }
64 // ===============
65 // is it a variable assignment?
66 if ( preg_match( '/^\s*([a-z]\w*)\s*=\s*(.+)$/', $expr, $matches ) ) {
67 if ( in_array( $matches[1], self::$vb ) ) { // make sure we're not assigning to a constant
68 return self::trigger( "cannot assign to constant '$matches[1]'" );
69 }
70 if ( ( $tmp = self::pfx( self::nfx( $matches[2] ) ) ) === false ) {
71 return false; // get the result and make sure it's good
72 }
73 self::$v[ $matches[1] ] = $tmp; // if so, stick it in the variable array
74 return self::$v[ $matches[1] ]; // and return the resulting value
75 // ===============
76 // is it a function assignment?
77 } elseif ( preg_match( '/^\s*([a-z]\w*)\s*\(\s*([a-z]\w*(?:\s*,\s*[a-z]\w*)*)\s*\)\s*=\s*(.+)$/', $expr, $matches ) ) {
78 $fnn = $matches[1]; // get the function name
79 if ( in_array( $matches[1], self::$fb ) ) { // make sure it isn't built in
80 return self::trigger( "cannot redefine built-in function '$matches[1]()'" );
81 }
82 $args = explode( ",", preg_replace( "/\s+/", "", $matches[2] ) ); // get the arguments
83 if ( ( $stack = self::nfx( $matches[3] ) ) === false ) {
84 return false; // see if it can be converted to postfix
85 }
86 $stack_size = count( $stack );
87 for ( $i = 0; $i < $stack_size; $i++ ) { // freeze the state of the non-argument variables
88 $token = $stack[ $i ];
89 if ( preg_match( '/^[a-z]\w*$/', $token ) and ! in_array( $token, $args ) ) {
90 if ( array_key_exists( $token, self::$v ) ) {
91 $stack[ $i ] = self::$v[ $token ];
92 } else {
93 return self::trigger( "undefined variable '$token' in function definition" );
94 }
95 }
96 }
97 self::$f[ $fnn ] = array( 'args' => $args, 'func' => $stack );
98 return true;
99 // ===============
100 } else {
101 return self::pfx( self::nfx( $expr ) ); // straight up evaluation, woo
102 }
103 }
104
105 /**
106 * Convert infix to postfix notation.
107 *
108 * @param string $expr
109 *
110 * @return array|string
111 */
112 private static function nfx( $expr ) {
113
114 $index = 0;
115 $stack = new WC_Eval_Math_Stack;
116 $output = array(); // postfix form of expression, to be passed to pfx()
117 $expr = trim( $expr );
118
119 $ops = array( '+', '-', '*', '/', '^', '_' );
120 $ops_r = array( '+' => 0, '-' => 0, '*' => 0, '/' => 0, '^' => 1 ); // right-associative operator?
121 $ops_p = array( '+' => 0, '-' => 0, '*' => 1, '/' => 1, '_' => 1, '^' => 2 ); // operator precedence
122
123 $expecting_op = false; // we use this in syntax-checking the expression
124 // and determining when a - is a negation
125 if ( preg_match( "/[^\w\s+*^\/()\.,-]/", $expr, $matches ) ) { // make sure the characters are all good
126 return self::trigger( "illegal character '{$matches[0]}'" );
127 }
128
129 while ( 1 ) { // 1 Infinite Loop ;)
130 $op = substr( $expr, $index, 1 ); // get the first character at the current index
131 // find out if we're currently at the beginning of a number/variable/function/parenthesis/operand
132 $ex = preg_match( '/^([A-Za-z]\w*\(?|\d+(?:\.\d*)?|\.\d+|\()/', substr( $expr, $index ), $match );
133 // ===============
134 if ( '-' === $op and ! $expecting_op ) { // is it a negation instead of a minus?
135 $stack->push( '_' ); // put a negation on the stack
136 $index++;
137 } elseif ( '_' === $op ) { // we have to explicitly deny this, because it's legal on the stack
138 return self::trigger( "illegal character '_'" ); // but not in the input expression
139 // ===============
140 } elseif ( ( in_array( $op, $ops ) or $ex ) and $expecting_op ) { // are we putting an operator on the stack?
141 if ( $ex ) { // are we expecting an operator but have a number/variable/function/opening parenthesis?
142 $op = '*';
143 $index--; // it's an implicit multiplication
144 }
145 // heart of the algorithm:
146 while ( $stack->count > 0 and ( $o2 = $stack->last() ) and in_array( $o2, $ops ) and ( $ops_r[ $op ] ? $ops_p[ $op ] < $ops_p[ $o2 ] : $ops_p[ $op ] <= $ops_p[ $o2 ] ) ) {
147 $output[] = $stack->pop(); // pop stuff off the stack into the output
148 }
149 // many thanks: https://en.wikipedia.org/wiki/Reverse_Polish_notation#The_algorithm_in_detail
150 $stack->push( $op ); // finally put OUR operator onto the stack
151 $index++;
152 $expecting_op = false;
153 // ===============
154 } elseif ( ')' === $op && $expecting_op ) { // ready to close a parenthesis?
155 while ( ( $o2 = $stack->pop() ) != '(' ) { // pop off the stack back to the last (
156 if ( is_null( $o2 ) ) {
157 return self::trigger( "unexpected ')'" );
158 } else {
159 $output[] = $o2;
160 }
161 }
162 if ( preg_match( "/^([A-Za-z]\w*)\($/", $stack->last( 2 ), $matches ) ) { // did we just close a function?
163 $fnn = $matches[1]; // get the function name
164 $arg_count = $stack->pop(); // see how many arguments there were (cleverly stored on the stack, thank you)
165 $output[] = $stack->pop(); // pop the function and push onto the output
166 if ( in_array( $fnn, self::$fb ) ) { // check the argument count
167 if ( $arg_count > 1 ) {
168 return self::trigger( "too many arguments ($arg_count given, 1 expected)" );
169 }
170 } elseif ( array_key_exists( $fnn, self::$f ) ) {
171 if ( count( self::$f[ $fnn ]['args'] ) != $arg_count ) {
172 return self::trigger( "wrong number of arguments ($arg_count given, " . count( self::$f[ $fnn ]['args'] ) . " expected)" );
173 }
174 } else { // did we somehow push a non-function on the stack? this should never happen
175 return self::trigger( "internal error" );
176 }
177 }
178 $index++;
179 // ===============
180 } elseif ( ',' === $op and $expecting_op ) { // did we just finish a function argument?
181 while ( ( $o2 = $stack->pop() ) != '(' ) {
182 if ( is_null( $o2 ) ) {
183 return self::trigger( "unexpected ','" ); // oops, never had a (
184 } else {
185 $output[] = $o2; // pop the argument expression stuff and push onto the output
186 }
187 }
188 // make sure there was a function
189 if ( ! preg_match( "/^([A-Za-z]\w*)\($/", $stack->last( 2 ), $matches ) ) {
190 return self::trigger( "unexpected ','" );
191 }
192 $stack->push( $stack->pop() + 1 ); // increment the argument count
193 $stack->push( '(' ); // put the ( back on, we'll need to pop back to it again
194 $index++;
195 $expecting_op = false;
196 // ===============
197 } elseif ( '(' === $op and ! $expecting_op ) {
198 $stack->push( '(' ); // that was easy
199 $index++;
200 // ===============
201 } elseif ( $ex and ! $expecting_op ) { // do we now have a function/variable/number?
202 $expecting_op = true;
203 $val = $match[1];
204 if ( preg_match( "/^([A-Za-z]\w*)\($/", $val, $matches ) ) { // may be func, or variable w/ implicit multiplication against parentheses...
205 if ( in_array( $matches[1], self::$fb ) or array_key_exists( $matches[1], self::$f ) ) { // it's a func
206 $stack->push( $val );
207 $stack->push( 1 );
208 $stack->push( '(' );
209 $expecting_op = false;
210 } else { // it's a var w/ implicit multiplication
211 $val = $matches[1];
212 $output[] = $val;
213 }
214 } else { // it's a plain old var or num
215 $output[] = $val;
216 }
217 $index += strlen( $val );
218 // ===============
219 } elseif ( ')' === $op ) { // miscellaneous error checking
220 return self::trigger( "unexpected ')'" );
221 } elseif ( in_array( $op, $ops ) and ! $expecting_op ) {
222 return self::trigger( "unexpected operator '$op'" );
223 } else { // I don't even want to know what you did to get here
224 return self::trigger( "an unexpected error occurred" );
225 }
226 if ( strlen( $expr ) == $index ) {
227 if ( in_array( $op, $ops ) ) { // did we end with an operator? bad.
228 return self::trigger( "operator '$op' lacks operand" );
229 } else {
230 break;
231 }
232 }
233 while ( substr( $expr, $index, 1 ) == ' ' ) { // step the index past whitespace (pretty much turns whitespace
234 $index++; // into implicit multiplication if no operator is there)
235 }
236 }
237 while ( ! is_null( $op = $stack->pop() ) ) { // pop everything off the stack and push onto output
238 if ( '(' === $op ) {
239 return self::trigger( "expecting ')'" ); // if there are (s on the stack, ()s were unbalanced
240 }
241 $output[] = $op;
242 }
243 return $output;
244 }
245
246 /**
247 * Evaluate postfix notation.
248 *
249 * @param mixed $tokens
250 * @param array $vars
251 *
252 * @return mixed
253 */
254 private static function pfx( $tokens, $vars = array() ) {
255 if ( false == $tokens ) {
256 return false;
257 }
258 $stack = new WC_Eval_Math_Stack;
259
260 foreach ( $tokens as $token ) { // nice and easy
261 // if the token is a binary operator, pop two values off the stack, do the operation, and push the result back on
262 if ( in_array( $token, array( '+', '-', '*', '/', '^' ) ) ) {
263 if ( is_null( $op2 = $stack->pop() ) ) {
264 return self::trigger( "internal error" );
265 }
266 if ( is_null( $op1 = $stack->pop() ) ) {
267 return self::trigger( "internal error" );
268 }
269 switch ( $token ) {
270 case '+':
271 $stack->push( $op1 + $op2 );
272 break;
273 case '-':
274 $stack->push( $op1 - $op2 );
275 break;
276 case '*':
277 $stack->push( $op1 * $op2 );
278 break;
279 case '/':
280 if ( 0 == $op2 ) {
281 return self::trigger( 'division by zero' );
282 }
283 $stack->push( $op1 / $op2 );
284 break;
285 case '^':
286 $stack->push( pow( $op1, $op2 ) );
287 break;
288 }
289 // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
290 } elseif ( '_' === $token ) {
291 $stack->push( -1 * $stack->pop() );
292 // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
293 } elseif ( ! preg_match( "/^([a-z]\w*)\($/", $token, $matches ) ) {
294 if ( is_numeric( $token ) ) {
295 $stack->push( $token );
296 } elseif ( array_key_exists( $token, self::$v ) ) {
297 $stack->push( self::$v[ $token ] );
298 } elseif ( array_key_exists( $token, $vars ) ) {
299 $stack->push( $vars[ $token ] );
300 } else {
301 return self::trigger( "undefined variable '$token'" );
302 }
303 }
304 }
305 // when we're out of tokens, the stack should have a single element, the final result
306 if ( 1 != $stack->count ) {
307 return self::trigger( "internal error" );
308 }
309 return $stack->pop();
310 }
311
312 /**
313 * Trigger an error, but nicely, if need be.
314 *
315 * @param string $msg
316 *
317 * @return bool
318 */
319 private static function trigger( $msg ) {
320 self::$last_error = $msg;
321 if ( ! Constants::is_true( 'DOING_AJAX' ) && Constants::is_true( 'WP_DEBUG' ) ) {
322 echo "\nError found in:";
323 self::debugPrintCallingFunction();
324 trigger_error( $msg, E_USER_WARNING );
325 }
326 return false;
327 }
328
329 /**
330 * Prints the file name, function name, and
331 * line number which called your function
332 * (not this function, then one that called
333 * it to begin with)
334 */
335 private static function debugPrintCallingFunction() {
336 $file = 'n/a';
337 $func = 'n/a';
338 $line = 'n/a';
339 $debugTrace = debug_backtrace();
340 if ( isset( $debugTrace[1] ) ) {
341 $file = $debugTrace[1]['file'] ? $debugTrace[1]['file'] : 'n/a';
342 $line = $debugTrace[1]['line'] ? $debugTrace[1]['line'] : 'n/a';
343 }
344 if ( isset( $debugTrace[2] ) ) {
345 $func = $debugTrace[2]['function'] ? $debugTrace[2]['function'] : 'n/a';
346 }
347 echo "\n$file, $func, $line\n";
348 }
349 }
350
351 /**
352 * Class WC_Eval_Math_Stack.
353 */
354 class WC_Eval_Math_Stack {
355
356 /**
357 * Stack array.
358 *
359 * @var array
360 */
361 public $stack = array();
362
363 /**
364 * Stack counter.
365 *
366 * @var integer
367 */
368 public $count = 0;
369
370 /**
371 * Push value into stack.
372 *
373 * @param mixed $val
374 */
375 public function push( $val ) {
376 $this->stack[ $this->count ] = $val;
377 $this->count++;
378 }
379
380 /**
381 * Pop value from stack.
382 *
383 * @return mixed
384 */
385 public function pop() {
386 if ( $this->count > 0 ) {
387 $this->count--;
388 return $this->stack[ $this->count ];
389 }
390 return null;
391 }
392
393 /**
394 * Get last value from stack.
395 *
396 * @param int $n
397 *
398 * @return mixed
399 */
400 public function last( $n=1 ) {
401 $key = $this->count - $n;
402 return array_key_exists( $key, $this->stack ) ? $this->stack[ $key ] : null;
403 }
404 }
405 }
406