Ocena zestawu danych za pomocą formuły łańcuchowej w php

9

Miałem za zadanie zaktualizować niektóre warunki w aplikacji. Mam zestaw danych do oceny i został on zakodowany na stałe w aplikacji w następujący sposób:

$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;

i tak dalej...

To koszmar w wielu miejscach.

Zasadniczo szukam sposobu, aby przekazać ciąg zapytania w celu oceny danych. Na początek prostą formułę można zdefiniować jako tablicę

$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]);
    }
}

Można to następnie rozszerzyć i wywołać rekurencyjnie

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

ale zasadniczo szukam formuł ciągów, takich jak:

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

gdzie [] identyfikuje klucze tablicy

a może coś w stylu Excela

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

Czy jest na to jakieś czyste rozwiązanie? Mam pomysł, jak zbudować dla niego całą bibliotekę, może w przyszłości.

Nie chcę dodawać nowych warunków do kodu za każdym razem. Są już w całej aplikacji. Lepiej byłoby przechowywać go w konfiguracji i rozszerzać lub modyfikować w jednym miejscu.

Każda pomoc bardzo doceniana.

Paweł Jankowski
źródło
1
Pisanie ewaluatora jest złożonym zadaniem, ale możesz rzucić okiem na rozszerzenie odpowiedzi ircmaxell na to pytanie, aby obsługiwać i / lub i łańcuchy; lub spójrz na silnik obliczeniowy w czymś takim jak PHPExcel
Mark Baker
1
Wydaje mi się, że „czystym” rozwiązaniem byłoby skonfigurowanie klasy z różnymi funkcjami i włączenie jej do kilku plików. Aby przechowywać kod jako ciąg i oceniać go później, PHP oferuje eval().
1
Dziękuję @ MarkBaker, mógłbym na nie rzucić okiem. Nie do końca tego, czego szukam. Naprawdę nie chcę używać eval (), może to być zbyt niebezpieczne, ponieważ te formuły będą używane przez użytkowników. To powinno być bardziej niezawodne.
Paweł Jankowski
3
Uważaj na tworzenie „efektu wewnętrznej platformy”. Z tego, co pokazałeś do tej pory, trudno sobie wyobrazić, że w końcu zapisałeś wiele wierszy kodu. Możesz nie preferować całej składni PHP, ale jest to standard, który każdy programista PHP (lub programista C ++ lub Java) może zrozumieć. Chociaż wydaje się to zabawną rzeczą do wypróbowania, lepiej może poeksperymentować najpierw z mniejszym projektem bocznym. Jeśli to działa, rozważ włączenie go do dużego projektu.
John Doe
1
Ocena RPN byłaby prostszym zadaniem do napisania i utrzymania. Jest dość potężny i jest mniej rzeczy, które możesz pomylić. Jest mniej przyjazny dla użytkownika jako myśl językowa.
Scara95

Odpowiedzi:

1

Jest to więc tylko szybkie rozwiązanie, ale teraz działa dla mnie.

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

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

Jeśli formuła zawiera [a], będzie traktowana jak klucz tablicy.

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);
    }
}

W tym rozwiązaniu będzie działać z formułą taką jak:

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

Nie jest to dokładnie to, czego chciałem, ale spełnia swoją rolę i nie jest tak straszne (mam nadzieję). Wymaga sprawdzenia danych wejściowych i obsługi błędów, ale to tylko przykład.

Paweł Jankowski
źródło