README
                    
Модуль 4slovo/expression позволяет преобразовывать псевдокод в php-код, это позволяет:
- вынести бизнес логику на конфигурационный уровень
- ограничить языковые конструкции до разрешённых в псевдокоде
- упрощать синтаксис операций (например, для сложения дат можно использовать операцию +)
Пример, формулу расчёта площади круга можно записать как:
$area = 3.14 * ($radius ** 2);
что будет преобразовано в php-код, производящий расчёт площади круга в зависимости от
переданной переменной $radius
Пример:
$expressionText = '$radius = 2; $area = 3.14 * ($radius ** 2);';
$codeContext = new CodeContext();
$codeExecutor = new CodeExecutor();
$variableName = '$result';
$areaResult = $codeExecutor
    ->setCode($expressionText)
    ->setCodeContext($codeContext)
    ->execute()
    ->getVariableByName('$area');
echo $areaResult; # выведет 12.56  
Поддерживаемые операции
Сложение
| Знак | Класс операции | 
| + | addOperation | 
Допустимые операции с типами
| Тип левого операнда | Тип правого операнда | Тип результата | Пример | 
| int | int | int | 1 + 1 == 2 | 
| int | float | float | 1 + 1.1 == 2.1 | 
| float | int | float | 1.1 + 1 == 2.1 | 
| float | float | float | 1.1 + 1.2 == 2.3 | 
| money | money | money | 1$ + 2$20 == 3$20 | 
| dateInterval | dateInterval | dateInterval | 1 day + 2 days == 3 days | 
| dateInterval | dateTime | dateTime | 1 day + 2018.01.02 == 2018.01.03 | 
| dateTime | dateInterval | dateTime | 2018.01.02 + 1 day == 2018.01.03 | 
Вычитание
| Знак | Класс операции | 
| - | SubtractionOperation | 
Допустимые операции с типами
| Тип левого операнда | Тип правого операнда | Тип результата | Пример | 
| int | int | int | 2 - 1 == 1 | 
| int | float | float | 2 - 1.1 == 0.9 | 
| float | int | float | 1.1 - 2 == -0.9 | 
| float | float | float | 2.2 - 1.1 == 1.1 | 
| money | money | money | 2$ - 1$ == 1$ | 
| dateInterval | dateInterval | dateInterval | 2 day - 1 day == 1 day | 
| dateTime | dateTime | dateInterval | 2018.01.02 - 2018.01.01 == 1 day | 
| dateTime | dateInterval | dateTime | 2018.01.02 - 1 day == 2018.01.01 | 
Умножение
| Знак | Класс операции | 
| * | MultiplyOperation | 
Допустимые операции с типами
| Тип левого операнда | Тип правого операнда | Тип результата | Пример | 
| int | int | int | 2 * 2 == 4 | 
| int | float | float | 2 * 1.1 == 2.2 | 
| float | int | float | 1.1 * 2 == 2.2 | 
| float | float | float | 1.1 * 1.1 == 1.21 | 
| money | int | money | 2$ * 2 == 4$ | 
| money | float | money | 2$ * 2.1 == 4$20 | 
Деление
| Знак | Класс операции | 
| / | DivisionOperation | 
Допустимые операции с типами
| Тип левого операнда | Тип правого операнда | Тип результата | Пример | 
| int | int | float | 5 / 2 == 2.5 | 
| int | float | float | 5 * 1.1 == 2.2 | 
| float | int | float | 5 / 2.2 == 2.272727... | 
| float | float | float | 1.21 / 1.1 == 1.1 | 
| money | int | money | 2$ / 2 == 1$ | 
| money | float | money | 2$ / 2.1 == $95 | 
Возведение в степень
| Знак | Класс операции | 
| ** | ExponentiationOperation | 
Допустимые операции с типами
| Тип левого операнда | Тип правого операнда | Тип результата | Пример | 
| int | int | int | 2 ** 3 == 8 | 
| int | float | float | 2 ** 1.1 == 2.1435... | 
| float | int | float | 2.2 ** 2 == 4.84 | 
| float | float | float | 2.2 ** 2.2 == 5.6666... | 
Остаток от деления
| Знак | Класс операции | 
| % | RemainderOfDivisionOperation | 
Допустимые операции с типами
| Тип левого операнда | Тип правого операнда | Тип результата | Пример | 
| int | int | int | 5 % 3 == 2 | 
| int | float | int | 5 % 3.9 == 2 | 
| float | int | int | 5.5 % 3 == 2 | 
| float | float | int | 5.5 % 3.9 == 2 | 
##Операции сравнения
Равно
| Знак | Класс операции | 
| == | EqualOperation | 
Допустимые операции с типами
| Тип левого операнда | Тип правого операнда | Тип результата | Пример | Результат | 
| int | int | boolean | 1 == 1 | true | 
| int | int | boolean | 1 == 2 | false | 
| float | float | boolean | 1.1 == 1.1 | true | 
| float | float | boolean | 1.1 == 1.2 | false | 
| money | money | boolean | 100$ == 100$ | true | 
| money | money | boolean | 100$ == 200$ | false | 
| dateInterval | dateInterval | boolean | 2 day == 2 day | true | 
| dateInterval | dateInterval | boolean | 2 day == 3 day | false | 
| dateTime | dateTime | boolean | 2018.06.19 15:06:00 == 2018.06.19 15:06:00 | true | 
| dateTime | dateTime | boolean | 2018.06.19 15:06:00 == 2018.06.19 15:06:01 | false | 
| string | string | boolean | 'a' == 'a' | true | 
| string | string | boolean | 'a' == 'b' | false | 
Не равно
| Знак | Класс операции | 
| != | EqualOperation | 
Допустимые операции с типами
| Тип левого операнда | Тип правого операнда | Тип результата | Пример | Результат | 
| int | int | boolean | 1 != 1 | false | 
| int | int | boolean | 1 != 2 | true | 
| float | float | boolean | 1.1 != 1.1 | false | 
| float | float | boolean | 1.1 != 1.2 | true | 
| money | money | boolean | 100$ != 100$ | false | 
| money | money | boolean | 100$ != 200$ | true | 
| dateInterval | dateInterval | boolean | 2 day != 2 day | false | 
| dateInterval | dateInterval | boolean | 2 day != 3 day | true | 
| dateTime | dateTime | boolean | 2018.06.19 15:06:00 != 2018.06.19 15:06:00 | false | 
| dateTime | dateTime | boolean | 2018.06.19 15:06:00 != 2018.06.19 15:06:01 | true | 
| string | string | boolean | 'a' != 'a' | false | 
| string | string | boolean | 'a' != 'b' | true | 
Больше
| Знак | Класс операции | 
| > | GreaterOperation | 
Допустимые операции с типами
| Тип левого операнда | Тип правого операнда | Тип результата | Пример | Результат | 
| int | int | boolean | 3 > 2 | true | 
| int | int | boolean | 3 > 3 | false | 
| int | int | boolean | 3 > 4 | false | 
| float | float | boolean | 3.14 > 3.13 | true | 
| float | float | boolean | 3.14 > 3.14 | false | 
| float | float | boolean | 3.14 > 3.15 | false | 
| money | money | boolean | 301$ > 300$ | true | 
| money | money | boolean | 300$ > 300$ | false | 
| money | money | boolean | 300$ > 301$ | false | 
| dateInterval | dateInterval | boolean | 6 day > 5 day | true | 
| dateInterval | dateInterval | boolean | 6 day > 6 day | false | 
| dateInterval | dateInterval | boolean | 6 day > 7 day | false | 
| dateTime | dateTime | boolean | 2018.06.19 15:06:00 > 2018.06.19 15:05:59 | true | 
| dateTime | dateTime | boolean | 2018.06.19 15:06:00 > 2018.06.19 15:06:00 | false | 
| dateTime | dateTime | boolean | 2018.06.19 15:06:00 > 2018.06.19 15:06:01 | false | 
| string | string | boolean | 'a' > 'a' | false | 
| string | string | boolean | 'a' > 'b' | false | 
| string | string | boolean | 'b' > 'a' | true | 
Меньше
| Знак | Класс операции | 
| < | LessOperation | 
Допустимые операции с типами
| Тип левого операнда | Тип правого операнда | Тип результата | Пример | Результат | 
| int | int | boolean | 3 < 4 | true | 
| int | int | boolean | 3 < 3 | false | 
| int | int | boolean | 3 < 2 | false | 
| float | float | boolean | 3.14 < 3.15 | true | 
| float | float | boolean | 3.14 < 3.14 | false | 
| float | float | boolean | 3.14 < 3.13 | false | 
| money | money | boolean | 300$ < 301$ | true | 
| money | money | boolean | 300$ < 300$ | false | 
| money | money | boolean | 301$ < 300$ | false | 
| dateInterval | dateInterval | boolean | 6 day < 7 day | true | 
| dateInterval | dateInterval | boolean | 6 day < 6 day | false | 
| dateInterval | dateInterval | boolean | 6 day < 5 day | false | 
| dateTime | dateTime | boolean | 2018.06.19 15:06:00 < 2018.06.19 15:06:01 | true | 
| dateTime | dateTime | boolean | 2018.06.19 15:06:00 < 2018.06.19 15:06:00 | false | 
| dateTime | dateTime | boolean | 2018.06.19 15:06:00 < 2018.06.19 15:05:59 | false | 
| string | string | boolean | 'a' < 'a' | false | 
| string | string | boolean | 'a' < 'b' | true | 
| string | string | boolean | 'b' < 'a' | false | 
Больше или равно
| Знак | Класс операции | 
| >= | GreaterOrEqualOperation | 
Допустимые операции с типами
| Тип левого операнда | Тип правого операнда | Тип результата | Пример | Результат | 
| int | int | boolean | 3 >= 2 | true | 
| int | int | boolean | 3 >= 3 | true | 
| int | int | boolean | 3 >= 4 | false | 
| float | float | boolean | 3.14 >= 3.13 | true | 
| float | float | boolean | 3.14 >= 3.14 | true | 
| float | float | boolean | 3.14 >= 3.15 | false | 
| money | money | boolean | 301$ >= 300$ | true | 
| money | money | boolean | 300$ >= 300$ | true | 
| money | money | boolean | 300$ >= 301$ | false | 
| dateInterval | dateInterval | boolean | 6 day >= 5 day | true | 
| dateInterval | dateInterval | boolean | 6 day >= 6 day | true | 
| dateInterval | dateInterval | boolean | 6 day >= 7 day | false | 
| dateTime | dateTime | boolean | 2018.06.19 15:06:00 >= 2018.06.19 15:05:59 | true | 
| dateTime | dateTime | boolean | 2018.06.19 15:06:00 >= 2018.06.19 15:06:00 | true | 
| dateTime | dateTime | boolean | 2018.06.19 15:06:00 >= 2018.06.19 15:06:01 | false | 
| string | string | boolean | 'a' >= 'a' | true | 
| string | string | boolean | 'a' >= 'b' | false | 
| string | string | boolean | 'b' >= 'a' | true | 
Меньше или равно
| Знак | Класс операции | 
| <= | LessOrEqualsOperation | 
Допустимые операции с типами
| Тип левого операнда | Тип правого операнда | Тип результата | Пример | Результат | 
| int | int | boolean | 3 <= 4 | true | 
| int | int | boolean | 3 <= 3 | true | 
| int | int | boolean | 3 <= 2 | false | 
| float | float | boolean | 3.14 <= 3.15 | true | 
| float | float | boolean | 3.14 <= 3.14 | true | 
| float | float | boolean | 3.14 <= 3.13 | false | 
| money | money | boolean | 300$ <= 301$ | true | 
| money | money | boolean | 300$ <= 300$ | true | 
| money | money | boolean | 301$ <= 300$ | false | 
| dateInterval | dateInterval | boolean | 6 day <= 7 day | true | 
| dateInterval | dateInterval | boolean | 6 day <= 6 day | true | 
| dateInterval | dateInterval | boolean | 6 day <= 5 day | false | 
| dateTime | dateTime | boolean | 2018.06.19 15:06:00 <= 2018.06.19 15:06:01 | true | 
| dateTime | dateTime | boolean | 2018.06.19 15:06:00 <= 2018.06.19 15:06:00 | true | 
| dateTime | dateTime | boolean | 2018.06.19 15:06:00 <= 2018.06.19 15:05:59 | false | 
| string | string | boolean | 'a' <= 'a' | true | 
| string | string | boolean | 'a' <= 'b' | true | 
| string | string | boolean | 'b' <= 'a' | false | 
Встроенные функции
date - приведение даты и времени к дате
Допустимые операции с типами
| Тип параметра | Тип результата | Пример | 
| dateTime | dateTime | (dateTime) date(2018.01.02 22:32:18) == 2018.01.02 | 
dateFormat - преобразование даты и времени к строке
Допустимые операции с типами
| Тип параметра | Тип результата | Пример | 
| dateTime | string | (string) dateFormat(2018.01.02, 'Y.m.d') == '2018.01.02' | 
daysInYear - определение числа дней в году для указанной даты
Допустимые операции с типами
| Тип параметра | Тип результата | Пример | 
| dateTime | int | (int) daysInYear(2018.01.02) == 365 (int) daysInYear(2016.01.02) == 366
 | 
