Оценка набора данных с помощью строковой формулы в php

9

Мне было поручено обновить некоторые условия в приложении. У меня есть набор данных для оценки, и он был жестко запрограммирован в приложении следующим образом:

$arr = array(
'a' => 'apple',
'b' => 'orange',
'c' => 1,
'd' => 2,
'e' => 5,
'f' => 'green',
'g' => 'red',
'h' => 'yellow',
)

$res1 = ($arr['a'] == 'apple') ? TRUE : FALSE;
$res2 = (($arr['b'] == $arr['f']) && ($arr['c'] < $arr['d']) ? TRUE : FALSE;
$res3 = (($arr['e'] == '5') && $res2) ?TRUE : FALSE;

и так далее...

Это кошмар, чтобы поддерживать во многих местах.

По сути, я ищу способ передать строку запроса для оценки данных. Для начала простую формулу можно определить как массив

$formula = ['a', '=', 'apple'];

function query($formula, $arr) {
    switch ($formula[1]) {
        case '=':
            return ($arr[$formula[0]] == $formula[2]);
        case '!=':
            return ($arr[$formula[0]]!= $formula[2]);
        case '>':
            return ($arr[$formula[0]] > $formula[2]);
        case '<':
            return ($arr[$formula[0]] == $formula[2]);
    }
}

Затем это можно расширить и вызвать рекурсивно

$formula = [['a','=','apple'], 'AND', ['e','<','10']]

но то, что я по сути ищу, это хранить формулы в виде строки, например:

"((([a]="orange") OR ([c]<"4")) AND ([g]="red"))"

где [] идентифицирует ключи массива

или может быть что-то вроде в Excel

"AND(OR(IF('a'='orange'),IF('c'<4)),IF('g'='red'))"

Есть ли чистое решение для этого? У меня есть идея, как создать целую библиотеку для этого, возможно, в будущем.

Я не хочу добавлять новые условия в код каждый раз. Они уже во всем приложении. Было бы лучше хранить его в конфигурации и расширять или модифицировать в одном месте.

Любая помощь высоко ценится.

Павел Янковский
источник
1
Написание оценщика - сложная задача, но вы можете взглянуть на расширение ответа ircmaxell на этот вопрос для обработки и / или строк; или посмотрите на механизм расчета в чем-то вроде PHPExcel
Марк Бейкер
1
Я думаю, что «чистым» решением было бы создать класс с различными функциями и включить его в несколько файлов. Чтобы сохранить код в виде строки и оценить его позже, PHP предлагает eval().
1
Спасибо @MarkBaker, я мог бы взглянуть на них. Не совсем то, что я ищу. Я действительно не хочу использовать eval (), это может быть слишком опасно, так как эти формулы будут использоваться пользователями. Это должно быть более надежным.
Павел Янковский
3
Не упустите создание "эффекта внутренней платформы". Из того, что вы нам показали, трудно представить, что в итоге вы сэкономите много строк кода. Вы можете не предпочесть весь синтаксис PHP, но это стандарт, который может понять любой разработчик PHP (или разработчик C ++ или Java). Поэтому, хотя это и кажется забавной попыткой, было бы лучше сначала поэкспериментировать с ним на небольшом стороннем проекте. Если это работает там, тогда подумайте о том, чтобы свернуть это в большой проект
Джон Доу
1
Оценщик RPN будет проще написать и поддерживать. Это довольно мощный и есть меньше вещей, которые вы можете ошибиться. Это менее удобно для пользователя, как языковая мысль.
Scara95

Ответы:

1

Так что это только быстрое решение, но у меня работает прямо сейчас.

$arr = array('a' => 'red','b' => 'blue');

$formula = ['[a]', '=', 'red'];

Если в формуле есть [a], он будет рассматриваться как ключ массива.

function query($formula, $arr) {

    $query_operator=$formula[1];

    if (is_array($formula[0])) {
        //recursive call
        $query_left = query($formula[0], $arr);
    } else {
        //extracting string between brackets
        preg_match("/\[([^\]]*)\]/", $formula[0], $match);
        $query_left = $match ? $arr[($match[1])] : $formula[0];
    }

    if (is_array($formula[2])) {
        //recursive call
        $query_right = query($formula[2], $arr);
    } else {
        //extracting string between brackets
        preg_match("/\[([^\]]*)\]/", $formula[2], $match);
        $query_right = $match ? $arr[($match[1])] : $formula[2];
    }


    switch ($query_operator) {
        case '=':
            return ($query_left == $query_right);
        case '!=':
            return ($query_left != $query_right);
        case '>':
            return ($query_left > $query_right);
        case '<':
            return ($query_left == $query_right);
        case 'AND':
            return ($query_left && $query_right);
        case 'OR':
            return ($query_left || $query_right);
    }
}

В этом решении это будет работать с формулой как:

$formula = [['[a]', '=', 'red'], 'AND', ['[b]', '=', 'blue']];

Это не совсем то, что я хотел, но делает работу, и не так страшно (я надеюсь). Требуется некоторая проверка ввода и обработка ошибок, но это всего лишь пример.

Павел Янковский
источник