Crash php sur ob_get_contents() conditions difficiles à déterminer, aucune piste : help

Eléphanteau du PHP | 19 Messages

19 mai 2015, 13:30

Bonsoir,

Sur un plugin WordPress dont je suis l'auteur (une création de sommaire de documents, non encore totalement finalisé), j'avais, depuis quelques semaines constaté une défaillance sur certains contenus, (avait fonctionné correctement pendant le développement) : un bug de cas particulier de détail à voir...

Je viens de finaliser des test.

Pour une raison totalement inconnue pour l'instant ob_get_content()interrompt la procédure courante qui l'appelle , pas un crash général (j'ai créé une trace -commentaire html -"avant" la fonction : ok, celui "après" n'est pas exécuté : la fonction exécute comme un "return" dans la procédure appelante).

Try catch ne renvoie rien, on plante dans le try, il n'y a pas d'exception générée.


Je n'ai donc pas de piste.
Je ne vois pas comment le contenu peut avoir une influence sur la fonction.
Je ne vois pas quel contexte d'exécution peut avoir une influence.
Je n'ai pas d'outil permettant d'analyser le crash interne de la fonction.
Il n'y a pas de message d'erreur (exécuté en mode debug chez OVH).

Merci d'avance pour des pistes.

Cordialement

Trebly

____________________________________________________________________________________________________________

Detail de contexte :
Plugin WordPress, widget de ma conception (non encore publié, en développement de compléments et qui fonctionnait parfaitement depuis deux mois) il traite le buffer en cours de création quand le contenu principal a été généré, un callback partiel en quelque sorte.
Le plugin est appelé après la création de ce contenu principal dans lequel je peux effectuer les replace utiles avant de repasser la main.
La génération dynamique de listes d'articles nécessite un callback pour faire un sommaire et quelques traitement de données numériques repérables par des balises spécifiques etc...
Quand le même plugin est appelé pour faire un sommaire d'un article isolé (sans liste d'articles) tout se déroule parfaitement bien. Les instructions sont identiques jusqu'à la recherche d'information dans le buffer. C'est la lecture du buffer qui permet de déterminer le type de document pour décider du mode de génération de sommaire.
Le crash se produit lors de cette lecture du buffer pour un type de contenus particulier, j'y perds mon latin puisque la lecture n'a en principe rien à voir avoir le contenu.
Je ne vois aucune raison pour que la situation soit différente en fonction du contenu (par exemple une ouverture d'un ob intermédiaire non refermé -i.e. ajax : sa lecture pourrait de toute manière s'effectuer, mais comme les traces immédiatement précédentes, générées par "echo de comment HTML" sont là il n'y rien à voir sur cette piste)...


NB : j'ai entre 50 000 et 100 000 lignes de code à mon actif.

Avatar du membre
Administrateur PHPfrance
Administrateur PHPfrance | 9782 Messages

19 mai 2015, 14:22

Bonjour,

Mon conseil serait que tu effectues ton développement en local sur ton poste par exemple en installant wampserver comme ça tu as la maitrise de la configuration et la possibilité d'aller chercher facilement dans les log d'Apache et de PHP ce qui est indispensable pour développer.

Par ailleurs, afin de debuguer efficacement, il faut essayer d'isoler au maximum le problème que tu rencontres, c'est à dire sortir la ou les fonctions qui posent problème pour les tester dans un nouveau script réduit à son minimum.
Quand tout le reste a échoué, lisez le mode d'emploi...

Eléphanteau du PHP | 19 Messages

19 mai 2015, 18:49

Merci,

J'ai un serveur local pour le développement (j'ai plus de cent sites de développement installés dessus).

L'installation de WordPress a été considérée comme un outil de production qui ne nécessitait pas de développement. Les développements sont relativement mineurs et ne nécessitaient pas d'avoir à déboguer (sauf js ce qui est exécuté en local). C'est ce qui s'est produit d'ailleurs jusque là et presque tout fonctionne dans l'appli. Et pour ce qui ne convient pas on contourne.
Quant à la fonction de lecture de output buffer, elle fonctionne 200 fois dans l'appli.

