Situation actuelle : PHP2Shell. Deux failles PHP critiques (CVSS 9.2) permettent d'exécuter du code malveillant sans authentification. Correctifs disponibles depuis le 21 octobre 2026.Ce qu'il faut faire maintenant →

Erreurs et exceptions en PHP

PHP connaît les messages d'erreur classiques et les exceptions. Cette page explique la hiérarchie Throwable, une gestion propre des erreurs, des réglages sûrs en production et la mesure PHP2Shell sur fatal_error_backtraces.

État 6 min de lectureSavoir PHP

En bref : PHP signale les problèmes de deux façons : sous forme d'erreurs classiques, comme les avertissements et les notices, pilotées par error_reporting, et sous forme d'exceptions, qui peuvent être interceptées avec try et catch. Depuis PHP 7, de nombreuses erreurs internes lèvent elles aussi un objet de la classe Error ; avec Exception, elle implémente l'interface Throwable. En production, la règle est : consigner les erreurs, ne jamais les afficher dans le navigateur. Sur PHP 8.5, fatal_error_backtraces=0 fait en outre partie des mesures transitoires contre PHP2Shell.

Deux mondes : erreurs classiques et exceptions

PHP date d'une époque où les erreurs étaient avant tout affichées sous forme de message : notices (E_NOTICE), avertissements (E_WARNING), fonctions obsolètes (E_DEPRECATED) et erreurs fatales (E_ERROR). Beaucoup de ces messages laissent le script continuer, souvent avec des données erronées ou vides.

Les exceptions fonctionnent autrement. Elles interrompent le déroulement normal et sautent au prochain bloc catch approprié. Si aucun n'est trouvé, le script s'arrête. Pour les décideurs, la différence est importante : un avertissement silencieux peut conduire à l'enregistrement incomplet d'une commande sans que personne ne s'en aperçoive. Une exception rend le problème visible et traitable.

Un avantage sous-estimé des messages classiques est celui des notices de type E_DEPRECATED. Elles annoncent qu'une fonctionnalité du langage disparaîtra dans une future version de PHP. Qui les collecte dans le log et les exploite régulièrement sait, avant une mise à niveau, quels endroits doivent être adaptés, et n'a pas besoin de longs tests en cas de faille de sécurité urgente.

La hiérarchie Throwable

Tout ce qui peut être lancé avec throw et attrapé avec catch implémente Throwable. En dessous, il y a deux branches :

Classe Rôle Exemples de sous-classes
Exception erreurs prévisibles de l'application et des extensions ErrorException, PDOException, classes personnalisées
Error erreurs internes de PHP TypeError, ValueError, ArgumentCountError, ArithmeticError, DivisionByZeroError, UnhandledMatchError

Les classes d'erreur personnalisées dérivent de Exception, non de Error. Depuis PHP 8.0, la plupart des fonctions internes lèvent une TypeError ou une ValueError en cas de paramètres invalides, au lieu de se contenter d'émettre un avertissement. strlen([]) en est un exemple.

try, catch et finally en pratique

<?php
declare(strict_types=1);

final class ZahlungFehlgeschlagen extends RuntimeException {}

function bezahlen(Bestellung $b, Zahlungsdienst $dienst, LoggerInterface $log): bool
{
    try {
        $dienst->belasten($b->betrag(), $b->referenz());
        return true;
    } catch (ZahlungFehlgeschlagen $e) {
        $log->warning('Zahlung abgelehnt', ['bestellung' => $b->id(), 'exception' => $e]);
        return false;
    } catch (TimeoutException | VerbindungsFehler) {
        // depuis PHP 8.0, la variable est facultative dans le catch
        $log->error('Zahlungsdienst nicht erreichbar', ['bestellung' => $b->id()]);
        return false;
    } finally {
        $dienst->verbindungSchliessen();
    }
}

Plusieurs classes peuvent être regroupées avec | dans un seul catch. Le bloc finally s'exécute toujours, qu'une erreur soit survenue ou non. Depuis PHP 8.0, throw est en outre une expression et peut par exemple figurer derrière ?? : $id = $_GET['id'] ?? throw new InvalidArgumentException('id fehlt');.

Gestionnaires globaux : set_error_handler et set_exception_handler

Deux fonctions interceptent ce qui passerait sinon entre les mailles. set_exception_handler() enregistre une fonction pour les Throwable non interceptés. Selon le manuel, le script se termine après son appel ; elle convient donc à la journalisation et à une page d'erreur neutre, pas à la poursuite du traitement.

Un gestionnaire qui écrit l'erreur complète dans le log, avec l'horodatage et un identifiant aléatoire, et ne montre au visiteur qu'une page neutre portant cet identifiant, a fait ses preuves. Le support peut alors retrouver le cas dans le log, sans que des détails internes ne filtrent vers l'extérieur.

set_error_handler() intercepte les erreurs classiques. Le manuel montre comment les convertir en ErrorException, afin que les avertissements soient traités comme des exceptions :

