Expression executor, which allows to implement domain-specific language. This lib doesn’t contain any implemented operators or functions. Its only a framework, which allows you to build your own domain-specific language for expressions, with any functions, operators and typing system.
You can define your own operator, functions and variables. For example, you want to calc/execute expressions like:
MIN(5, 10.5) + NUMBER_OF_DAY(year: "2019", month: "01", day: "20") + PI * {{VARIABLE}} + ((-2) + 2.5) * 2
In example above
MIN
andNUMBER_OF_DAY
- functions{{VARIABLE}}
- variablePI
- syntax user-defined constant (for example, you can define TRUE, FALSE and NULL constants)+
and*
- operators"5"
,"10"
,"2019"
- strings in double-quotes5
,10.5
,(-2)
,2.5
,2
- int/float as is, but negative values should be wrapped in brackets
Also, it support arrays and boolean logic like:
("HELLO" IN ["HELLO", "WORLD"]) && (10 IN [2+2, 3+3, 5+5, "string here"])
where
["HELLO", "WORLD"]
- array of strings[2+2, 3+3, 5+5, "string here"]
- mixed array of integers and stringsIN
- operator, that check is array contain value or not&&
- logic operator "and"
composer require xakepehok/expression-executor
In order to calc/execute expressions above you need to define those functions, operators and values
MIN():
<?php
class MinFunction implements \XAKEPEHOK\ExpressionExecutor\FunctionInterface
{
public function getName(): string
{
return 'MIN';
}
public function execute(array $arguments, array $context)
{
return min($arguments);
}
}
NUMBER_OF_DAY():
<?php
class NumberOfDayFunction implements \XAKEPEHOK\ExpressionExecutor\FunctionInterface
{
public function getName(): string
{
return 'NUMBER_OF_DAY';
}
public function execute(array $arguments, array $context)
{
$year = $arguments['year'] ?? ($arguments[0] ?? null);
$month = $arguments['month'] ?? ($arguments[1] ?? null);
$day = $arguments['day'] ?? ($arguments[2] ?? null);
if ($year === null || $month === null || $day === null) {
throw new \XAKEPEHOK\ExpressionExecutor\Exceptions\FunctionException('Arguments error');
}
return date('N', strtotime("{$year}-{$month}-{$day}"));
}
}
+
operator:
<?php
class PlusOperator implements \XAKEPEHOK\ExpressionExecutor\OperatorInterface
{
public function operator() : string
{
return '+';
}
/**
* Custom integer priority value. For example, for "+" it can be 1, for "*" it can be 2
* @return int
*/
public function priority() : int
{
return 1;
}
public function execute($leftOperand, $rightOperand, array $context)
{
return $leftOperand + $rightOperand;
}
}
*
operator:
<?php
class MultiplyOperator implements \XAKEPEHOK\ExpressionExecutor\OperatorInterface
{
public function operator() : string
{
return '*';
}
/**
* Custom integer priority value. For example, for "+" it can be 1, for "*" it can be 2
* @return int
*/
public function priority() : int
{
return 2;
}
public function execute($leftOperand, $rightOperand, array $context)
{
return $leftOperand * $rightOperand;
}
}
IN
operator:
<?php
class InOperator implements \XAKEPEHOK\ExpressionExecutor\OperatorInterface
{
public function operator() : string
{
return 'IN';
}
/**
* Custom integer priority value. For example, for "+" it can be 1, for "*" it can be 2
* @return int
*/
public function priority() : int
{
return 1;
}
public function execute($leftOperand, $rightOperand, array $context)
{
return in_array($leftOperand, $rightOperand, true);
}
}
&&
operator:
<?php
class AndOperator implements \XAKEPEHOK\ExpressionExecutor\OperatorInterface
{
public function operator() : string
{
return '&&';
}
/**
* Custom integer priority value. For example, for "+" it can be 1, for "*" it can be 2
* @return int
*/
public function priority() : int
{
return 1;
}
public function execute($leftOperand, $rightOperand, array $context)
{
return $leftOperand && $rightOperand;
}
}
Create executor instance:
<?php
$executor = new \XAKEPEHOK\ExpressionExecutor\Executor(
[new MinFunction(), new NumberOfDayFunction()],
[new PlusOperator(), new MultiplyOperator(), new InOperator(), new AndOperator()],
function ($name, array $context) {
$vars = [
'VARIABLE' => 10,
'CONTEXT.VALUE' => $context['value'],
];
return $vars[$name];
},
['PI' => 3.14]
);
//And simply execute our expression
$result_1 = $executor->execute('MIN(5, 10.5) + NUMBER_OF_DAY(year: "2019", month: "01", day: "20") + PI * {{VARIABLE}} + ((-2) + 2.5) * 2');
$result_2 = $executor->execute('("HELLO" IN ["HELLO", "WORLD"]) && (10 IN [2+2, 3+3, 5+5, "string here"])');
- Its safe. No
eval()
- Executor can return and work with any types of data. All types checking and manipulating should be implemented in your classes (functions and operators)
- String arguments support escaped double quotes, for example
"My name is \"Timur\""
- Functions accept any count of arguments (you can limit in function body by exceptions)
- Functions arguments can be named
NUMBER_OF_DAY(year: "2019", month: "01", day: "20")
and unnamed NUMBER_OF_DAY("2019", "01", "20"), but not combined - Function arguments can be strings, numbers, variables, constants, other functions result and any expressions
- You can pass context (any common data as array) as second param for
execute()
method. Context will be passed to functions, operators and variables callable - Use brackets
(2 + 2) * 2
for priority - Use brackets for negative numbers, such as
(-1)
,(-1.2)
- You can implement any operator, such as
>
,>=
,<
,<=
and any what you want and desire
See ExecutorTest.php for more examples.
- https://symfony.com/doc/current/components/expression_language.html - great symfony component for expressions, but it is impossible to override logic of any built-in operators and also impossible use your own strict type system. Only one way to extend - define your own function
- https://github.com/NeonXP/MathExecutor - good math expressions calculator with user-defined operators and functions, but it is also impossible to use your own strict type system. For example, you can't do something like Datetime - Datetime (with type saving)