web-dev-qa-db-fra.com

Dérouté: PHP Erreur fatale: Exception levée sans cadre de pile dans Unknown sur la ligne 0

J'ai constaté que l'une des raisons courantes de l'erreur est une exception est levée depuis un gestionnaire d'exceptions . Je suis à peu près sûr que cela ne se produit pas dans l'application que j'essaie de déboguer ... Mais j'ai placé toutes les lignes de traitement d'initialisation en haut du fichier index.php dans un try/catch. *

Cela peut apparemment aussi arriver parce que certaines choses ne peuvent pas être sérialiséespour être stockées dans une session . Tout au plus cette application stocke-t-elle des tableaux dans la session (pas mal), mais je suis convaincu qu'elle ne stocke rien d'extraordinaire.

Quelqu'un a commenté que cela leur était arrivé parce que leur clé primaire devait être CHAR (32) au lieu de INT (11) . Les PK dans cette application sont tous les INT.

D'autres suggestions sont que cela pourrait être un problème avec PHP 5.3.3corrigé dans 5.3.6 , disque complet et/ besoin de transtyper une valeur SimpleXML . Il se trouve que nous utilisons PHP 5.3.3, mais la mise à niveau serait un dernier recours dans ce cas. Cela n'a pas toujours été le cas.

UPDATE/NOTE: En fait, je ne peux pas reproduire l'erreur moi-même, je la vois seulement dans les journaux, voir le paragraphe ci-dessous pour savoir où je crois que l'erreur se produit ...

* D'après les journaux d'erreurs, il semble probable qu'au moins un endroit où cela se produit est index.php. Je déduis cela uniquement parce que cela est indiqué dans certaines entrées par une URL de référence. Le code try/catch ne concerne actuellement que la partie "initiale" du script, en dessous se trouve principalement la sortie HTML. Il y a du code PHP dans la sortie (ce qui est plutôt simple, cependant), il faudra peut-être le tester. Voici la partie catch qui ne produit aucune sortie dans les journaux:

} catch (Exception $e) {
    error_log(get_class($e)." thrown. Message: ".$e->getMessage(). "  in " . $e->getFile() . " on line ".$e->getLine());
    error_log('Exception trace stack: ' . print_r($e->getTrace(),1));
}

J'apprécierais vraiment tous les conseils à ce sujet!

EDIT: PHP est exécuté en tant que module Apache (API du serveur: gestionnaire Apache 2.0). Je ne pense pas qu'il y ait des accélérateurs PHP en cours d'utilisation, mais il se pourrait simplement que je ne sache pas comment le savoir. Aucun de ceux énumérés sur Wikipedia sont dans phpinfo ().

Autant que je sache, le MPM est préfork. C'est la première fois que j'ai examiné le MPM:

# ./httpd -l
Compiled in modules:
  core.c
  prefork.c
  http_core.c
  mod_so.c
32
groovenectar

Le problème

En bref, vous faites une exception quelque part, vous ne savez pas où et jusqu’à présent, vous ne pouviez pas reproduire l’erreur: cela ne se produit que pour certaines personnes, mais pas pour vous. Vous savez que cela se produit pour d'autres personnes, car vous le voyez dans les journaux d'erreurs.

Reproduire le problème

Puisque vous avez déjà éliminé les raisons courantes, vous devrez reproduire l'erreur. Si vous savez quel paramètre causera l'erreur, il devrait être facile de localiser l'erreur.

  • Très probablement, cela suffit si vous connaissez tous les paramètres POST/GET.
  • Si vous ne pouvez pas reproduire uniquement ces éléments, vous devez connaître les en-têtes de requête supplémentaires. Tels que agent utilisateur, accept-encoding, ...
  • Si vous ne pouvez toujours pas reproduire, alors cela devient très difficile: l'erreur peut dépendre d'un état (une session), de l'heure actuelle, de l'adresse IP source ou similaire.

