[Expression] ! pour les connaisseurs!

Hello,

Comme certains semblent joueurs, je vous propose un petit jeux sur les soucis liés aux expressions.

Moteur d’expression de Jeedom : The ExpressionLanguage Component (Symfony Docs)
Github : https://github.com/symfony/symfony/tree/5.x/src/Symfony/Component/ExpressionLanguage
Fichiers Core : \vendor\symfony\expression-language

La fonction du Core utilisée est : core/php/utils.inc.php function evaluate()

Cette fonction intervient lors d’un scénario execute() et d’un test dans le testeur d’expression par exemple. Avant cela, l’expression passe par scenarioExpression::setTags() qui s’occupe des tag() variable() etc et par jeedom::fromHumanReadable() pour transformer les #macommand# en id puis en valeur.

Puis enfin le evaluate() de symfony. C’est çà qu’on va tester ici avec un simple scénario et un bloc code, en injectant directement une expression dans Symfony pour voir ce que çà donne.

Scenario de test, bloc code:

//load symfony expression engine:
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;

//Core function calling symfony, no core parsing (tag, variable etc won't work):
//Source Core code in core/php/utils.inc.php function evaluate()
function testExpression($expr) {
  global $scenario;
  $expresser = new ExpressionLanguage();
  try {
    $result = $expresser->evaluate($expr);
    return $result;
  } catch (Exception $e) {
    $scenario->setLog('First try ERROR: '.$e->getMessage());
  }
  try {
    $result = $expresser->evaluate(str_replace('""', '"', $expr));
    return $result;
  } catch (Exception $e) {
    $scenario->setLog('Second try ERROR: '.$e->getMessage());
  }
  return $exp;
}

//test:
$scenario->setLog(testExpression('1E-2'));
$scenario->setLog(testExpression('floatval(2.0E-6)+1'));

D’ailleurs vous allez voir que le floatval ici ne fonctionne pas. C’est parce que le core fait un pre-parsing pour que des choses comme çà fonctionne quand même.

Vous pouvez aussi mettre d’autres librairies et les tester comme çà. Également dans utils.inc.php ajouter des log de debug pour comprendre ce qu’il se passe.

Perso, je ne maitrise pas du tout ce sujet. Un exemple que je ne comprend pas, ici ils mettent que les exponent sont supportés, alors pourquoi çà passe pas, j’en sais rien …

Mais si çà peux faire avancer le schimblick(Jeedom), tout le monde en sortira gagnant. Je suis certains que pas mal de brutes en php trainent par ici, si çà peux les intéresser pour remonter dans les fonctions du core et améliorer le moteur :wink:

D’ailleurs avant de modifier des trucs, ce serait bien, peu-être, de relever tout les cas qui posent problème. Ce qui pourrait ensuite servir de test de validation à d’éventuelles améliorations.

PS: je supprimerai unilatéralement les post de personnes non constructives qui répondent juste pour râler et critiquer. Je souhaite tirer le débat vers le haut, et ceci ne m’intéresse pas, comme nombre d’entre vous :wink:

Soucis identifiés

exp : donne / devrait donner

  • variable(sunset) * 1E+2 → OK SF5.1
  • 2E-6 + 1 → OK SF5.1
  • 1e-2 → OK SF5.1
  • #mois#(-1.15#mois#+15)+5 : 19.40013 / 19.4 ( 12*(-1.15*12+15)+5 in symfony 2.6 OK)
  • strlen(‹ to,to ›) : ‹  › / 5
  • substr(‹ toto et titi ›, 0, 8) : toto && / toto et
  • pow(2, (3+2)) : pow(2, (3+2)) / 32
7 « J'aime »

Pour les tests, je crois que @Domatizer en a fait pas mal il me semble :wink:

Il y a aussi un round() bizarre dans https://github.com/jeedom/core/blob/alpha/core/class/scenarioExpression.class.php

public static function round($_value, $_decimal = 0) {

        $_value = self::setTags($_value);

        try {

            $result = evaluate($_value);

            if (is_string($result)) {

                $result = $_value;

            }

        } catch (Exception $e) {

            $result = $_value;

        }

        return round(floatval(str_replace(',', '.', $result)), $_decimal);

    }

pourquoi bizarre ???

Bizarre car :

  • Elle a la même primitive qu’une fonction php de base : nom et nb arg.
  • Elle a tendance à remplacer les , par .
  • J’ai pas trouvé où elle est appelée explicitement avec son nom complet scenarioExpression::round() ou ->round() dans le code…

Et donc je me demande si de temps en temps, il y a pas inversion entre les 2 (impact avec les arguments de fonctions imbriquées ?)

1 « J'aime »

çà c’est normal c’est pour intepreter les 6,2 en 6.2

Pour le round() de php en effet. C’est bien une fonction de class donc pas de soucis avec celle de php mais on dirait qu’elle n’est jamais appellée en tant que telle (scenarioExpression::round ou scenarioExpression->round ou $this->round)

