Évaluation de l'ensemble de données avec une formule de chaîne en php

9

J'ai été chargé de mettre à jour certaines conditions dans une application. J'ai un ensemble de données à évaluer, et il a été codé en dur dans l'application de la manière suivante:

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

etc...

C'est un cauchemar à entretenir dans de nombreux endroits.

Ce que je recherche essentiellement, c'est de trouver un moyen de passer la chaîne de requête pour évaluer les données. Pour commencer, une formule simple pourrait être définie comme un tableau

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

Cela pourrait alors être étendu et appelé récursivement

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

mais ce que je recherche essentiellement, c'est de stocker des formules sous forme de chaîne, comme:

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

où [] identifierait les clés du tableau

ou peut-être quelque chose comme dans Excel

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

Existe-t-il une solution propre pour ce faire? J'ai une idée de comment construire une bibliothèque entière pour cela, peut-être à l'avenir.

Je ne veux pas ajouter de nouvelles conditions au code à chaque fois. Ils sont déjà partout dans l'application. Il serait préférable de le stocker dans la configuration et de l'étendre ou de le modifier en un seul endroit.

Toute aide très appréciée.

Pawel Jankowski
la source
1
Écrire un évaluateur est une tâche complexe, mais vous pourriez envisager d'étendre la réponse d'ircmaxell à cette question pour gérer également les chaînes et / ou et; ou regardez le moteur de calcul dans quelque chose comme PHPExcel
Mark Baker
1
Je suppose qu'une solution "propre" serait de mettre en place une classe avec différentes fonctions et de l'inclure dans plusieurs fichiers. Pour stocker le code sous forme de chaîne et l'évaluer plus tard, PHP propose eval().
1
Merci @MarkBaker, je pourrais y jeter un œil. Pas exactement ce que je cherche. Je ne veux pas vraiment utiliser eval (), cela pourrait être trop dangereux, car ces formules seront utilisées par les utilisateurs. Cela devrait être plus infaillible.
Pawel Jankowski
3
Attention à la création d'un "effet de plateforme interne". Il est difficile d'imaginer d'après ce que vous nous avez montré jusqu'à présent que vous finiriez par enregistrer de nombreuses lignes de code. Vous ne pouvez pas préférer toute la syntaxe de PHP, mais c'est une norme que tout développeur PHP (ou développeur C ++ ou Java) peut comprendre. Donc, bien que cela semble être une chose amusante à essayer, il serait peut-être préférable de l'expérimenter d'abord sur un projet parallèle à plus petite échelle. Si cela fonctionne là-bas, envisagez de l'intégrer dans le grand projet.
John Doe
1
Un évaluateur RPN serait une tâche plus simple à écrire et à maintenir. Il est assez puissant et il y a moins de choses que vous pouvez vous tromper. Il est moins convivial en tant que pensée linguistique.
Scara95

Réponses:

1

Ce n'est donc qu'une solution rapide, mais fonctionne pour moi en ce moment.

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

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

S'il y a [a] dans la formule, il sera traité comme une clé de tableau.

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

Dans cette solution, il fonctionnera avec une formule comme:

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

Ce n'est pas exactement ce que je voulais, mais fait le travail, et ce n'est pas si terrible (j'espère). Il a besoin d'une vérification d'entrée et d'une gestion des erreurs, mais ce n'est qu'un exemple.

Pawel Jankowski
la source