La méthode du journal personnalisé

Commençons simplement: pour obtenir tous les paramètres, vous pouvez écrire au tout début du fichier php concerné, par exemple:

file_put_contents("/path/to/some/custom_error_log", date()."\n".print_r(get_defined_vars(), true), FILE_APPEND | LOCK_EX);

N'oubliez pas que le fichier custom_error_log doit être accessible en écriture pour votre application php. Ensuite, lorsque l'erreur se produit dans le journal des erreurs, recherchez les lignes correspondantes dans votre fichier custom_error_log. Espérons qu’il n’y ait pas beaucoup de requêtes à la seconde afin que vous puissiez toujours identifier la requête. Certains paramètres supplémentaires dans le journal des erreurs, tels que l'adresse IP source, peuvent vous aider à identifier la demande (si votre journal des erreurs l'indique) . À partir de ces données, reconstruisez une demande avec les mêmes paramètres POST/GET.

La méthode tcpdump

L’option suivante, très simple également, mais qui nécessite un accès root sur votre ordinateur cible, consiste à installer tcpflow. Ensuite, créez un dossier, cd dans ce dossier et exécutez simplement (en tant que root) tcpflow "port 80". L'option (port 80) est une expression de filtre pcap. Pour voir tout ce que vous pouvez faire avec cela, voir man pcap-filter. Il y a beaucoup de choses que ces expressions de filtre peuvent faire.

Maintenant, tcpflow va enregistrer toutes les connexions TCP sur le port 80, reconstruire l’échange complet de données en combinant les paquets appartenant à une connexion et vider ces données dans un fichier, créant deux nouveaux fichiers par connexion, un pour les données entrantes et un pour les données sortantes. Recherchez maintenant les fichiers pour une connexion ayant provoqué une erreur, toujours en vous basant sur l'horodatage de votre journal des erreurs et sur le dernier horodatage modifié des fichiers. Ensuite, vous obtenez les en-têtes complets de la requête http. Vous pouvez maintenant reconstruire complètement la requête HTTP, y compris en définissant le même encodage d'acceptation, le même agent d'utilisateur, etc. Vous pouvez même diriger la requête directement dans netcat, en réexécutant la requête exacte. Attention cependant, certains arguments, comme un identifiant de session, pourraient vous gêner. Si php découvre qu'une session a expiré, vous pouvez simplement obtenir une redirection vers un identifiant ou quelque chose d'inattendu. Vous devrez peut-être échanger des choses comme l'identifiant de session.

Se moquer de plus de choses

Si cela ne vous aide pas et que vous ne pouvez pas reproduire l'erreur sur votre ordinateur, vous pouvez essayer de simuler tout ce qui est difficile à simuler. Par exemple l'adresse IP source. Cela peut rendre certaines cascades nécessaires, mais c'est possible: Vous pouvez vous connecter à votre serveur en utilisant ssh avec l'option "-w", créant ainsi une interface de tunnel. Attribuez ensuite l'adresse IP incriminée à votre propre ordinateur et définissez les règles de routage (route add Host) pour utiliser le tunnel pour l'adresse IP spécifique. Si vous pouvez câbler directement les deux ordinateurs, vous pouvez même le faire sans tunnel.

Ne pas oublier de se moquer de la session qui devrait être la plus facile. Vous pouvez lire toutes les variables de session à l'aide de la méthode print_r (get_defined_vars ()). Ensuite, vous devez créer une session avec exactement les mêmes variables.

Demander à l'utilisateur

Une autre option serait de demander à l'utilisateur ce qu'il faisait. Peut-être que vous pouvez suivre les mêmes étapes que lui et peut reproduire.Si rien de tout cela ne vous aide.