La nature du plantage ne me laisse que très peu d'espoir de reproduction après un changement d'environnement.
Un try catch sur ob_get_contents() qui ne récupère rien (il n'y a pas d'erreur récupérable), ne laisse pas beaucoup d'espoir d'une solution simple et d'un problème facilement maîtrisable s'il n'est pas connu.

Je ne compte que sur une seule chose, des cas similaires sur la même fonction, qui résolus donneraient une piste. Cette fonction de bas niveau sans paramètre ne peut pas se planter sauf sur une défaillance système, pas sur un problème de développement. Et pour ma part je ne débogue pas php.

Ma recherche sur le net n'a donné aucun cas de plantage de cette fonction fondamentale.

Si un spécialiste de développement de php (pas en php) pouvait dire ce qui peut produire le phénomène observé on avancerait.

Cordialement

Trebly

Eléphanteau du PHP | 23 Messages

19 mai 2015, 21:29

quelques autres pistes : est ce que tu as testé avec plusieurs version de PHP ? sous plusieurs système d'exploitation ?

à tout hasard j'ai lancé une recherche sur "ob_get_contents" sans succès dans la base de bug mais je ne suis pas très familier
avec cela donc quelqu'un d'autre pourra peut-être mieux aider pour cette piste
https://bugs.php.net/

Avatar du membre
Administrateur PHPfrance
Administrateur PHPfrance | 9782 Messages

20 mai 2015, 00:28

L'installation de WordPress a été considérée comme un outil de production qui ne nécessitait pas de développement. Les développements sont relativement mineurs et ne nécessitaient pas d'avoir à déboguer
Considéré par qui ? :shock: Car c'est contraire à toutes les best practices existantes...
Avant de faire un déploiement en prod, on doit toujours passer par une phase de dev et/ou de préprod... surtout quand ce n'est pas juste un Wordpress "out of the box" mais bien un plug-in dont tu es l'auteur.
Et dans le cas présent tu as bel et bien un bug quelque part qu'il est nécessaire de déboguer...

La nature du plantage ne me laisse que très peu d'espoir de reproduction après un changement d'environnement.
Pour avancer, il faut que l'on ait le code problématique, la config de la plateforme et les logs.

Il est donc d'abord nécessaire que tu isoles la partie problématique pour la réduire au minimum afin de reproduire le problème.
Ensuite pour les logs, soit tu peux les avoir via OVH, ou sinon tu peux reproduire l'environnement (par exemple dans une machine virtuelle) pour être le plus proche possible de ton environnement de prod.
Je maintiens que si tu n'as pas accès au log de ton serveur, il est très difficile de faire un debugage ou même une remontée de bug au PHP group qui soit efficace.
Cette fonction de bas niveau sans paramètre ne peut pas se planter sauf sur une défaillance système, pas sur un problème de développement.
D'expérience, 99.9% des développeurs qui mettent en cause le moteur PHP plutôt que leur code se trompe.
PHP est une plateforme ultra-mature avec plusieurs dizaines de millions d'utilisateurs et un nombre de retours d'expérience hallucinant que ce soit dans la doc officielle, les forums ou même le bugtracker officielle, donc si tu n'as rien trouvé en cherchant sur internet, il est fort probable que le problème vienne de ton code ou d'une spécificité de ton hébergeur.
Quand tout le reste a échoué, lisez le mode d'emploi...

Eléphanteau du PHP | 19 Messages

20 mai 2015, 04:41

Bonsoir,

Code : Tout sélectionner

Le générateur de widget à écrit <div id='widgets'> et lance widget_toc function widget_toc ($args=array(), $instance=array() ) { echo '<!-- Widget_toc begin -->'; $str_ob = ob_get_contents(); echo '<!-- Widget_toc begin : got output buffer (ob_get_content) -->'; ... }
Dans certain cas d'exécution ce code produit (le widget est le premier) :

Code : Tout sélectionner

<div id='widgets' > <!-- Widget_toc begin --> <!-- Widget_toc begin : got output buffer (ob_get_content) --> puis tout le code html correct du widget puis les autres widgets suivants </div>
Dans le cas de plantage :

Code : Tout sélectionner

<div id='widgets' ...> <!-- Widget_toc begin --> puis les autres widgets suivants </div>
Il n'y strictement rien qui puisse produire une erreur.

