Dotclear utilise, comme beaucoup d'applications, beaucoup de fonctions de rappel (callback). Seulement les développeurs ont fait l'erreur de définir des fonctions attendant des références.

Rappels :

  • en PHP on ne passe pas d'argument par référence au moment de l'appel (c'est déprécié depuis un bon moment) mais c'est la fonction qui déclare attendre une référence.
  • le passage par référence permet de modifier l'objet dans la fonction, mais surtout évite des copies et donc améliore les performances de l'application (consommation mémoire).

En résumé, on avait quoi :

function UneAction (&$objet) {
}
function LanceLesActions () {
call_user_func_array('UneAction', func_get_args());
}
LanceLesAction($monObjet);

L'appel de LanceLesActions provoque la copie de l'objet et donc le passage par valeur à UneAction qui attend une référence.

  • En PHP ≤ 5.2.9, c'était accepté et fonctionnait, partiellement car on n'avait pas le comportement attendu, et la fonction n'avait aucune chance de pouvoir modifier le contenu de l'objet. Donc des dysfonctionnements difficiles à détecter / corriger.
  • En PHP ≥ 5.3.0, c'est refusé, cela provoque un signalement, et la référence reçoit NULL au lieu d'une copie de l'objet. Donc ça ne marche plus, mais cela semble quand même plus robuste / propre.

C'est d'ailleurs écrit dans le brouillon des notes de mise à jour vers la version 5.3 : scratchpad upgrade 5.3

If a function with by-reference parameters is called by value ( for example with call_user_func_array) a warning message is produced and all by-reference parameters are set to NULL. In older versions, the function was called with by value parameters.

Quelles solutions ?

  • Soit modifier toutes les fonctions de rappel pour accepter uniquement des arguments passés par valeur, mais perdre l'optimisation et provoquer un grand nombre de copies inutiles (mais c'était l'ancien fonctionnement obtenu involontairement).
function UneAction ($objet) {
}
  • Soit imposer le passage par référence à LanceLesActions et perdre la souplesse de la liste d'arguments variable.
function LanceLesActions (&$objet) {
call_user_func('UneAction', $objet);
}

J'ai donc rapporté le dysfonctionnement au projet (bien entendu, comme souvent, avec une proposition de correctif) et j'attends leur retour.

A suivre..

P.S. bien sur c'est Dotclear qui est concerné ici, mais on risque de retrouver le même problème dans bon nombre d'applications.