Embauchez-moi

Je suis freelance ! Besoin d'un professionnel du développement web ? Pourquoi ne pas me passer un coup de fil ?

Plus d'infos sur… http://thibault.jouannic.fr

mots-cles : Ingénieur web freelance Symfony eZ Publish Solr

Comment PHP me rends fou

Chers amis développeurs bonsoir. Je vous écris car je souhaite soumettre à votre sagacité un problème étrange. À défaut de pouvoir me venir en aide (seule ma dignité m’empêche encore de verser des larmes de frustration), peut-être au moins cela vous fera-t-il marrer. Je suis victime d’une version particulièrement vicieuse du bug de 17h30 (avec un combo « bug du vendredi de fin d’itération » compte triple).

Projet symfony. Je code, tout se déroule bien, fin de semaine, je balance mon code sur le serveur de recette, et je jette un coup d’œil sur l’environnement de prod. Horreur ! Page blanche ! Je vérifie la même page sous l’environnement de dev. Elle s’affiche correctement.

Je jette un coup d’œil dans la log apache :

child pid 16813 exit signal Segmentation fault (11)

Un sentiment d’immense lassitude m’envahit aussitôt.

Après une longue et pénible session profilage / debugage avec xdebug, je finit par circonvenir plus ou moins l’endroit du problème, et parviens même à le reproduire à l’envie. Vous allez voir, c’est surprenant :

$category = Article->getCategory(); // objet de classe 'Category'
echo $category;  // Affiche le titre de la catégorie
printf('%s', $category); // Page blanche, segfault, sacrifice de chatons, etc.

Amusant, non ? Après quelques recherches, il semblerait que dans le contexte d’une fonction *printf, php n’utilise pas la fonction magique __toString().

Je teste donc :

<?php
 
class Toto
{
  private $tata;
 
  public function __construct($tata)
  {
    $this->tata = $tata;
  }
 
  public function __toString()
  {
    return (string) $this->tata . "\n";
  }
}
 
$toto = new Toto('tutu');
echo $toto;
printf('%s', $toto);
 
// tutu
// tutu

WTF ? Mais ça marche trés bien ! C’est donc un problème spécifique à mon code ? Je poursuis mon débuggage, et j’arrive ici :

$category = $article->getCategory();
// $category est de la classe sfOutputEscaperIteratorDecorator
// qui hérite de sfOutputEscaperObjectDecorator
// qui définit une fonction __toString()
 
printf('%s', $category);
// Appelle sfOutputEscaperObjectDecorator::__toString

Bon, allons voir sur place ce qui ne va pas.

class sfOutputEscaperObjectDecorator
{public function __toString()
  {
    // Jusqu'ici, tout va bien, mais dés l'instruction return, ça plante
    return $this->escape($this->escapingMethod, $this->value->__toString());
  }
 
  // Je modifie donc la fonction pour obtenir ceci :
  public function __toString()
  {
    $value = $this->escape($this->escapingMethod, $this->value->__toString());
    var_dump(gettype($value));
    var_dump($value);
    die();
    return $value;
  }
 
// J'obtiens pour affichage :
// 'String'
// 'Titre de ma catégorie'
 
// En revanche :
 
  public function __toString()
  {
    $value = $this->escape($this->escapingMethod, $this->value->__toString());
    var_dump(gettype($value));
    var_dump($value);
    // die();
    return $value;
  }
 
// Page blanche. Continuons dans l'étrange, avec quelques modifications de la même méthode :
 
  public function __toString()
  {
    $value = $this->escape($this->escapingMethod, $this->value->__toString());
    die('ici'); // Affiche 'ici'
    return 'toto';
  }
 
  public function __toString()
  {
    return 'toto'; // Affiche 'toto'
  }
 
  public function __toString()
  {
    $value = $this->escape($this->escapingMethod, $this->value->__toString());
    return 'toto'; // Page blanche
  }
}

Tout ça, bien sûr, c’est sur l’environnement de prod. En dev, tout se passe toujours normalement.

J’en suis là. Si quelqu’un a une bonne explication. En attendant, je crois que je vais aller me coucher de bonne heure. Bon week-end à tous.


7 Commentaires

  1. NiKo
    Posté le 12/03/2010 à 21:04 | Permalien

    Bug bien connu de php 5.2 sur les toString(). Monte en version, ou appele directement la propriété :)

    Dsl sur iphone je suis un peu laconique.

  2. Posté le 15/03/2010 à 15:59 | Permalien

    Et si dans la fonction __toString() tu remplaces l’appel à $this->value->__toString() par «  toto  », ça plante toujours ?

    J’ai comme l’impression que toString() est pas trop réentrante et que la rappeler depuis _toString() est la raison pour laquelle PHP part en vrille.

  3. Pierre Beau
    Posté le 25/03/2010 à 11:20 | Permalien

    Salut,

    Je crois que le truc le plus sûr pour éviter ce genre de désagrément, c’est de faire les développements & les tests sur le système de production.
    Plus de souci d’installation, de différence de niveau qui génère des effets dont les causes sont difficiles à trouver, &c.
    En plus, ça permet d’utiliser les systèmes de développement ou de test pour faire de la perruque… que du bonheur, je te dis…

    Vale
    Pierre

  4. Posté le 25/03/2010 à 11:26 | Permalien

    @Pierre Je crois qu’il y a méprise. Je parlais bien des environnements sous symfony, l’outil qui permet d’adapter ta conf simplement en changeant de controlleur frontal.

    Les systèmes de dev et de production sont, à quelques poils près, les mêmes.

  5. Posté le 25/03/2010 à 11:37 | Permalien

    thibault> gaffe à l’impact du «  poil près  » quand même, des fois une version mineure de PHP différente (voire de distrib utilisée) et c’est le drâme.

  6. Posté le 17/04/2010 à 10:07 | Permalien

    Les stratégie du Decorator sont les même en dev et prod ?

  7. Posté le 19/04/2010 à 09:19 | Permalien

    Tiens c’est marrant, il m’est arrivé la même chose vendredi : gros bug de la page blanche sur l’environnement de prod (et pas en dev).

    C’était également un bug de php et le code exécuté était le même en prod et en dev. Le bug était un «  method_exists  » qui provoquait également un «  segmentation fault  » de Apache ; ce bug est répertorié sur bug.php.net.
    Je pense qu’en dev, il doit chargé plus de classe et donc ça empêche le problème de survenir. Il me semble avoir déjà eu ce bug il y’a 6 mois.

    Si vous avez un problème de page blanche en prod, regardez du coté des method_exists.