Maintenant, qu'il y ait une anomalie précédente qui ne génère pas d'erreur signalée à l'exécution ou conduisant à un crash, mais perturbant l'exécution de Php de sorte que l'appel de ob_get_contents() produise un effet inattendu, ça je suis tenté de le croire.
Mais ce code n'est pas le mien.

Pour information, indépendamment de certains développements que je réalise depuis longtemps, j'au eu besoin d'un outil pour publier un blog. Je l'ai pris standard sur étagère. Malheureusement le générateur de table des matières, un plugin existant, a une insuffisance assez rédhibitoire que j'ai corrigée (ne traite pas dans sa version courante distribuée pas les documents dits "multipages" découpés en sections avec des url chargées successivement. Pour cela j'ai du, tout de suite au début du widget lire le buffer, et il s'ensuit du code certes, mais postérieur dans l'exécution à l'endroit du plantage, et cela fonctionnait depuis quatre mois sans broncher. Et puis sans crier gare et aucune modification nouvelle, l'exécution se plante... sur la lecture alors qu'aucun code modifié n'a encore été exécuté.

J'entreprends donc, puisqu'il est impossible de trouver de cas de plantage de cette fonction, le rapatriement de l'application sur ma plateforme de développement.

Mais je sais qu'il il n'y aucune chance que je trouve une erreur qui "implique" le plantage, et je suis quasi certain que les logs Apache-Php et surtout les traces que pourra produire XDebug ne donneront aucun lien entre une erreur et le plantage.

Par contre en éliminant des erreurs ou alertes non fatales (générées par le code de WordPress ou d'autres plugins précédents dans l'exécution), je pense qu'il est à peu près certain que le problème disparaîtra sans que jamais on ne découvre pourquoi, ni surtout que le délai pour le traiter soit maîtrisable.

Et en l'occurence il n'y a pas de contournement possible.

Ceci étant j'ai déjà connu plusieurs cas de plantages (pas en php mais en langage compilé) du à des données non (mal) contrôlées, et en fait de contenu invalide, générant des défaillances aléatoires générant en cascade des erreurs ultérieures à des emplacements imprévisibles (en général les données invalides entraînaient un débordement mémoire sans conséquence immédiate...).

Evidemment le problème est qu'il n'y a pas de limite ni d'effort ni de temps prévisible à consacrer à ce type de problème.

Cordialement

Trebly

Avatar du membre
Administrateur PHPfrance
Administrateur PHPfrance | 9782 Messages

20 mai 2015, 09:02

Il n'y strictement rien qui puisse produire une erreur.
Si tu ne veux pas parler d'erreur, parlons à défaut d'un comportement inattendu mais dans tous les cas, il y a bien un problème quelque part sinon tu ne serais pas là.
Maintenant, qu'il y ait une anomalie précédente qui ne génère pas d'erreur signalée à l'exécution ou conduisant à un crash, mais perturbant l'exécution de Php de sorte que l'appel de ob_get_contents() produise un effet inattendu, ça je suis tenté de le croire.
Mais ce code n'est pas le mien.
Ton code s'appuie sur du code de Wordpress, il t'appartient de bien vérifier que toutes les données que tu récupères en entrée soient conformes à ce que tu attends et il faut que tu remontes les appels de fonctions pour valider que l'architecture dans laquelle tu t'inscris fait bien les bons appels aux bons moments tels que tu l'as conçu.

En l’occurrence, avec les quelques lignes que tu nous as donné, tu fait un appel à ob_get_contents() mais tu ne sembles pas maitriser le moment où ob_start() est déclenché ni qu'il n'y aurais pas un ob*_flush() ou ob*_clean() ou équivalent qui viderait le buffer et t’empêcherait de l'exploiter.
cela fonctionnait depuis quatre mois sans broncher. Et puis sans crier gare et aucune modification nouvelle, l'exécution se plante... sur la lecture alors qu'aucun code modifié n'a encore été exécuté.
En informatique, rien n'est magique, il y a donc forcément eu une modification quelque part, que ce soit une mise à jour de wordpress (qui peut se faire automatiquement), d'un de ses plugin ou une mise à jour de la plateforme qui t'héberge...
J'entreprends donc, puisqu'il est impossible de trouver de cas de plantage de cette fonction, le rapatriement de l'application sur ma plateforme de développement.

Mais je sais qu'il il n'y aucune chance que je trouve une erreur qui "implique" le plantage, et je suis quasi certain que les logs Apache-Php et surtout les traces que pourra produire XDebug ne donneront aucun lien entre une erreur et le plantage.
A un moment il va peut être quand même falloir se remettre en question... et arrêter les "je sais que c'est impossible", "je sais que *mon* code est bon..."
Par contre en éliminant des erreurs ou alertes non fatales (générées par le code de WordPress ou d'autres plugins précédents dans l'exécution), je pense qu'il est à peu près certain que le problème disparaîtra sans que jamais on ne découvre pourquoi, ni surtout que le délai pour le traiter soit maîtrisable.
Si tu procède à un débogage méthodique en comprenant ce que tu fais, il n'y a pas de raison que le problème disparaisse sans savoir pourquoi... :D

Ceci étant j'ai déjà connu plusieurs cas de plantages (pas en php mais en langage compilé) du à des données non (mal) contrôlées, et en fait de contenu invalide, générant des défaillances aléatoires générant en cascade des erreurs ultérieures à des emplacements imprévisibles (en général les données invalides entraînaient un débordement mémoire sans conséquence immédiate...).
D'où l'importance de bien valider en entrée les données reçues de l'extérieur et de comprendre le fonctionnement du code dans lequel tu t'insères.
Quand tout le reste a échoué, lisez le mode d'emploi...

Eléphanteau du PHP | 19 Messages

20 mai 2015, 14:53

Bonjour,
Si tu ne veux pas parler d'erreur, parlons à défaut d'un comportement inattendu mais dans tous les cas, il y a bien un problème quelque part sinon tu ne serais pas là.
Nous sommes parfaitement d'accord.
La seule cause possible me semble d'avoir donné à manger au code des données indigestes quelque part avant dans l'exécution.
Il y a, je l'espère bien une "erreur" détectable, sinon je suis mal parti si je veux avoir une appli stable.

Pour la petite histoire j'ai eu un cas sévère qui m'a coûté une entreprise, que j'ai trouvée plus de dix ans plus tard... de mémoire, je relisais le code sans objectif précis quand j'ai constaté un absence de contrôle sur des données... C'était ça le client donnait à manger des données totalement improbables et la limitation d'un code que je n'avais pas écrit n'était pas spécifiée. C'est un peu mon autre message sur le format ligne batard (x0D0D0A mélange Windows Unix) mais là j'ai trouvé très vite.

Ton code s'appuie sur du code de Wordpress, il t'appartient de bien vérifier que toutes les données que tu récupères en entrée soient conformes à ce que tu attends et il faut que tu remontes les appels de fonctions pour valider que l'architecture dans laquelle tu t'inscris fait bien les bons appels aux bons moments tels que tu l'as conçu.
Si tu es en train de me dire qu'un utilisateur de WordPress, je ne le crois pas, doit vérifier le code quand s'il écrit : "Jean est allé à l'école", "Claude est allée à l'école", passe mais que "Jean-Michel est allé à l'école" est une erreur parce que le code ne sait pas traiter le noms composés... On ne peut pas être d'accord.

Dans le cas où une pile d'appels serait en cause, comme d'une part ce qui précède ne sollicite pas mon développement et que j'ai pris de plus soin de rajouter (depuis) un test sur les paramètres de la fonction (qui ne sont, de plus, pas encore utilisés), je ne pense pas que la question soit là. Ceci étant je peux faire une extraction sur les deux array d'options reçues en paramètres. Je n'y crois pas du tout (trop long à expliquer). Tout ce qui se passe avant est du standard WordPress mais ce qui est exécuté est plus complexe dans le cas du plantage. C'est la piste mais pas dans mon code. Ce que je récupère est sain ou bien, le contenu du buffer ne pas pouvoir agir sur l'exécution de ob_get_content() qui devrait pouvoir retourner une chaîne binaire quelconque (inutilisable certes), mais elle l'est puisque c'est bien sont contenu, auquel l’accès ne peut avoir lieu qui génère ensuite la sortie dans laquelle il n'y a aucune anomalie.

Tu dis :
En l’occurrence, avec les quelques lignes que tu nous as donné, tu fait un appel à ob_get_contents() mais tu ne sembles pas maitriser le moment où ob_start() est déclenché ni qu'il n'y aurais pas un ob*_flush() ou ob*_clean() ou équivalent qui viderait le buffer et t’empêcherait de l'exploiter.
Sauf que, j'ai donné le code d'origine mais pas les test, avant d'écrire j'ai tout vérifié (du moins je crois) et si le buffer était vide ou non ouvert il retournerait une chaîne vide ou false. Ceci étant comme le commentaire qui précède fait bien partie de la page HTML générée il n'y a pas de question a se poser.
En informatique, rien n'est magique, il y a donc forcément eu une modification quelque part, que ce soit une mise à jour de wordpress (qui peut se faire automatiquement), d'un de ses plugin ou une mise à jour de la plateforme qui t'héberge...
Mais si, dans les données, le cas que je cite plus haut le produit fonctionnait depuis des années et puis un jour quelqu'un a créé un jeu de données "invraisemblable" pour l'homme de l'art... ou bien une combinaison théorique valide mais... mais où
Il est aussi possible qu'une mise à jour de plugin ou du noyau de WP soit à l'origine de problème. Et là il est presque totalement impossible de rembobiner, sauf vérifier que cela fonctionnait, et faire de comparaison de traces...
A un moment il va peut être quand même falloir se remettre en question... et arrêter les "je sais que c'est impossible", "je sais que *mon* code est bon..."
Mais le plantage arrive avant que mon code ne s'exécute. A moins que l'exécution future ne lu fasse peur...

Je dis :
Mais je sais qu'il il n'y aucune chance que je trouve une erreur qui "implique" le plantage, et je suis quasi certain que les logs Apache-Php et surtout les traces que pourra produire XDebug ne donneront aucun lien entre une erreur et le plantage.
Je ne dis pas que je ne vais rien trouver, mais qu'il n'y aura pas d' "implication" directe. Autrement dit ob_get_content() arrête la fonction parce qu'il y a eu un "accident" en amont. J'ai eu le cas ou la zone mémoire lue (une string) avait été écrasée par une instruction précédente, bien plus haut dans le code. C'est donc en éliminant des erreurs possibles ou certaines mais sans conséquences immédiates que le problème disparaîtra. Je te donne rendez-vous.
Si tu procède à un débogage méthodique en comprenant ce que tu fais, il n'y a pas de raison que le problème disparaisse sans savoir pourquoi... :D
Mais si, je saurais qu'il y a une relation de cause à effet (même reproductible) mais je n'aurai pas d'explication de la causalité : Si x alors y, est vrai mais pourquoi ?
D'où l'importance de bien valider en entrée les données reçues de l'extérieur et de comprendre le fonctionnement du code dans lequel tu t'insères.
C'est vrai mais c'est un voeux pieux, avec 12000 modules et 1000000 de lignes de code...

Il y a presque sans aucun doute un problème de "solidité" de code, peu détectable à la simple lecture, qui fait faire à php quelque chose d'imprévu dont la conséquence est un comportement anormal plus tard, comme un trou dans une chaussée, tant qu'on en passe pas dedans on peut ne pas savoir qu'il existe.

Je bosse et je te tiens au courant.

Cordialement

Trebly

Avatar du membre
Mammouth du PHP | 1609 Messages

20 mai 2015, 16:36

Salut, peut être qu'un code vide le buffer après l'appel de widget_toc mais avant les widgets suivants.
Mais dans ce cas je pense que la ligne <!-- Widget_toc begin --> devrait également être absente car n'ayant pas de ob_start, elle est dans le même tampon que ce qui vient après le ob_get_contents().

As tu essayé d'afficher le buffer après l'appel à ob_get_contents ? à la fin de la fonction widget_toc ?

As tu regardé, débogué le code du générateur de widget ? tu pourrais probablement vérifier le buffer après chaque génération de widget.

PS : en tout cas pour moi, avec les éléments que j'ai, le problème viendrait à priori plutôt du code après le ob_get_contents de la fonction widget_toc. Pas forcément dans la fonction, mais dans ce qui se passe après.
Développeur web depuis + de 20 ans

Eléphanteau du PHP | 19 Messages

20 mai 2015, 21:40

Salut,

En résumé et pas dans l'ordre :
J'ai encadré le ob_get_content() entre deux commentaires html comme je l'indique.
J'ai le premier, pas celui qui suit.
Mais pas toujours, avec des données différentes tout s'exécute normalement, conclusion : c'est l'exécution qui précède sur ces données différentes qui conduit à un comportement anormal de ob_get_contents() (j'ai vu des programmes s'arrêter sur un $i.='tata'; ou d'autres instructions anodines).
As tu essayé d'afficher le buffer après l'appel à ob_get_contents ? à la fin de la fonction widget_toc ?
Désolé mais comme c'est l'instruction de base d'affectation à une variable du contenu du buffer qui plante et qui fait sortir de la procédure comme je l'indique, il est vérifié qu'aucune instruction de la fonction ne s'exécute après le ob_get_content(); qui s'avère terminal.

