Avaliando conjunto de dados com uma fórmula de string em php

9

Fui encarregado de atualizar algumas condições em um aplicativo. Eu tenho um conjunto de dados a ser avaliado e foi codificado no aplicativo da seguinte maneira:

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

e assim por diante...

É um pesadelo para manter em muitos lugares.

O que eu estou procurando, essencialmente, é fazer alguma maneira de passar na string de consulta para avaliar os dados. Para iniciar, uma fórmula simples pode ser definida como uma matriz

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

Isso poderia ser estendido e chamado recursivamente

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

mas o que eu estou procurando essencialmente é armazenar fórmulas e uma string, como:

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

onde [] identificaria chaves de matriz

ou talvez algo como no Excel

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

Existe alguma solução limpa para fazer isso? Eu tenho uma idéia de como construir uma biblioteca inteira para ela, talvez no futuro.

Não quero adicionar novas condições ao código todas as vezes. Eles já estão em todo o aplicativo. Seria melhor armazená-lo na configuração e estender ou modificar em um só lugar.

Qualquer ajuda muito apreciada.

Pawel Jankowski
fonte
11
Escrever um avaliador é uma tarefa complexa, mas você pode dar uma olhada em estender a resposta da ircmaxell a esta pergunta para lidar com e / ou strings também; ou olhar para o motor de cálculo em algo como PHPExcel
Mark Baker
11
Eu acho que uma solução "limpa" seria configurar uma classe com funções diferentes e incluí-la em vários arquivos. Para armazenar o código como uma string e avaliá-lo mais tarde, o PHP oferece eval().
11
Obrigado @ MarkBaker, eu posso dar uma olhada neles. Não é exatamente o que estou procurando. Eu realmente não quero usar eval (), isso pode ser muito perigoso, pois essas fórmulas serão usadas pelos usuários. Isso deve ser mais infalível.
Pawel Jankowski
3
Cuidado ao criar um "efeito da plataforma interna". É difícil imaginar pelo que você nos mostrou até agora que você acabaria salvando muitas linhas de código. Você pode não preferir toda a sintaxe do PHP, mas é um padrão que qualquer desenvolvedor de PHP (ou desenvolvedor de C ++ ou Java) pode entender. Portanto, embora isso pareça uma coisa divertida de tentar, talvez seja melhor experimentar primeiro em um projeto paralelo de menor escala. Se funcionar lá, considere incluí-lo no grande projeto.
John Doe
11
Um avaliador de RPN seria uma tarefa mais simples de escrever e manter. É muito poderoso e há menos coisas que você pode errar. É menos amigável como um pensamento de linguagem.
Scara95

Respostas:

1

Portanto, essa é apenas uma solução rápida, mas funciona para mim agora.

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

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

Se houver [a] na fórmula, ela será tratada como chave de matriz.

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

Nesta solução, ele funcionará com fórmula como:

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

Não é exatamente o que eu queria, mas faz o trabalho e não é tão terrível (espero). Ele precisa de alguma verificação de entrada e tratamento de erros, mas este é apenas um exemplo.

Pawel Jankowski
fonte