Oui mais dans l’hypothèse ou il y a conflit ça peut aussi transformer un truc(arg1,arg2)
en un string qui n’est pas évaluable truc(arg1.arg2)
Mais bon comme je vois pas où on l’appelle non plus…

Ok, la fonction round de la class scenarioExpression est bien utilisée à la place de celle de php. Elle permet de corriger un bug justement, par contre on ne le voit pas car elle appelée dynamiquement par la fonction setTags(). if (method_exists(__CLASS__, $function))

C’est clair que c’est un peu complexe cette partie …

Le bug en question est surement rapport avec le fait que tous les champs de la config jeedom sont pas homogènes : certains sont de type texte qui prends . comme séparateur de la décimale. Et d’autres champs numériques qui du fait de la localisation prennent , (sujet déjà évoqué je sais plus où)

Et donc quand on appelle un round() au sens PHP, c’est celui de jeedom qui s’applique …

Par forcément çà permet de corriger un non spécialiste php qui écrirait un IF round(6,2) ou un retour d’API par exemple. Cela dit c’est accessoire par rapport au sujet je pense.

un nom ou un non ?

Oui, à condition quand même qu’il n’y ai pas d’autre surcharge de ce type ailleurs…
J’en ai pas vu cela dit

En fait le workaround floatval() ou intval() marche justement parcqu’il passe par l’évaluation du settag().

Mais à l’evaluate de symfony pourquoi il plante sur 2E-6 + 1 çà je comprend pas. Car du coup un intval() récupere l’évaluation avant le evaluate symfony donc çà marche, mais symfony devrait être capable de le gérer.

Il nous faudrait un spécialiste symfony là !

Typiquement sur ce sujet : Comment effectuer un calcul numérique dans un scénario?
Le problème ne vient pas de Jeedom mais de Symfony. Ou de son implémentation je sais pas.

Oui j’ai suivi le sujet…
Là je dirais comme ça que le 2E-6+1 il voit pas ça comme un nombre avec une puissance mais comme une chaine (forcément php ça type rien si on le force pas)
Donc faire une addition sur une chaine …

Oui mais justement symfony est sensé le gerer non ? The Expression Syntax (Symfony Docs)

Si l’argument est passé avec le type float ok… => exponential mais si on balance un string, ça risque d’être plus tendu (:sweat_smile: je devais la faire)

Arf, en python3 t’est plus embêté, y’a plus de string, c’est que des bytes …

En fait j’ai l’impression que le core gère déjà beaucoup plus de truc que symfony …

Faudrait relever des expressions comme le exponent qui ne passent pas et voir si c’est coté core ou symfony.

J’essayerai aussi avec la dernière version du github mais j’ai des doutes. Et apparemment c’est require php 7.2.5, ma smart de prod est en php7.0 donc on peux pas non plus mettre les dernières versions direct … Mais c’est dans le composer.json c’est pour docker çà non ??

Un bête env dans une VM avec php+symfony (la version de jeedom tant qu’à faire) ça permettrai de voir si ça traite bien l’exposant.
Si ça plante, pas c’est la m*** parce que symfony c’est pas léger à corriger
Si ça marche, c’est la m** aussi

Perso j’arrive pas croire vue la popularité de symfony , que cette partie soit buggée

Je ne connais pas du tout, et symfony ce n’est pas que l’expressionlanguage. Mais je vois dans le code qu’on peu setter des fonctions et passer pas mal de trucs avec le evaluate(), peu être un soucis sur l’implémentation. Faudrait qqlun qui connaisse bien et qui jette un œil, pour un habitué il doit pouvoir voir rapidement si un truc ne va pas. D’où le scenario et le bloc code qui balance direct dans symfony sans passer par jeedom, je comprend pas que çà ne marche pas !

ARG !!!

Une 4.1 stock : $scenario->setLog(testExpression('1E-2'));

[2020-12-06 17:01:57][SCENARIO] First try ERROR: Unexpected token « name » of value « E » around position 2 for expression 1E-2.
[2020-12-06 17:01:57][SCENARIO] Second try ERROR: Unexpected token « name » of value « E » around position 2 for expression 1E-2.

Mon alpha avec dernières lib à jour : $scenario->setLog(testExpression('1E-2'));

[2020-12-06 16:59:58][SCENARIO] 0.01

WTF ??

Par contre sur les deux, çà, çà passe toujours pas. Mais floatval() attend une string en effet …

$scenario->setLog(testExpression('floatval(2.0E-6)+1'));
$scenario->setLog(testExpression('round(2.222)'));

Bonsoir,

Je suis en 4.0.61 et le code suivant :

var_dump(testExpression(1E-2));
var_dump(testExpression(floatval(2.0E-6)+1));

me retourne le résultat suivant :

float(0.01)
float(1.000002)

Je ne suis pas un pro de symfony mais j’ai l’impression qu’il pose problème sur les expressions entre simple quote…

1 « J'aime »