Bug de sérialisation en PHP 5.2.8

Un des utilisateurs du site Calcamo m’a signalé un problème sur les amortissements de crédit. Quand il faisait un remboursement anticipé, celui-ci n’était pas pris en compte dans les tableaux d’amortissement.

Comme je n’avais pas fait de modification sur ce site, récemment, je me suis douté que la cause était le changement de version de PHP installé il y a quelques jours.

Je vous passe la patie détection du bug, à base d’instructions echo, print_r et pour finir var_dump. Voilà ce que j’ai trouvé : les noms de clés d’objet numérique sont stockés sous forme de chaînes de caractères. Après une sérialisation puis une désérialisation, ces clés sont transformés en entier, ce qui rend impossible tout adressage direct.

Exemple :

// Définition objet dynamique
$a = (object) NULL;
$num = 3;
$a->$num->numero = "10";
$a->$num->montant = "1000";

if (isset($a->$num)){
    print "trouvé";
} else {
    print "pas trouvé";
}
// -> affiche trouvé 

// On sérialise / désérialise
$toto = serialize($a);
$a  = unserialize($toto);

if (isset($a->$num)){
    print "trouvé";
} else {
    print "pas trouvé";
}
// -> affiche pas trouvé

Dans le premier cas, la clé est « 3 » (string), après désérialisation, la clé est devenue 3 (integer). Il est impossible d’y accéder car PHP considère que la clé doit être du string et recherche systématiquement du « 3 » (string).

Contournement du bug

Pour contourner le bug, j’ai simplement réaffecté toutes les valeurs de l’objet avant son utilisation :

// Contournement bug sérialisation PHP 528
if (isset($a)){
  $save = $a;
  unset($a);
  foreach($save as $cle=>$valeur){
      $a->$cle = $valeur;
  }
}