days - определение числа дней во временном интервале
Допустимые операции с типами
| Тип параметра | Тип результата | Пример | 
| dateInterval | int | (int) days(2 days) == 2 (int) days(2016.01.03 - 2016.01.01) == 2
 | 
| int | dateInterval | (dateInterval) days(1) == 1 day | 
firstYearDay - преобразование даты в дату первого числа года
Допустимые операции с типами
| Тип параметра | Тип результата | Пример | 
| dateTime | dateTime | (dateTime) firstYearDay(2018.06.12 08:56:10) == 2018.01.01 00:00:00 | 
int - преобразование числа с плавающей точкой в целое число
Допустимые операции с типами
| Тип параметра | Тип результата | Пример | 
| float | int | int(1.1) == 1 | 
| float | int | int(3.9) == 3 | 
| money | int | int(1$01) == 101 | 
money - преобразование числа в деньги
Допустимые операции с типами
| Тип параметра | Тип результата | Пример | 
| float | money | money(100.1) == 1$ | 
| int | money | money(100) == 1$ | 
min - определение минимального значения
Допустимые операции с типами
| Тип параметра 1 | Тип параметра N | Тип результата | Пример | 
| int | int | int | (int) min(3,1,2) == 1 | 
| float | float | float | (float) min(3.1, 1.2, 2.3) == 1.2 | 
| money | money | money | (money) min(3$, 1$, 2$) == $1 | 
| dateTime | dateTime | dateTime | (dateTime) min(2018.01.01, 2019.01.01, 2015.01.01) == 2015.01.01 | 
| dateInterval | dateInterval | dateInterval | (dateInterval) min(3 days, 1 day, 2 days) == 1 day | 
max - определение минимального значения
Допустимые операции с типами
| Тип параметра 1 | Тип параметра N | Тип результата | Пример | 
| int | int | int | (int) max(3,1,2) == 3 | 
| float | float | float | (float) max(3.1, 1.2, 2.3) == 3.1 | 
| money | money | money | (money) max(3$, 1$, 2$) == $3 | 
| dateTime | dateTime | dateTime | (dateTime) max(2018.01.01, 2019.01.01, 2015.01.01) == 2019.01.01 | 
| dateInterval | dateInterval | dateInterval | (dateInterval) max(3 days, 1 day, 2 days) == 3 days | 
isNull - определение является ли значение нулевым
Допустимые операции с типами
| Тип параметра | Тип результата | Пример | 
| null | boolean | (boolean) isNull(null) == true | 
| int | boolean | (boolean) isNull(3) == false | 
| float | boolean | (boolean) isNull(3.1) == false | 
| money | boolean | (boolean) isNull(3$) == false | 
| dateTime | boolean | (boolean) isNull(2018.01.01) == false | 
| dateInterval | boolean | (boolean) isNull(3 days) == false | 
isNotNull - определение является ли значение не нулевым
Допустимые операции с типами
| Тип параметра | Тип результата | Пример | 
| null | boolean | (boolean) isNull(null) == false | 
| int | boolean | (boolean) isNull(3) == true | 
| float | boolean | (boolean) isNull(3.1) == true | 
| money | boolean | (boolean) isNull(3$) == true | 
| dateTime | boolean | (boolean) isNull(2018.01.01) == true | 
| dateInterval | boolean | (boolean) isNull(3 days) == true | 
floor - округление до ближайшего меньшего целого (для денег до мажорных единиц)
Допустимые операции с типами
| Тип параметра | Тип результата | Пример | 
| float | float | (int) floor(100.23) == 100 | 
| money | money | (money) floor(1$23) == 1$ | 
ceil - округление до ближайшего большего целого (для денег до мажорных единиц)
Допустимые операции с типами
| Тип параметра | Тип результата | Пример | 
| float | float | (int) ceil(100.23) == 101 | 
| money | money | (money) ceil(1$23) == 2$ | 
round - округление по математическим правилам (для денег до мажорных единиц)
Допустимые операции с типами
| Тип параметра | Тип результата | Пример | 
| float | float | (int) round(100.5) == 101 | 
| money | money | (money) round(1$50) == 2$ | 
printR - вывод значения в формате print_r
| Тип параметра | Тип результата | Пример | 
| произвольный тип | null | printR(1); // выведет 1 | 
varDump - вывод значения в формате var_dump
| Тип параметра | Тип результата | Пример | 
| произвольный тип | null | varDump(1); // выведет int(1) | 
Встроенные функции для работы с массивами
array - создание массива
| Тип параметра 1 | Тип параметра N | Тип результата | Пример | 
| произвольный тип | произвольный тип | array | array(1$, 2.0, 3); | 
getArrayValue - получение значения из массива по ключу
| Тип параметра 1 | Тип параметра 2 | Тип результата | Пример | 
| array | int или string (ключ) | произвольный тип (значение) | $list = array(1, 2, 3); $result = (int)getArrayValue($list, 2);
 // переменная $result будет содержать значение 3его элемента массива
 | 
