Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@ It can take a string with a math expression and parses it so it can be evaluated

The packages supports operations like addition, subtraction, multiplication and division.

Changes in comparison to original project
-----------------------------------------

- allow multi-character variable names
- allow reuse of formula object:
```php
$formula = new Formula('a + b');
$formula->setVariable('a', 1);
$formula->setVariable('b', 41);
$c = $formula->calculate(); // 42
$formula->setVariable('a', 3);
$formula->setVariable('b', 1334);
$d = $formula->calculate(); // 1337
```
- remove try-catch to hide exceptions from stdout (e.g. website)
- bugfix for formulas that use brackets like `(a + b) * c`
- remove `round(..., 2)` for divisions to avoid minor calculation errors
- changed division-by-zero behavior from PHP's error to giving result `NAN` (a PHP constant) because I prefer silent error handling in this case ;-)
- added exception if a variable is undefined (missing `setVariable(...)` statement)

Installation
------------

Expand Down
2 changes: 1 addition & 1 deletion src/ExpressionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public static function factory(string $expression, $variables = [])
return new Double( $expression );
} else if ( strpos( $expression, '%' ) !== false ) {
return new Percent( $expression );
} else if ( ( int ) $expression > 0 ) {
} else if ( is_numeric($expression) ) {
return new Integer( $expression );
} else {
$variable = new Variable( $expression );
Expand Down
46 changes: 25 additions & 21 deletions src/Formula.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ class Formula
*/
protected $source = '';

/**
* @var string
*/
protected $parsed = '';

/**
* @var Operator
*/
Expand Down Expand Up @@ -64,7 +69,7 @@ public function getSource(): string
*/
public function getExpression(): ?Operator
{
$key = substr($this->source, 1, -1);
$key = substr($this->parsed, 1, -1);

if (array_key_exists($key, $this->expressions)) {
return $this->expressions[$key];
Expand Down Expand Up @@ -128,10 +133,13 @@ private function getExpressionObject(string $expression): Expression
*/
public function parse()
{
$this->parsed = $this->source;
$this->expressions = [];

$operandPattern = '([\d\.,%]+|[^\{\}\.,%\(\)\*\/\+-]+|\{[\w\d]+\})';
$patterns = [
'/(([\d\.,%]+|[^\{\}]|[\{\w\d\}]+)(\*)([\d\.,%]+|[^\{\}]|[\{\w\d\}]+))/i',
'/(([\d\.,%]+|[^\{\}]|[\{\w\d\}]+)(\/)([\d\.,%]+|[^\{\}]|[\{\w\d\}]+))/i',
'/(([\d\.,%]+|[^\{\}]|[\{\w\d\}]+)([\+|-])([\d\.,%]+|[^\{\}]|[\{\w\d\}]+))/i',
'/('.$operandPattern.'(\*|\/)'.$operandPattern.')/i',
'/('.$operandPattern.'(\+|-)'.$operandPattern.')/i'
];

$operators = [
Expand All @@ -142,34 +150,30 @@ public function parse()
];

// Выражение в скобках
while (preg_match('/\(((?:(?>[^()]+)|(?R))*)\)/i', $this->source, $results)) {
while (preg_match('/\(((?:(?>[^()]+)|(?R))*)\)/i', $this->parsed, $results)) {
$key = $this->generateKey();
$formula = new static($results[1]);
foreach ($this->variables as $key => $var) {
$formula->setVariable($key, $var);
foreach ($this->variables as $varKey => $var) {
$formula->setVariable($varKey, $var);
}
$formula->parse();

$this->expressions[$key] = $formula->getExpression();
$this->source = str_replace($results[0], '{' . $key . '}', $this->source);
$this->parsed = str_replace($results[0], '{' . $key . '}', $this->parsed);
}

foreach ($patterns as $pattern) {
while (preg_match($pattern, $this->source, $results)) {
try {
$left = $this->getExpressionObject( $results[2] );
$right = $this->getExpressionObject( $results[4] );
$key = $this->generateKey();

$this->expressions[ $key ] = new $operators[$results[3]]( $left, $right );

$this->source = str_replace( $results[1], '{' . $key . '}', $this->source );
} catch(\Exception $e) {
echo $e->getMessage();
}
while (preg_match($pattern, $this->parsed, $results)) {
$left = $this->getExpressionObject( $results[2] );
$right = $this->getExpressionObject( $results[4] );
$key = $this->generateKey();

$this->expressions[ $key ] = new $operators[$results[3]]( $left, $right );

$this->parsed = str_replace( $results[1], '{' . $key . '}', $this->parsed );
}
}

$this->result = $this->getExpressionObject( $this->source );
$this->result = $this->getExpressionObject( $this->parsed );
}
}
2 changes: 1 addition & 1 deletion src/expression/Division.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ class Division extends Operator
*/
public function doCalculate(float $left, float $right): float
{
return round($left / $right, 2);
return $right == 0 ? NAN : $left / $right;
}
}
5 changes: 4 additions & 1 deletion src/operator/Variable.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class Variable extends Expression
{
protected $key;

public function __construct(string $key, string $value = '0')
public function __construct(string $key, string $value = '')
{
$this->key = $key;
parent::__construct($value);
Expand All @@ -26,6 +26,9 @@ public function setValue(string $value)
*/
public function calculate(Operator $operator): float
{
if($this->value === ''){
throw new \BadMethodCallException('undefined variable: '.$this->key);
}
$this->value = str_replace(',', '.', $this->value);
return (float) $this->value;
}
Expand Down