chriskonnertz/string-calc
StringCalc is a lightweight PHP library that evaluates mathematical expressions passed as strings, turning terms like 1+1 or sqrt(16)*2 into results. It supports basic operators, brackets, and common functions, with no dependencies—ideal for calculators and formula inputs.
Install with Composer:
composer require chriskonnertz/string-calc
The smallest useful integration is the StringCalc facade:
use ChrisKonnertz\StringCalc\StringCalc;
$calc = new StringCalc();
echo $calc->calculate('(2 + 3) * 4 / 2'); // 10
echo $calc->calculate('sqrt(16) + pow(2, 3)'); // 12
Where to look first:
README.md for the supported operators, functions, and constants (pi, e, sin, cos, sqrt, log, etc.).src/StringCalc.php — the facade that wires the tokenizer, parser, and calculator together.src/Symbols/Concrete — the actual operator, function, and constant implementations. This is the best reference when you need to add your own.src/Exceptions — the failure modes you will need to catch at your application boundary.The first real use case is almost always replacing eval() in a feature where an admin, customer, or configuration author types a formula: pricing rules, discount expressions, margin calculations, dynamic form fields, or spreadsheet-style inputs. StringCalc gives you arithmetic parsing without the arbitrary-code-execution risk of eval().
Wrap it in one application service. Construct StringCalc once per request (or register it as a singleton in the container) and inject a small wrapper rather than scattering new StringCalc() through controllers:
final class FormulaEvaluator
{
public function __construct(private StringCalc $calc = new StringCalc()) {}
public function evaluate(string $expression): float
{
return (float) $this->calc->calculate($expression);
}
}
Validate before you calculate. Treat the expression as untrusted input: enforce a maximum length, reject unexpected characters, and keep the grammar to what the package supports. This protects you from accidental pathological input and gives users a better message than a parser exception.
Map library exceptions to domain errors. Catch \Throwable at the boundary, then narrow to the library’s exception classes under ChrisKonnertz\StringCalc\Exceptions. Return a validation error (“The formula is not valid”) rather than leaking parser internals.
Substitute variables yourself. The package evaluates literal expressions; it is not a variable-binding formula engine. For templates like price * quantity or {price} * {qty}, replace placeholders with numeric literals before calling calculate(). Validate each substituted value is numeric, and use a strict placeholder syntax so user text cannot smuggle in operators.
$expression = strtr($template, [
'{price}' => (string) $price,
'{qty}' => (string) $quantity,
]);
$result = $evaluator->evaluate($expression);
Add custom symbols when the built-in set is insufficient. Extend the abstract operator/function/constant classes in src/Symbols, set the identifier and arity, and implement the execution method. Register the symbol on the container before the first calculation:
$calc->getContainer()->getSymbols()->add(new MyCustomFunction());
Check the corresponding Abstract* class for the exact property and method signatures; they are the contract.
Round at the boundary, not inside the math. Keep full precision while composing expressions, then apply round() or a money formatter only when presenting the result.
Test with a data provider. A table of valid expressions and expected outputs, plus a table of invalid expressions and expected exception types, catches most regressions and documents the supported grammar for your team.
Cache repeated evaluations. If the same static formula is evaluated many times (for example, a stored pricing rule), cache by expression string plus substituted inputs. There is no compiled-expression API, so caching the evaluated result is the practical optimization.
% is modulo, not percentage. If users type 10%, they probably mean 0.10. Handle percentage semantics in your UI or preprocessor; do not assume the parser does it.2 * (3 + 4), not 2(3 + 4), and normalize user input if you want to accept the latter.1,5 + 2 is not 1.5 + 2. Normalize commas to dots only when you are certain the comma is a decimal separator and not a function argument separator.0.1 + 0.2 produces 0.30000000000000004. Round before display and compare with an epsilon in tests.sqrt(-1), and log(0) may throw or produce non-finite values depending on the operation. Validate inputs and check results with is_finite().-2^2 and 2 * -3 are common sources of disagreement between what a user expects and what the grammar defines. Test these explicitly.StringCalc instance across tenants with different custom symbols; construct per configuration.How can I help you explore Laravel packages today?