setArrayValue - установка значения в массив по ключу
| Тип параметра 1 | Тип параметра 2 | Тип параметра 3 | Тип результата | Пример | 
| array | int или string (ключ) | произвольный тип (значение) | null | $list = array(1, 2, 3); setArrayValue($list, 2, 5);
 // 3ий элемент массива будет содержать значение 5
 | 
arrayKeyExists - проверка существования ключа в массиве
| Тип параметра 1 | Тип параметра 2 | Тип результата | Пример | 
| int или string (ключ) | array | boolean | $list = array(1, 2, 3); $resultTrue = arrayKeyExists(0, $list);
 $resultFalse = arrayKeyExists(3, $list);
 | 
reset - сброс указателя массива на первый элемент
| Тип параметра 1 | Тип результата | Пример | 
| array | произвольный тип (значение) | $list = array(1, 2, 3); $firstElement = (int) reset($list);
 // $firstElement = 1
 | 
end - сброс указателя массива на последний элемент
| Тип параметра 1 | Тип результата | Пример | 
| array | произвольный тип (значение) | $list = array(1, 2, 3); $lastElement = (int) end($list);
 // $lastElement = 3
 | 
next - передвинуть указатель массива вперёд
| Тип параметра 1 | Тип результата | Пример | 
| array | произвольный тип (значение) | $list = array(1, 2, 3); $secondElement = (int) next($list);
 // $secondElement = 2
 | 