Si rien de tout cela ne vous aide… eh bien… Alors cela devient sérieusement difficile. La chose IP est déjà très improbable. Ce pourrait être une bibliothèque GEO-IP qui cause l'erreur sur les IP d'une région spécifique, mais ce sont toutes des choses plutôt improbables. Si rien de ce qui précède ne vous a aidé à reproduire le problème, vous n'avez probablement pas trouvé la demande correcte dans toutes les données générées par le fichier custom_log_file-call/tcpflow. Essayez d'augmenter vos chances en obtenant un horodatage plus précis. Vous pouvez utiliser microtime () dans php en remplacement de date (). Vérifiez votre serveur Web, si vous pouvez obtenir quelque chose de plus précis que quelques secondes dans votre journal des erreurs. Écrivez votre propre implémentation de "tail", qui vous donne un horodatage plus précis, ... Réduisez la charge du système, de sorte que vous n'ayez pas à choisir autant de données (essayez une autre heure de la journée, charge d'utilisateurs vers différents serveurs, ...)

Entourez le problème une fois que vous pouvez reproduire

maintenant, une fois que vous pouvez reproduire, il devrait être une promenade dans le parc pour trouver la cause réelle. Vous pouvez rechercher le paramètre qui cause l'erreur par essais et erreurs ou en le comparant à d'autres requêtes ayant provoqué une erreur, en recherchant également des similitudes. Et ensuite, vous pouvez voir ce que fait ce paramètre, les bibliothèques qui y ont accès, etc. Vous pouvez désactiver chaque composant un par un utilisant ce paramètre jusqu'à ce que vous ne puissiez plus reproduire. Ensuite, vous avez votre composant et pouvez plonger plus profondément dans le problème.

Dites-nous ce que vous avez trouvé. Je suis curieux ;-).

Tell us what you found. I am curious ;-).

18
yankee

J'ai eu une telle erreur aussi. J'ai découvert que j'avais retourné un objet SQL dans ma classe de session (utilisée par le gestionnaire de session) au lieu de ne rien renvoyer ou du moins pas l'objet SQL. Commencez par examiner vos méthodes _write et _read, si vous retournez également des éléments incorrects.

Avis: ... Inconnu sur la ligne 0 - Comment trouver la bonne ligne, ce n'est pas "la ligne 0"

4
djot

Au lieu d'encapsuler le code dans un bloc try/catch, que se passe-t-il lorsque vous enregistrez un gestionnaire d'exceptions? Il est clair que votre bloc try/catch n'intercepte pas l'exception, ce qui entraîne les erreurs consignées dans Apache. En enregistrant un gestionnaire, vous pouvez être sûr que toute exception non capturée est gérée.

De même, si vous utilisez des espaces de nom dans votre application, veillez à écrire\Exception dans votre bloc catch (ou à inclure la classe Exception via une instruction use).

3
Peter Kruithof

Pour nous, cette erreur était due à la sérialisation par inadvertance d'objets SimpleXML. 

Si vous utilisez des objets SimpleXML avec 5.3.3, assurez-vous de convertir les valeurs de nœud selon vos besoins (chaîne, par exemple) si vous sérialisez les valeurs dans la session.

Avant :

  $token = $response->Token->Value;
  /* token saved in session, results in line 0 error */

Après :

$token = (string) $response->Token->Value;
  /* token saved in session, no error */
2
David

Je me rends compte que cette question a déjà été posée, mais j'ajouterai ceci car cela peut aider quelqu'un:

J'ai réussi à produire (involontairement) des erreurs sans un cadre de pile à partir d'une fonction qui utilisait son propre gestionnaire d'erreurs pour garder le contrôle de l'exécution tout en appelant une fonction potentiellement "dangereuse", comme ceci:

// Assume the function my_error_handler() has been defined to convert any
// PHP Errors, Warnings, or Notices into Exceptions.

function foo() {
    // maintain control if danger() crashes outright:
    set_error_handler('my_error_handler');

    try {
        // Do some stuff.

        $r = danger();
    } catch (Exception $e) {
        $r = 'Bad Stuff, Man!';
    }

    restore error_handler();
    return $r;
}

