Erreur 500 au redémarrage du démon, et horaires de filtration figés (PHP 8)

Bonjour @scadinot,

D’abord merci pour ce plugin, je m’en sers tous les jours depuis des années et il fait
exactement ce que je lui demande.

Je suis tombé sur deux soucis liés à PHP 8. Ils ne touchent sans doute pas grand monde, mais
ils sont faciles à corriger, alors je me dis que ça peut t’intéresser. J’ai testé les
corrections chez moi, tout fonctionne depuis.

Mon installation : Jeedom 4.6.1, PHP 8.2, plugin Piscine en stable (version du 17/09/2024),
un équipement, et aucune ligne dans les réglages « Asservissement », « Arrêt total » et
« Marche forcée ».


1. Erreur 500 dès qu’on enregistre l’équipement

Ce que je vois : un clic sur « Redémarrer le démon » (ou tout simplement un enregistrement
de l’équipement) renvoie une erreur interne 500. Dans log/http.error :

PHP Fatal error: Uncaught TypeError: count(): Argument #1 ($value) must be of type
Countable|array, string given in .../plugins/pool/core/class/pool.class.php:3481
Stack trace:
#0 .../core/class/DB.class.php(339): pool->postSave()
#1 .../core/class/eqLogic.class.php(1061): DB::save()
#2 .../plugins/pool/core/class/pool.class.php(315): eqLogic->save()
#3 .../plugins/pool/core/ajax/pool.ajax.php(62): pool::stopDaemon()

Pourquoi : tant qu’aucune ligne n’a jamais été saisie dans ces trois tableaux, Jeedom stocke
la chaîne vide et non un tableau. Sous PHP 7 count('') passait avec un simple
avertissement ; sous PHP 8 c’est une erreur fatale. Comme elle tombe dans postSave(), c’est
tout enregistrement de l’équipement qui échoue — donc aussi stopDaemon(), donc le bouton
de redémarrage.

Ça n’arrive qu’à ceux qui n’ont configuré aucun asservissement : dès qu’on saisit une ligne, la
valeur devient un tableau et le problème disparaît. C’est sans doute pour ça qu’il est passé
inaperçu.

Contournement sans toucher au code, si quelqu’un tombe dessus en attendant : remettre les
trois réglages à un tableau vide.

$eq = eqLogic::byId(<id de l'équipement>);
foreach (array('asservissement', 'arretTotal', 'marcheForcee') as $cle) {
    if (!is_array($eq->getConfiguration($cle))) {
        $eq->setConfiguration($cle, array());
    }
}
$eq->save();

2. Les horaires de filtration ne se recalculent plus si les valeurs sont vides

Ce que je vois : « Temps filtration » et « Horaires » restent désespérément vides, et ne
reviennent jamais, même après un redémarrage du démon.

Comment j’y suis arrivé : j’avais vidé le cache de Jeedom — ça efface la valeur courante de
toutes les commandes, dont filtrationDebut et filtrationFin. Une restauration de sauvegarde
doit produire le même effet.

Pourquoi ça ne repart pas : calculateStatusFiltration() décide de (re)lancer le calcul avec

if ($filtrationDebut == 0 || $filtrationFin == 0) {

En PHP 8, "" == 0 vaut faux (la comparaison chaîne/nombre a changé). Une valeur vide n’est
donc plus vue comme « jamais calculé » : le plugin part dans la branche else, où toutes les
comparaisons de dates échouent en silence, et les horaires ne reviennent jamais. Même chose dans
calculateStatusFiltrationHivernage().


Le correctif

Cinq lignes au total, dans core/class/pool.class.php. Chez moi, le redémarrage du démon passe
sans erreur et les horaires se sont recalculés tout seuls au cron suivant
(« Temps filtration 05:51 », « Horaires 09:33-11:30 13:00-16:54 »).

@@ -940,7 +940,8 @@  (calculateStatusFiltration)
-        if ($filtrationDebut == 0 || $filtrationFin == 0) {
+        if (!is_numeric($filtrationDebut) || !is_numeric($filtrationFin)
+            || $filtrationDebut == 0 || $filtrationFin == 0) {

@@ -1332,7 +1333,8 @@  (calculateStatusFiltrationHivernage)
-        if ($filtrationDebut == 0 || $filtrationFin == 0) {
+        if (!is_numeric($filtrationDebut) || !is_numeric($filtrationFin)
+            || $filtrationDebut == 0 || $filtrationFin == 0) {

@@ -3478,7 +3480,7 @@  (postSave)
-            if (count($asservissements) > 0) {
+            if (is_array($asservissements) && count($asservissements) > 0) {

@@ -3501,7 +3503,7 @@  (postSave)
-            if (count($arretTotals) > 0) {
+            if (is_array($arretTotals) && count($arretTotals) > 0) {

@@ -3524,7 +3526,7 @@  (postSave)
-            if (count($marcheForcees) > 0) {
+            if (is_array($marcheForcees) && count($marcheForcees) > 0) {

Trois autres endroits parcourent ces mêmes réglages avec un foreach (asservissementOn(),
asservissementOff() et le listener au début du fichier). Là, PHP 8 se contente d’un
avertissement, donc rien ne casse — mais si tu passes dessus un jour, un is_array() y serait
aussi à sa place.

Je peux ouvrir une PR si tu préfères, dis-moi. Et si tu as besoin d’un test sur une
configuration particulière, je suis volontaire.

Merci encore pour le travail.

Merci beaucoup pour ce retour très précis, et pour les tests !

J’ai reproduit les deux problèmes sous PHP 8 et ton analyse est juste. J’ai intégré ton correctif tel quel dans cette PR, en te créditant dans le commit :
:backhand_index_pointing_right: Correctifs PHP 8 : enregistrement sans asservissement, recalcul après cache vidé, lever du soleil by scadinot · Pull Request #30 · scadinot/pool · GitHub

Petite précision au passage : l’erreur 500 apparaît dès qu’un seul des trois tableaux est vide. Avec une ligne d’asservissement mais rien en « Arrêt total », l’erreur se déplace simplement sur arretTotal. Ton contournement couvre bien les trois cas.

J’en ai profité pour corriger deux choses voisines :

  • Hivernage calé sur le lever du soleil : evaluateLeverSoleil() utilisait une variable non définie. Après un vidage du cache, les horaires revenaient, mais centrés autour de minuit.
  • Listeners : vider l’une des trois listes sur un équipement actif laissait l’ancien listener en place. Il est maintenant supprimé.

Tu t’étais proposé pour tester, alors avec plaisir ! Tout est dans core/class/pool.class.php sur la branche de la PR :

Il suffit de remplacer ce fichier dans plugins/pool/core/class/ puis d’enregistrer l’équipement. Si tu as un équipement en hivernage, un retour m’intéresse aussi.

Merci encore !