Et, bien sur, comme je récupère, in fine, la page complète sauf le widget, tout s'exécute normalement sauf le widget dès le ob_get_contents().
Le contenu est affiché et sans aucune anomalie, à l'exception du widget vide...

Donc après être sorti du widget l'exécution reprend son cours normalement.

Le buffer reste ouvert, j'ai écrit mon commentaire dedans et il est dans la page (examen du code html généré), et d'autres widgets qui suivent continuent ensuite à écrire, tout va bien, sauf que ob_get_contents() dont le rôle n'est que de lire le contenu du buffer se comporte comme un return... c'est pour moi la preuve qu'il travaille sur quelque chose de pas bien propre résultant de ce qui précède (des erreurs non récupérées et non terminales n'ayant probablement rien à voir).

Cordialement

Trebly

Avatar du membre
Mammouth du PHP | 1609 Messages

21 mai 2015, 11:43

Re trebly, je n'ai personnellement jamais vu un ob_get_contents() provoquer une sortie de fonction et je dois avouer que je suis plutôt sceptique à cette idée.

Mais si tu es persuadé que c'est ce qui se passe, je ne vois pas en quoi nous pourrions t'aider ?

Il suffirait d'un ob_get_clean() ou ob_end_clean() mal placé pour obtenir le "cas de plantage".

