PluginProbe
WowAddons – Product Addons and Product Options With Custom Fields / trunk
WowAddons – Product Addons and Product Options With Custom Fields vtrunk
1.7.2 1.7.1 1.7.0 1.6.21 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.5.10 1.5.11 1.5.2 1.5.3 1.5.4 1.5.5 All 57 releases
product-addons / includes / common / class-safe-math-evaluator.php

class-safe-math-evaluator.php in WowAddons – Product Addons and Product Options With Custom Fields trunk, at includes/common/class-safe-math-evaluator.php

170 lines 5.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php //phpcs:ignore
2 /**
3 * Class SafeMathEvaluator
4 *
5 * @package WowAddons
6 */
7
8 namespace PRAD\Includes\Common;
9
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit; // Exit if accessed directly.
12 }
13
14 /**
15 * SafeMathEvaluator class.
16 */
17 class SafeMathEvaluator {
18
19 /**
20 * Evaluates a mathematical expression safely, replacing dynamic variables and handling percentages.
21 *
22 * @param string $expression The mathematical expression to evaluate.
23 * @param array $dynamic_variables Associative array of dynamic variables to replace in the expression.
24 * @return float The evaluated result or 0 on error.
25 */
26 public static function evaluate_expression( $expression, $dynamic_variables = array() ) {
27 $expression = sanitize_text_field( $expression );
28
29 // Replace dynamic variables.
30 $processed_expression = preg_replace_callback(
31 '/\{\{([a-zA-Z0-9_-]+)\}\}/',
32 function ( $matches ) use ( $dynamic_variables ) {
33 return ! empty( $dynamic_variables[ $matches[1] ] ) ? $dynamic_variables[ $matches[1] ] : 0;
34 },
35 $expression
36 );
37
38 // Handle percentages: convert 'X%' to '(X/100)'.
39 $processed_expression = preg_replace( '/(\d+(?:\.\d+)?)%/', '($1/100)', $processed_expression );
40
41 try {
42 $result = self::safe_evaluate( $processed_expression );
43 return is_numeric( $result ) && $result >= 0 ? (float) $result : 0;
44 } catch ( \Exception $e ) {
45 echo esc_html( 'Error evaluating expression: ' . $e->getMessage() );
46 return 0;
47 }
48 }
49
50 /**
51 * Safely evaluates a sanitized mathematical expression.
52 *
53 * @param string $expression The sanitized mathematical expression to evaluate.
54 * @return float The evaluated result.
55 * @throws \Exception If the expression contains invalid characters or unbalanced parentheses.
56 */
57 public static function safe_evaluate( $expression ) {
58 // Remove whitespace.
59 $expression = preg_replace( '/\s+/', '', $expression );
60
61 // Validate expression contains only allowed characters.
62 if ( ! preg_match( '/^[0-9+\-*\/().]+$/', $expression ) ) {
63 throw new \Exception( 'Invalid characters in expression' );
64 }
65
66 // Check for balanced parentheses.
67 if ( ! self::has_balanced_parentheses( $expression ) ) {
68 throw new \Exception( 'Unbalanced parentheses' );
69 }
70
71 // Evaluate the expression.
72 return self::parse_expression( $expression );
73 }
74
75 /**
76 * Checks if the given expression has balanced parentheses.
77 *
78 * @param string $expression The expression to check.
79 * @return bool True if parentheses are balanced, false otherwise.
80 */
81 public static function has_balanced_parentheses( $expression ) {
82 $count = 0;
83 $exp_length = strlen( $expression );
84 for ( $i = 0; $i < $exp_length; $i++ ) {
85 if ( '(' === $expression[ $i ] ) {
86 ++$count;
87 } elseif ( ')' === $expression[ $i ] ) {
88 --$count;
89 if ( $count < 0 ) {
90 return false;
91 }
92 }
93 }
94 return 0 === $count;
95 }
96
97 /**
98 * Parses and evaluates a mathematical expression, handling parentheses recursively.
99 *
100 * @param string $expression The mathematical expression to parse and evaluate.
101 * @return float The evaluated result.
102 * @throws \Exception If the expression is invalid.
103 */
104 public static function parse_expression( $expression ) {
105 // Handle parentheses first.
106 while ( strpos( $expression, '(' ) !== false ) {
107 $expression = preg_replace_callback(
108 '/\(([^()]+)\)/',
109 function ( $matches ) {
110 return self::evaluate_simple_expression( $matches[1] );
111 },
112 $expression
113 );
114 }
115
116 return self::evaluate_simple_expression( $expression );
117 }
118
119 /**
120 * Evaluates a simple mathematical expression without parentheses.
121 *
122 * Handles multiplication, division, addition, and subtraction operations.
123 *
124 * @param string $expression The mathematical expression to evaluate.
125 * @return float The evaluated result.
126 * @throws \Exception If the expression is invalid or division by zero occurs.
127 */
128 public static function evaluate_simple_expression( $expression ) {
129 // Handle multiplication and division first (left to right).
130 while ( preg_match( '/(-?\d+(?:\.\d+)?)\s*([*\/])\s*(-?\d+(?:\.\d+)?)/', $expression, $matches ) ) {
131 $left = (float) $matches[1];
132 $operator = $matches[2];
133 $right = (float) $matches[3];
134
135 if ( '*' === $operator ) {
136 $result = $left * $right;
137 } elseif ( '/' === $operator ) {
138 if ( 0 == $right ) {
139 throw new \Exception( 'Division by zero' );
140 }
141 $result = $left / $right;
142 }
143
144 $expression = str_replace( $matches[0], $result, $expression );
145 }
146
147 // Handle addition and subtraction (left to right).
148 while ( preg_match( '/(-?\d+(?:\.\d+)?)\s*([+\-])\s*(-?\d+(?:\.\d+)?)/', $expression, $matches ) ) {
149 $left = (float) $matches[1];
150 $operator = $matches[2];
151 $right = (float) $matches[3];
152
153 if ( '+' === $operator ) {
154 $result = $left + $right;
155 } elseif ( '-' === $operator ) {
156 $result = $left - $right;
157 }
158
159 $expression = str_replace( $matches[0], $result, $expression );
160 }
161
162 // Should be left with just a number.
163 if ( ! is_numeric( $expression ) ) {
164 throw new \Exception( 'Invalid expression result: ' . esc_html( $expression ) );
165 }
166
167 return (float) $expression;
168 }
169 }
170