Fix 3 loi nghiem trong: eval() -> safe parser, Contract::saved() infinite loop, DB Transaction for schedule generation
This commit is contained in:
@@ -72,35 +72,116 @@ class MailMergeService
|
||||
$evalExpression = $expression;
|
||||
foreach ($values as $key => $value) {
|
||||
if (is_numeric($value)) {
|
||||
$evalExpression = str_replace($key, (float) $value, $evalExpression);
|
||||
$evalExpression = str_replace($key, $value, $evalExpression);
|
||||
}
|
||||
}
|
||||
|
||||
// Chỉ cho phép số và các phép toán cơ bản
|
||||
// Chỉ cho phép số, dấu chấm, dấu phẩy và các phép toán cơ bản
|
||||
$evalExpression = str_replace(',', '.', $evalExpression);
|
||||
$evalExpression = preg_replace('/[^0-9.\+\-\*\/\(\)\s]/', '', $evalExpression);
|
||||
$evalExpression = str_replace(' ', '', $evalExpression);
|
||||
|
||||
if (empty($evalExpression)) return 0;
|
||||
|
||||
try {
|
||||
// Eval an toàn với chỉ phép toán
|
||||
$result = self::safeEval($evalExpression);
|
||||
$result = self::safeCalculate($evalExpression);
|
||||
return (float) $result;
|
||||
} catch (\Throwable $e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected static function safeEval(string $expression): float
|
||||
protected static function safeCalculate(string $expression): float
|
||||
{
|
||||
// Loại bỏ các hàm nguy hiểm, chỉ giữ phép toán
|
||||
$expression = preg_replace('/[^0-9.\+\-\*\/\(\)\s]/', '', $expression);
|
||||
// Tokenize: tách số và operators
|
||||
$tokens = [];
|
||||
$number = '';
|
||||
|
||||
for ($i = 0; $i < strlen($expression); $i++) {
|
||||
$char = $expression[$i];
|
||||
|
||||
if (ctype_digit($char) || $char === '.') {
|
||||
$number .= $char;
|
||||
} else {
|
||||
if ($number !== '') {
|
||||
$tokens[] = (float) $number;
|
||||
$number = '';
|
||||
}
|
||||
$tokens[] = $char;
|
||||
}
|
||||
}
|
||||
|
||||
if ($number !== '') {
|
||||
$tokens[] = (float) $number;
|
||||
}
|
||||
|
||||
if (empty($expression) || preg_match('/[a-zA-Z]/', $expression)) {
|
||||
// Shunting yard algorithm: infix → postfix
|
||||
$output = [];
|
||||
$stack = [];
|
||||
$precedence = ['+' => 1, '-' => 1, '*' => 2, '/' => 2];
|
||||
|
||||
foreach ($tokens as $token) {
|
||||
if (is_numeric($token)) {
|
||||
$output[] = $token;
|
||||
} elseif ($token === '(') {
|
||||
$stack[] = $token;
|
||||
} elseif ($token === ')') {
|
||||
while (!empty($stack) && end($stack) !== '(') {
|
||||
$output[] = array_pop($stack);
|
||||
}
|
||||
array_pop($stack); // pop '('
|
||||
} else {
|
||||
// Operator
|
||||
while (!empty($stack) && end($stack) !== '(' &&
|
||||
isset($precedence[end($stack)]) &&
|
||||
$precedence[end($stack)] >= $precedence[$token]) {
|
||||
$output[] = array_pop($stack);
|
||||
}
|
||||
$stack[] = $token;
|
||||
}
|
||||
}
|
||||
|
||||
while (!empty($stack)) {
|
||||
$output[] = array_pop($stack);
|
||||
}
|
||||
|
||||
// Evaluate postfix
|
||||
$evalStack = [];
|
||||
|
||||
foreach ($output as $token) {
|
||||
if (is_numeric($token)) {
|
||||
$evalStack[] = $token;
|
||||
} else {
|
||||
$b = array_pop($evalStack);
|
||||
$a = array_pop($evalStack);
|
||||
|
||||
if ($a === null || $b === null) {
|
||||
throw new \InvalidArgumentException('Invalid expression');
|
||||
}
|
||||
|
||||
switch ($token) {
|
||||
case '+':
|
||||
$evalStack[] = bcadd((string) $a, (string) $b, 10);
|
||||
break;
|
||||
case '-':
|
||||
$evalStack[] = bcsub((string) $a, (string) $b, 10);
|
||||
break;
|
||||
case '*':
|
||||
$evalStack[] = bcmul((string) $a, (string) $b, 10);
|
||||
break;
|
||||
case '/':
|
||||
if ((float) $b == 0) throw new \InvalidArgumentException('Division by zero');
|
||||
$evalStack[] = bcdiv((string) $a, (string) $b, 10);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($evalStack) !== 1) {
|
||||
throw new \InvalidArgumentException('Invalid expression');
|
||||
}
|
||||
|
||||
// Dùng bc math nếu có, hoặc eval đơn giản
|
||||
return (float) eval('return ' . $expression . ';');
|
||||
return (float) $evalStack[0];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user