EDIT
Sans voir le code qui s'exécute avant et le code qui s'exécute après c'est impossible de déterminer avec certitude d'où peut précisément venir le problème mais ce qui est sur c'est que la ligne <!-- Widget_toc begin --> fait partie du buffer retourné par le ob_get_contents qui vient juste après et que cette ligne s'affiche bien (ainsi que tout ce qui précède apparemment). Il semble donc qu'il n'y ait pas de problème avec ce buffer étant donné qu'il est bien affiché.
Développeur web depuis + de 20 ans

Eléphanteau du PHP | 19 Messages

21 mai 2015, 19:42

Je ne suis persuadé de rien, je constate :

Code : Tout sélectionner

function appel() { echo '<div ..>'; A(); B(); echo "</div>"; } function a() { echo "<!-- avant ob_get_content par a() -->"; ob_get_content(); echo "<!-- avant ob_get_content par a() -->"; ..... } function b() {echo "<!-- début de b() -->"; .... }
Html généré

Code : Tout sélectionner

<html précédent la fonction puis le tag précédent généré par appel() : <div...> <!-- avant ob_get_content par a() --> <!-- début de b() -->"; < HTML code généré ensuite par fonction suivante B > etc... </div>
Il suffirait d'un ob_get_clean() ou ob_end_clean() mal placé pour obtenir le "cas de plantage".
Désolé c'est à coté de la question et faux
1- le buffer est ouvert puisque l'echo "avant" est bien écrit, et il le reste puisqu'on le retrouve dans la sortie finale.
2- s'il était fermé, son contenu, à la suite d'une ouverture implicite générée par la commande echo serait le commentaire lui-même et je le récupérerai dans la variable et s'il n'est pas ouvert (et en l'absence du commentaire qui est volontairement écrit là) ob_get_contents renverrait false ce que j'ai, bien sur, testé.

C'est évidemment déroutant. L'aide, c'est d'aller à la pêche ou d'ouvrir des pistes, même de tenter d'en ouvrir.
Il est important de savoir que des données accidentellement erronées passant des filtres peuvent provoquer des effets totalement inattendus et bien avant que l'accident final intervienne.
Et la il n'y a pourtant rien d'anormal nulle part, c'est à dire détectable avec les meilleurs moyens existants.

L'affichage final est sans aucun défaut sauf l'absence dans certains cas de data du résultat d'exécution de a()...

A oui au fait, j'ai transféré data et soft sur mon serveur, tout se passe bien exécution parfaite.
La même requête avec les mêmes data et le même code produisent l'anomalie chez OVH.

Parfaitement reproductible avec les mêmes data, je rappelle qu'avec certaines data on passe et pas avec d'autres : A creuser avec des variantes (les journées n'ont que 16 à 20h utilisables).

xDebug ne donne pas d'anomalies. Deux erreurs mineures détectés, corrigées, mais non impliquées (aucun changement sur les deux plateformes)
Test effectué avec php 5.4.6 (ma plateforme sous windows) et 5.5.x et 5.4.x chez OVH : même résultat.

Je penche maintenant pour un pb de config.php avec une erreur sans trace chez OVH, il n'y a pas de piste à suivre sauf contrôler ce qui aurait pu changer sur la plateforme OVH depuis un mois environ et essayer des jeux de données différents.

Cordialement

Trebly

Avatar du membre
Mammouth du PHP | 1609 Messages

21 mai 2015, 20:08

Je ne vois pas en quoi ce que je dis est faux. A côté peut être, mais c'est dur de proposer des pistes avec le peu d'éléments et je n'ai aucune piste concernant ob_get_contents qui provoquerait une sortie de fonction.
<?php
echo '-- BEFORE ob_get_contents() --<br/>';
$str = ob_get_contents();
echo '-- AFTER  ob_get_contents() --<br/>';

echo $str;
Affiche :
-- BEFORE ob_get_contents() --
-- AFTER ob_get_contents() --
-- BEFORE ob_get_contents() --
Cet exemple illustre l'effet du ob_end_clean (il produit bien un affichage similaire au cas de plantage, on a la sortie qui précède le ob_get_contents mais pas la sortie qui suit) :
<?php
echo '-- BEFORE ob_get_contents() --<br/>';
$str = ob_get_contents();
echo '-- AFTER  ob_get_contents() --<br/>';

ob_end_clean();
echo $str;
Affiche :
-- BEFORE ob_get_contents() --
Et cet exemple pour montrer que c'est bien le echo $str qui produit l'affichage :
<?php
echo '-- BEFORE ob_get_contents() --<br/>';
$str = ob_get_contents();
echo '-- AFTER  ob_get_contents() --<br/>';

ob_end_clean();
N'affiche rien.

En revanche ton exemple n'imprime pas ce que tu dis, il imprime ceci (le commentaire après ob_get_contents s'affiche aussi) :
<div><!-- avant ob_get_content par a() --><!-- avant ob_get_content par a() --><!-- début de b() --></div>
Développeur web depuis + de 20 ans

Avatar du membre
Administrateur PHPfrance
Administrateur PHPfrance | 9782 Messages

21 mai 2015, 21:41

On en revient toujours au même point : il faut que tu réussisses à isoler ton problème dans un code PHP le + simple possible pour le mettre en évidence et qu'on puisse faire des tests dessus.
Quand tout le reste a échoué, lisez le mode d'emploi...

Eléphanteau du PHP | 19 Messages

23 mai 2015, 03:51

Bonsoir,

Je reviens vers vous pour vous tenir au courant. Le problème n'est pas résolu.

Remarque:
En revanche ton exemple n'imprime pas ce que tu dis, il imprime ceci (le commentaire après ob_get_contents s'affiche aussi) :