L '"échec introuvable" se produirait à la fin de l'exécution du programme si la logique de "Do some stuff" est renvoyée directement par foo (), en ignorant l'appel de restore_error_handler (). Voici ce que j'ai retenu de l'expérience:

  1. PHP maintient un stack de gestionnaires d'erreur qui devient plus profond/plus grand à chaque appel de set_error_handler ().
  2. De mauvaises choses peuvent se produire si vous placez les gestionnaires d’erreur sur la pile et ne nettoyez pas après vous-même avant que le programme ne se termine "normalement".

C'était un problème difficile à isoler - j'ai essentiellement réduit le problème à la fonction ci-dessus, puis je l'ai observé jusqu'à ce que mes yeux saignent.

Alors, comment aurais-je pu retrouver cette information, sachant ce que je sais maintenant? Comme je ne connais aucun moyen d'inspecter directement le "gestionnaire" d'erreurs PHP, je pense qu'il serait peut-être judicieux d'utiliser un objet Singleton pour encapsuler toutes les opérations set/restore pour PHP. gestionnaires d'erreur. Au moins, il serait alors possible d'inspecter l'état du Singleton avant de quitter le programme normalement, et si des gestionnaires d'erreur "pendants" sont détectés pour générer un message d'erreur/avertissement sensible avant que PHP ne s'effraie.

2
Peter

C'est peut-être un peu tard mais un problème que j'ai découvert lors du déplacement d'un site d'un serveur local vers un serveur distant. J'utilisais Concrete5 cms avait développé mon site localement (Windows 8 dans xampp), puis téléchargé sur un serveur distant exécutant Cent 0S.

Windows mysql par défaut est insensible à la casse et a créé une base de données en minuscules. Une fois que cela a été téléchargé sur le serveur distant, j'ai reçu le message "Exception levée sans trame de pile dans Unknown sur la ligne 0?"

J'ai ensuite corrigé le cas des tables de base de données et mon site a recommencé à fonctionner.

2
Sean

J'ai eu complètement la même erreur. Un cas très particulier: si vous connectez un hook de fonction non nommé (fermeture) à un point de hook d'instances d'objet. Après cela, vous essayez de sérialiser cet objet. 

0
Akos

Ce problème s'est produit pour moi lorsque j'ai modifié l'espace de noms de plusieurs bundles Symfony. La suppression des fichiers du répertoire de cache de symfony a résolu le problème.

0
ssjcory

Vous avez probablement une table corrompue/incohérente dans la base de données. Essayez de vider la base de données. Si vous obtenez une erreur, c'est le moment. Réparez cette table et le problème devrait disparaître.

C'est pour cette raison que l'installation propre fonctionne. L'installation propre est juste que propre.

mysqlcheck devrait fonctionner mais s'il ne s'affiche pas et que le problème persiste, comme indiqué ci-dessus.

0
Roland Booth

un moyen simple de générer cette erreur est d'utiliser un ancien serveur avec register_globals = On. alors vous n'avez besoin que de deux lignes de code:

<?php
    $_SESSION["my_var"] = "string";
    $my_var = new MyClass(); //could be any class, i guess
?>

dès que vous rechargez cette page une fois, vous obtenez l'erreur Exception thrown without a stack frame in Unknown on line 0 -. semble être un conflit entre l’instance de la classe et la variable (session).
du moins c’est comme ça que j’ai eu cette erreur ennuyeuse qui est si difficile à déboguer.

0
low_rents

J'ai eu la même erreur, il est apparu mettre à niveau le serveur de centos 5 à centos 6 et déclasser PHP de 5.4 à 5.3. Le problème réel était PHP apc, non configuré correctement. Vérifiez votre APC. J'utilisais Symfony2, vous pouvez donc trouver de l'aide sur Symfony Impossible d'allouer de la mémoire pour le pool

0
cargan