GPL-3.0 fork of Muqsit arithmexp with DevTools-compatible diagnostics for Axolotl-PM virions
- The virion manifest supports API
4.21.1and API5.0.0. - Composer requires PHP 8.1 or newer.
- The CI workflow runs PHPUnit and PHPStan at level max. The DevTools workflow builds a virion PHAR with PHP 8.4.
The fork's pending source revision removes unsafe rewrites of 0 / x, x / x, x ** y / x ** z, zero multiplied by an expression, exponentiation with variable operands, % 1 with a variable left operand, subtraction of identical variable expressions, reordering a negative left operand in addition, multiplication by one, addition with zero, subtraction by zero, division by one, unary plus, and NaN combined with a variable expression. These expressions now retain operand evaluation order or arithmetic conversion. A zero denominator raises DivisionByZeroError at evaluation time, as it does with the unoptimized parser. Several changes also preserve non-finite results or arithmetic result types in their affected cases. Earlier fork source could fold these expressions to a value and hide an error or change the result.
The default parser no longer registers ReorderExpressionOptimizer. A commutative operator does not guarantee that regrouping floating-point operands preserves the result. For example, x + y + z with x = 1e16, y = -1e16 and z = 1 now evaluates to 1 in both default and unoptimized parsers. The optimizer class remains available for explicit registration, but callers that opt in must validate its effects for their expressions. Division strength reduction also no longer cancels numeric factors across separate products, because that can change overflow and rounding. Other algebraic rewrites still need an error-preservation audit, so this release does not establish full equivalence between optimized and unoptimized evaluation.
Subtraction with a negative left operand now keeps its original grouping. Rewriting (-1) - x as a negated sum promoted the result from an integer to a float when x was PHP_INT_MAX.
Addition and subtraction with a negative right operand also keep their original operators. Rewriting x + (-y) as x - y, or x - (-y) as x + y, changed the result type and value when y was PHP_INT_MIN.
Nested min() and max() calls also retain their argument order and grouping. Flattening them changed results when an argument was NaN, because PHP's comparison behavior for NaN depends on evaluation order.
sqrt() now calls PHP's square-root function directly. Rewriting it as exponentiation changed the sign of negative zero.
The pending parser changes reject empty parentheses used as operands and argument separators consumed as operator operands with a ParseException. These malformed expressions previously could hang or expose an internal RuntimeException. Zero-argument calls such as pi() remain valid.
Tip
Try out arithmexp parser on the demo site!
The Composer package name remains muqsit/arithmexp, so composer require muqsit/arithmexp resolves the upstream package rather than this fork. Use this repository as a local virion source when you need the AxolotlBucketLabs maintenance branch.
Place the checkout in virions/arithmexp/ and declare it in the consuming plugin's devtools.yml:
virions:
- name: arithmexp
version: ^1.1.1See CHANGELOG.md for the tested consumer, behavior changes and rollback steps.
To evaluate a mathematical expression, create a Parser and invoke Parser::parse() with an expression string to obtain a reusable Expression object.
Expression::evaluate() returns the value of the expression.
$parser = Parser::createDefault();
$expression = $parser->parse("2 + 3");
var_dump($expression->evaluate()); // int(5)
$expression = $parser->parse("mt_rand()");
var_dump($expression->evaluate()); // int(1370501507)
var_dump($expression->evaluate()); // int(1522981420)Variables may be substituted at evaluation-time by passing an array<string, int|float|bool> value to Expression::evaluate().
$expression = $parser->parse("x + y");
var_dump($expression->evaluate(["x" => 2, "y" => 3])); // int(5)
var_dump($expression->evaluate(["x" => 1.5, "y" => 1.5])); // float(3)
$expression = $parser->parse("a > b or c");
var_dump($expression->evaluate(["a" => 1, "b" => 2, "c" => true])); // bool(true)The return value type of the evaluation follows PHP's arithmetic rules. int + int returns an int value, while float + int|float returns a float value. bool + bool returns an int value, while int || int returns a bool value.
Note
See CHANGELOG.md for fork-specific behavior changes and compatibility notes.