Code : Tout sélectionner

<div><!-- avant ob_get_content par a() --><!-- avant ob_get_content par a() --><!-- début de b() --></div>
Je pense que tout le monde a compris qu'il y avait une erreur dans la mise en forme du message il fallait lire "après" dans le code que j'avais un tout petit peu mis en forme avec un copier coller alors que le code comprend des notes.

Ceci étant, après installation sur le serveur dans un premier temps j'ai obtenu sur les mêmes données sur le serveur OVH l'erreur et sur mon serveur un fonctionnement correct. Mais après quelque chose d'inconnu il se sont mis à avoir un comportement identique avec le même défaut.

Les différences de config :
  • Le serveur OVh fonctionne en mode "developpement" et WP est configuré en mode debug php 5.5.22 (mais le pb est identique avec 5.4 en changeant de moteur grâce ovhconfig), mémoire 512Mo
  • Le serveur local de developpement est avec xDEBUG actif et le mode WP debug tourne en php 5.4.6, mémoire 375Mo
xDEBUG ne donne pas d'erreur.

J'ai complété le test avant exécution avec les fonctions ob_get_level() et ob_status(true) (trace avec commentaire ou var_dump) qui rapportent la même chose dans tous les cas et aucune anomalie.
Je teste et trace aussi les variables amont (paramètres passés et d'environnement), il n'y a rien.

La taille mémoire utilisée sur OVH est de 27Mo et de 57 sur le serveur de développement, donc RAS.
Quand ob_get_contents() fonctionne le besoin mémoire supplémentaire est de 2Mo donc RAS.
Il y a 4 niveaux de buffer, le dernier, celui des données HTML principales qui est celui que j'analyse et que je récupère et modifie quand ça marche, contient de manière variable environ de 60 à 100Ko (ce n'est que du texte, les images et d'autres éléments sont en href chargées par les requêtes générées par le navigateur après le premier chargement par les href ou ajax).

Donc rien à creuser.
Demain je teste avec ob_get_clean() et si ça fonctionne il n'y aura qu'à réécrire le contenu, c'est le seul contournement possible et auquel je viens de penser. Dans l'exécution normale c'est après avoir traité correctement la copie du buffer récupérée par bo_get_contents() que je fais un clean avant d'écrire le contenu modifié.

La différence entre les cas de bon fonctionnement et celui du défaut d'exécution est la nature des traitements.
En effet, dans le cas du fonctionnement correct on affiche un article unique.
Dans le cas du plantage il y a une boucle qui génère une liste d'articles, mais ce travail est terminé quand l'incident se produit et le code html généré fait apparaître les DIV successives sans anomalies. par contre c'est bien là la différence déterminante.
Donc c'est toujours le seule piste valide.

@rthur me conseille de faire un code simple, c'est vrai, c'est souvent la solution quand certains traitement posent problème. C'est ce que je fais très fréquemment et que j'ai fait là pour mettre au point le traitement du buffer (qui suit quand il est exécuté) avec des expressions régulières un peu complexes que j'ai fait tourner en simulation sur des jeux test les plus tordus possible.
Mais là, quand l'incident se produit c'est sur une fonction qui, réduite, n'a qu'une seule instruction et la pile d'appel est identique entre le cas du défaut et celui du bon fonctionnement. On en est au même stade d'exécution. Je ne vois vraiment pas comment isoler la procédure et recréer un environnement (buffers, utilisation mémoire etc...) identique, mais variable, extensible, avec l'espoir qu'en reconstruisant WP progressivement autour on verra apparaître le problème, dans dix ans... peut-être.
Reproduire un incident de ce type à partir de zéro, en complexifiant autour d'une seule instruction est à mon avis impossible et les chances de produire l'effet sont nulles. Seule une explication rationnelle du comportement observé pourrait permettre de connaitre la nature du problème.

Par contre, comme je l'ai rappelé plus haut, il y a sans aucun doute quelque chose qui se passe mal sans erreur signalée lorsque la boucle de parcours de la liste est exécutée, ceci même si le résultat est correct, et ça je ne l'ai pas encore vu. Faire un trace détaillée et l'analyser pour espérer voir quelque chose est aussi une piste ?
Je peux aussi déshabiller les fonctions qui interviennent dans la boucle effectuée pour produire la liste d'articles, c'est peut-être bien la meilleure piste. Et ensuite produire et analyser un trace d'exécution concernant uniquement cette partie. Si le problème a disparu en ré-habillant les quelques centaines de ligne de code concerné, le phénomène peut peut-être réapparaître.

Une autre piste est d'avoir un cas similaire que personne malheureusement n'a jamais vu pour comparer et trouver les points communs, réduire la variété, on ne l'a pas.

Merci de votre aide, elle me force à réfléchir, en particulier en m'exprimant ici.

Cordialement

Trebly