<?php
set_error_handler(function (int $nr, string $text, string $datei, int $zeile): bool {
    if (!(error_reporting() & $nr)) {
        return false; // exclue par error_reporting
    }
    if ($nr === E_DEPRECATED || $nr === E_USER_DEPRECATED) {
        return false; // ne pas faire escalader les avis d'obsolescence
    }
    throw new ErrorException($text, 0, $nr, $datei, $zeile);
});

Les erreurs fatales, comme un dépassement de mémoire, ne peuvent pas être interceptées par ce moyen. Ce qui se passe en interne lors d'un dépassement est expliqué sur la page memory_limit et Out-of-Memory.

Configurer l'affichage des erreurs et la journalisation

Les réglages les plus importants, avec leurs valeurs par défaut selon le manuel :

Réglage Valeur par défaut Recommandation en production
error_reporting E_ALL depuis PHP 8.0.0 E_ALL
display_errors 1 Off
display_startup_errors 1 depuis PHP 8.0.0 Off
log_errors 0 On
error_log vide (log du serveur web ou de la SAPI) fichier dédié en dehors de la racine web
fatal_error_backtraces 1, nouveau dans PHP 8.5.0 temporairement 0 (PHP2Shell)

Depuis PHP 8.2.0, error_log_mode définit les droits du fichier de log, par défaut 0644. Si les logs contiennent des données personnelles ou des chemins, une valeur plus stricte est judicieuse, afin que d'autres utilisateurs du serveur ne puissent pas les lire. Avec ignore_repeated_errors, les messages identiques provenant du même fichier et de la même ligne peuvent être regroupés, pour qu'une boucle d'erreurs n'inonde pas le log.

Le php.ini-production fourni met déjà display_errors sur Off. Selon le manuel, ini_set('display_errors', ...) n'agit pas à l'exécution sur les erreurs fatales, et les erreurs de syntaxe surviennent avant même qu'un ini_set() ne s'exécute. Ces valeurs relèvent donc de la configuration du serveur. Le contexte est détaillé dans l'article Désactiver l'affichage des erreurs en production ; la tenue de logs sans fuite de données, dans l'article Journaliser en toute sécurité.

Gestion des erreurs et PHP2Shell

Pour PHP 8.5, php.net cite comme l'une des mesures transitoires contre PHP2Shell de mettre fatal_error_backtraces=0, puis de redémarrer PHP. Les autres mesures concernent memory_limit, post_max_size et session.upload_progress.enabled. Aucune ne remplace la mise à jour vers PHP 8.5.12, 8.4.27, 8.3.36 ou 8.2.35.

; PHP 8.5 uniquement, dans php.ini ou le pool FPM, puis redémarrer tous les workers
fatal_error_backtraces = 0

Après PHP2Shell, les logs d'erreurs sont en outre un élément de preuve important. Si des erreurs de mémoire ou des workers interrompus se multiplient à partir du 20 octobre 2026, cela peut indiquer des tentatives d'attaque, mais il peut aussi y avoir des causes anodines. Conservez les logs et exploitez-les avec les logs d'accès, comme décrit dans le guide Analyser les logs du serveur web après une attaque.

Comment PHP SOS aide-t-il concrètement ?

  • Nous vérifions les réglages effectifs d'affichage des erreurs et de journalisation pour chaque site web, et les configurons pour la production.
  • Nous appliquons les mesures transitoires PHP2Shell adaptées à la version de PHP et redémarrons tous les workers.
  • Nous sauvegardons les logs d'erreurs et d'accès à partir du 20 octobre 2026 et les analysons à la recherche d'anomalies.
  • Dans le cadre de la surveillance de sécurité, nous signalons les accumulations inhabituelles d'erreurs, au lieu de les laisser se perdre dans le log.

Questions fréquentes

Quelle est la différence entre Error et Exception ?

Les deux implémentent l'interface Throwable. Exception est destinée aux erreurs qu'une application attend et traite, par exemple l'échec d'une connexion à la base de données. Error désigne les erreurs internes de PHP, comme une TypeError ou une division par zéro.

display_errors doit-il être activé en production ?

Non. Les messages d'erreur dans le navigateur révèlent des chemins, des requêtes et parfois des identifiants. En production, display_errors doit être sur Off et log_errors sur On, afin que les erreurs aboutissent dans le log plutôt que chez le visiteur.

Peut-on intercepter un dépassement de mémoire avec try/catch ?

Non. Lorsqu'un script dépasse memory_limit, PHP l'arrête par une erreur fatale qui ne peut pas être interceptée comme exception. Elle n'apparaît que dans le log des erreurs, pour autant que log_errors soit actif.

Qu'est-ce que fatal_error_backtraces ?

Un réglage disponible depuis PHP 8.5.0, qui ajoute une trace d'appels aux erreurs fatales. Il est actif par défaut. Comme mesure transitoire contre PHP2Shell, php.net indique pour PHP 8.5 de le mettre à 0, puis de redémarrer PHP.

Sources

Contact immédiat

Chaque heure compte. Écrivez-nous maintenant.

Décrivez brièvement votre système, votre version de PHP et ce que vous avez observé. Nous vous répondons, faisons le point et travaillons à distance jusqu'à la clôture et la documentation de l'incident.

Aide immédiate