# Erreurs et exceptions en PHP

> Erreurs et exceptions en PHP : Throwable, Error et Exception, try/catch/finally, gestionnaires globaux et réglages sûrs pour l'affichage des erreurs et les logs.

Source : https://phpsos.de/fr/php/fehler-und-exceptions/  
État : 25/10/2026  
Fournisseur : PHP SOS, Assistance d'urgence PHP et sécurité des serveurs. Aide immédiate par WhatsApp : https://wa.me/491736494914?text=Bonjour%20PHP%20SOS%2C%20nous%20avons%20besoin%20d%27aide%20avec%20notre%20serveur%20PHP.%20Entreprise%20%3A%20%E2%80%A6%20Site%20web%20%3A%20%E2%80%A6

**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
<?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
<?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](https://phpsos.de/fr/php/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](https://phpsos.de/fr/blog/display-errors-produktion/) ; la tenue de logs sans fuite de données, dans l'article [Journaliser en toute sécurité](https://phpsos.de/fr/blog/sicher-loggen-php/).

## 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.

```ini
; 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](https://phpsos.de/fr/ratgeber/logfiles-auswerten/).

## 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é](https://phpsos.de/fr/leistungen/security-monitoring/), 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

- [Manuel PHP : configuration de la gestion des erreurs](https://www.php.net/manual/de/errorfunc.configuration.php)
- [Manuel PHP : classe Error](https://www.php.net/manual/de/class.error.php)
- [Manuel PHP : ErrorException](https://www.php.net/manual/en/class.errorexception.php)
- [Manuel PHP : set_exception_handler](https://www.php.net/manual/en/function.set-exception-handler.php)
- [php.net : avis de sécurité](https://www.php.net/)
