在 php 8 中构建一个计算器,最稳健的做法是分离关注点:将输入解析、语法验证和数学运算逻辑拆分为独立的函数。直接编写包含所有逻辑的长函数会导致代码难以维护且容易出错。通过组合多个小型函数,可以清晰地处理运算符优先级和括号嵌套问题。

核心组件设计
计算器的核心在于如何处理字符串形式的数学表达式。我们需要一个词法分析器(Tokenizer)将字符串拆解为数字和运算符的列表,一个验证器(Validator)检查表达式的合法性,以及一个求值器(Evaluator)根据运算符优先级进行计算。这种模块化设计允许单独测试每个部分,确保整体系统的稳定性。

在实际实现中,词法分析器负责识别整数、浮点数以及加减乘除符号。验证器则检查括号是否匹配、运算符前后是否有操作数等常见错误。求值器通常使用栈结构或递归下降算法来处理复杂的表达式结构,确保先计算括号内的内容,再处理高优先级的乘除运算,最后处理加减运算。
修复 Core bailout 状态损坏安全问题,以及 BZ2、DOM、PDO、Opcache/JIT 内存与执行错误,改进 Unicode 字素处理、URL 解析、TLS 流状态、信号分发及会话错误处理。
立即学习“PHP免费学习笔记(深入)”;
完整实现与运行
以下代码展示了如何使用 PHP 8 的特性实现一个支持加减乘除和括号的计算器。该程序包含完整的词法分析、验证和求值逻辑,可以直接复制运行。
<?php
namespace Calculator;
class SimpleCalculator {
private array $tokens;
private int $pos;
public function calculate(string $expression): float {
$this->tokens = $this->tokenize($expression);
$this->pos = 0;
$result = $this->parseExpression();
if ($this->pos !== count($this->tokens)) {
throw new \Exception("Unexpected token at position: {$this->pos}");
}
return $result;
}
private function tokenize(string $expression): array {
$pattern = '/(\d+\.?\d*|[+\-*/()])/';
preg_match_all($pattern, $expression, $matches);
return $matches[0] ?? [];
}
private function parseExpression(): float {
$result = $this->parseTerm();
while ($this->pos < count($this->tokens) && in_array($this->tokens[$this->pos], ['+', '-'])) {
$op = $this->tokens[$this->pos++];
$right = $this->parseTerm();
$result = $op === '+' ? $result + $right : $result - $right;
}
return $result;
}
private function parseTerm(): float {
$result = $this->parseFactor();
while ($this->pos < count($this->tokens) && in_array($this->tokens[$this->pos], ['*', '/'])) {
$op = $this->tokens[$this->pos++];
$right = $this->parseFactor();
$result = $op === '*' ? $result * $right : $result / $right;
}
return $result;
}
private function parseFactor(): float {
if ($this->pos >= count($this->tokens)) {
throw new \Exception("Unexpected end of expression");
}
$token = $this->tokens[$this->pos];
if ($token === '(') {
$this->pos++; // skip '('
$result = $this->parseExpression();
if ($this->pos >= count($this->tokens) || $this->tokens[$this->pos] !== ')') {
throw new \Exception("Missing closing parenthesis");
}
$this->pos++; // skip ')'
return $result;
} elseif (is_numeric($token)) {
$this->pos++;
return (float)$token;
} else {
throw new \Exception("Unexpected token: $token");
}
}
}
// 示例用法
try {
$calc = new SimpleCalculator();
echo $calc->calculate("(2 + 3) * 4") . "\n"; // 输出 20
echo $calc->calculate("10 / 2 + 3") . "\n"; // 输出 8
} catch (\Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
运行上述代码时,程序会实例化 SimpleCalculator 类并调用 calculate 方法。对于输入 (2 + 3) * 4,词法分析器将其拆解为 ["(", "2", "+", "3", ")", "*", "4"]。求值过程首先处理括号内的加法得到 5,然后乘以 4 得到最终结果 20。对于 10 / 2 + 3,由于乘法除法优先级高于加法,先计算 10 / 2 得到 5,再加 3 得到 8。如果输入非法表达式如 2 ++ 3,验证器会在词法分析或求值阶段抛出异常。
处理浮点数精度问题
当计算器需要处理涉及小数的复杂运算时,PHP 默认的浮点数运算可能会产生精度误差。例如,0.1 + 0.2 的结果可能不是精确的 0.3。为解决此问题,可以在求值器中使用 bcmath 扩展或 gmp 扩展进行高精度计算。具体做法是将浮点数转换为字符串,使用 bcadd 或 bcmul 等函数进行运算,并指定小数位数。这样能确保金融计算或科学计算中的数值准确性,避免累积误差影响最终结果。


