prev - передвинуть указатель массива назад
| Тип параметра 1 | Тип результата | Пример | 
| array | произвольный тип (значение) | $list = array(1, 2, 3, 4); end($list);
 $thirdElement = (int) prev($list);
 // $thirdElement = 3
 | 
key - получение ключа элемента на котором находится указатель массива
| Тип параметра 1 | Тип результата | Пример | 
| array | int или string (или null если указатель находится вне массива) | $list = array(1, 2, 3, 4); $firstElementKey = key($list);
 // $firstElementKey = 0
 | 
count - определение размера массива
| Тип параметра 1 | Тип результата | Пример | 
| array | int | $list = array(1, 2, 3); $listSize = (int) count($list);
 // $listSize = 3
 | 
arrayKeys - получение ключей массива
| Тип параметра 1 | Тип результата | Пример | 
| array | array | $list = (array) arrayKeys(array(1, 3, 2)); // $list = array(0, 1, 2) | 
sort - сортировка элементы массива в порядке возрастания
| Тип параметра 1 | Тип результата | Пример | 
| array | array | $list = (array) sort(array(1, 3, 2)); // $list = array(1, 2, 3) | 
rsort - сортировка элементы массива в порядке убывания
| Тип параметра 1 | Тип результата | Пример | 
| array | array | $list = (array) rsort(array(1, 3, 2)); // $list = array(3, 2, 1) | 
asort - сортировка ключей массива в порядке возрастания
| Тип параметра 1 | Тип результата | Пример | 
| array | array | $list = (array) arrayKeys(asort(array(1, 3, 2))); // $list = array(0, 2, 1) | 
arsort - сортировка ключей массива в порядке убывания
| Тип параметра 1 | Тип результата | Пример | 
| array | array | $list = (array) arrayKeys(arsort(array(1, 3, 2))); // $list = array(0, 2, 1) | 
Присваивание
| Пример | Результат | 
| $a = 1; | создаётся переменная $a со значением 1 | 
| $i = 1; $i = $i + 1; | создаётся переменная $i со значением 1, а затем инициализируется значением 2 | 
Тернарный условный оператор
| Пример | Результат | 
| 1 > 2 ? 1 : 2 | 2 | 
| 1 < 2 ? 1 : 2 | 1 | 
| 1 < 2 && 2 < 3 ? 1 : 2 | 1 | 
| 1 < 2 && 2 > 3 ? 1 : 2 | 2 | 
| 1 < 2 ? 1 + 1 : 2 + 2 | 2 | 
| 1 > 2 ? 1 + 1 : 2 + 2 | 4 | 
Условный оператор if
синтаксис:
if(conditionExpression){ doStatementList }
, где
conditionExpression - логическое выражение, пока оно возвращает true - цикл не завершается
doStatementList - список инструкций который необходимо многократно выполнить
| Пример | Результат | 
| if(1 > 2) { $a = 1; } | переменная $a будет содержать значение 1 | 
Цикл for
синтаксис:
for(firstStatement; conditionExpression, eachStepStatement){ doStatementList }
, где
firstStatement - инструкция выполняющаяся первой
conditionExpression - логическое выражение, пока оно возвращает true - цикл не завершается
eachStepStatement - инструкция выполняющаяся каждый шаг
doStatementList - список инструкций который необходимо многократно выполнить
| Пример | Результат | 
| for($i = 1; $i < 10; $i = $i + 1) { $a = $i; } | переменная $a будет содержать значение 9 | 
Приоритет операций
Чем больше приоритет, тем раньше будет выполнена операция
| Обозначение операции | Приоритет | 
| ! | 17 | 
| *,/,% | 16 | 
| +,- | 15 | 
| >
 >=
 <
 <= | 13 | 
| == | 12 | 
| && | 8 | 
| || | 7 | 
| = | 4 | 
В выражениях можно использовать скобки для изменения стандартных приоритетов
Переменные
Переменные в выражениях должны начинаться с символа $, например, $creditAmount, $creditPeriod ...
Типы
Поддерживается работа с типами: boolean, int, float, money, dateTime, dateInterval, null, string
Пользовательские функции
В выражения можно внедрять пользовательские функции для расширения базового функционала
Пример пользовательской функции: (money) annuityPayment($yearPercent, $creditAmount, $creditMonths).
Название функции должно начинаться с указания возвращаемого типа и иметь набор параметров, заключённых в круглые скобки,
если параметров нет, то скобки () всё-равно должны присутствовать.
В качестве параметров функции могут использоваться переменные, выражения и типы.
Пример использования пользовательской функции можно найти в тесте:
TestExpression::testExpressionFunction
Комментарии
синтаксис:
/* многострочный комментарий */
| Пример | Результат | 
| /* Пример */ 1 + 1 /* комментария */ | 2 |