# Fejl og exceptions i PHP

> Fejl og exceptions i PHP: Throwable, Error og Exception, try/catch/finally, globale handlere og sikre indstillinger for fejlvisning og logs.

Kilde: https://phpsos.de/da/php/fehler-und-exceptions/  
Status: 25.10.2026  
Udbyder: PHP SOS, PHP-nødhjælp og serversikkerhed. Akut hjælp på WhatsApp: https://wa.me/491736494914?text=Hej%20PHP%20SOS%2C%20vi%20har%20brug%20for%20hj%C3%A6lp%20med%20vores%20PHP-server.%20Firma%3A%20%E2%80%A6%20Website%3A%20%E2%80%A6

**Kort sagt:** PHP melder problemer på to måder: som klassiske fejl som advarsler og notitser, der styres via `error_reporting`, og som exceptions, der kan fanges med `try` og `catch`. Siden PHP 7 kaster også mange interne fejl et objekt af klassen `Error`; sammen med `Exception` implementerer det interfacet `Throwable`. I produktion gælder: Log fejl, vis dem aldrig i browseren. På PHP 8.5 hører på grund af PHP2Shell desuden `fatal_error_backtraces=0` til de midlertidige foranstaltninger.

## To verdener: klassiske fejl og exceptions

PHP stammer fra en tid, hvor fejl for det meste blev udsendt som meddelelse: notitser (`E_NOTICE`), advarsler (`E_WARNING`), forældede funktioner (`E_DEPRECATED`) og fatale fejl (`E_ERROR`). Mange af disse meddelelser lader scriptet køre videre, ofte med forkerte eller tomme data.

Exceptions fungerer anderledes. De afbryder det normale forløb og springer til den næste passende `catch`-blok. Findes ingen, slutter scriptet. For beslutningstagere er forskellen vigtig: En lydløs advarsel kan medføre, at en ordre gemmes ufuldstændigt, uden at nogen opdager det. En exception gør problemet synligt og håndterbart.

En undervurderet nytte ved de klassiske meddelelser er notitser af typen `E_DEPRECATED`. De varsler, at et sprogmiddel bortfalder i en kommende PHP-version. Den, der samler dem i loggen og regelmæssigt gennemgår dem, ved før en opgradering, hvilke steder der skal tilpasses, og behøver ikke at teste længe, når en akut sårbarhed opstår.

## Throwable-hierarkiet

Alt, der kan kastes med `throw` og fanges med `catch`, implementerer `Throwable`. Derunder er der to grene:

| Klasse | Formål | Eksempler på underklasser |
|---|---|---|
| `Exception` | forventelige fejl i applikationen og i udvidelser | `ErrorException`, `PDOException`, egne klasser |
| `Error` | PHP's interne fejl | `TypeError`, `ValueError`, `ArgumentCountError`, `ArithmeticError`, `DivisionByZeroError`, `UnhandledMatchError` |

Egne fejlklasser arves fra `Exception`, ikke fra `Error`. Siden PHP 8.0 kaster de fleste interne funktioner ved ugyldige parametre en `TypeError` eller `ValueError` i stedet for blot at udsende en advarsel. `strlen([])` er et sådant tilfælde.

## try, catch og finally i praksis

```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) {
        // siden PHP 8.0 er variablen i catch valgfri
        $log->error('Zahlungsdienst nicht erreichbar', ['bestellung' => $b->id()]);
        return false;
    } finally {
        $dienst->verbindungSchliessen();
    }
}
```

Flere klasser kan samles i ét `catch` med `|`. `finally`-blokken kører altid, uanset om en fejl er opstået eller ej. Siden PHP 8.0 er `throw` desuden et udtryk og kan for eksempel stå efter `??`: `$id = $_GET['id'] ?? throw new InvalidArgumentException('id fehlt');`.

## Globale handlere: set_error_handler og set_exception_handler

To funktioner fanger det, der ellers ville slippe igennem. `set_exception_handler()` registrerer en funktion til ikke-fangede Throwables. Ifølge manualen slutter scriptet, efter at den er kaldt; den egner sig altså til logning og en neutral fejlside, ikke til at arbejde videre.

Det har vist sig godt med en handler, der skriver den fulde fejl med tidspunkt og et tilfældigt id i loggen og kun viser den besøgende en neutral side med dette id. Supporten kan så finde sagen igen i loggen, uden at interne detaljer slipper ud.

`set_error_handler()` fanger klassiske fejl. Manualen viser, hvordan man omdanner dem til en `ErrorException`, så advarsler behandles som exceptions:

```php
<?php
set_error_handler(function (int $nr, string $text, string $datei, int $zeile): bool {
    if (!(error_reporting() & $nr)) {
        return false; // udelukket af error_reporting
    }
    if ($nr === E_DEPRECATED || $nr === E_USER_DEPRECATED) {
        return false; // eskalér ikke henvisninger til forældet kode
    }
    throw new ErrorException($text, 0, $nr, $datei, $zeile);
});
```

Fatale fejl som et hukommelsesoverløb kan ikke fanges på denne måde. Hvad der sker indeni ved et overløb, forklarer siden [memory_limit og out-of-memory](https://phpsos.de/da/php/out-of-memory/).

## Konfigurér fejlvisning og logning

De vigtigste indstillinger med deres standardværdier ifølge manualen:

| Indstilling | Standard | Anbefaling produktion |
|---|---|---|
| `error_reporting` | `E_ALL` siden PHP 8.0.0 | `E_ALL` |
| `display_errors` | `1` | `Off` |
| `display_startup_errors` | `1` siden PHP 8.0.0 | `Off` |
| `log_errors` | `0` | `On` |
| `error_log` | tom (logfil hos webserveren hhv. SAPI'en) | egen fil uden for webroot |
| `fatal_error_backtraces` | `1`, ny i PHP 8.5.0 | midlertidigt `0` (PHP2Shell) |

Siden PHP 8.2.0 fastlægger `error_log_mode` filrettighederne for logfilen, som standard `0644`. Indeholder logs personoplysninger eller stier, er en strengere værdi fornuftig, så andre brugere på serveren ikke kan læse dem. Med `ignore_repeated_errors` kan identiske meddelelser fra samme fil og linje slås sammen, så en fejlløkke ikke oversvømmer loggen.

Den medfølgende `php.ini-production` sætter allerede `display_errors` til `Off`. Ifølge manualen virker `ini_set('display_errors', ...)` under kørslen ikke på fatale fejl, og syntaksfejl opstår, før et `ini_set()` overhovedet kører. Disse værdier hører derfor hjemme i serverkonfigurationen. Baggrund findes i indlægget [Slå fejlvisning fra i produktion](https://phpsos.de/da/blog/display-errors-produktion/); hvordan logs føres uden datalæk, står i indlægget [Log sikkert](https://phpsos.de/da/blog/sicher-loggen-php/).

## Fejlhåndtering og PHP2Shell

For PHP 8.5 nævner php.net som en af de midlertidige foranstaltninger mod PHP2Shell at sætte `fatal_error_backtraces=0` og derefter genstarte PHP. De øvrige foranstaltninger angår `memory_limit`, `post_max_size` og `session.upload_progress.enabled`. Ingen af dem erstatter opdateringen til PHP 8.5.12, 8.4.27, 8.3.36 eller 8.2.35.

```ini
; kun PHP 8.5, i php.ini eller FPM-pool, genstart derefter alle workers
fatal_error_backtraces = 0
```

Fejllogs er desuden efter PHP2Shell et vigtigt bevismiddel. Hober hukommelsesfejl eller afbrudte workers sig op fra 20.10.2026, kan det pege på angrebsforsøg, men det kan også have harmløse årsager. Gem logs og vurdér dem sammen med adgangslogs, som beskrevet i guiden [Gennemgå webserverlogs efter et angreb](https://phpsos.de/da/ratgeber/logfiles-auswerten/).

## Hvordan hjælper PHP SOS konkret?

- Vi kontrollerer de gældende indstillinger for fejlvisning og logning pr. website og indstiller dem, så de egner sig til produktion.
- Vi sætter PHP2Shell-foranstaltningerne i overensstemmelse med PHP-versionen og genstarter alle workers.
- Vi sikrer fejl- og adgangslogs fra 20.10.2026 og gennemgår dem for uregelmæssigheder.
- Som led i [sikkerhedsovervågningen](https://phpsos.de/da/leistungen/security-monitoring/) melder vi usædvanlige ophobninger af fejl i stedet for at lade dem forsvinde i loggen.


## Ofte stillede spørgsmål

**Hvad er forskellen på Error og Exception?**  
Begge implementerer interfacet Throwable. Exception er tænkt til fejl, som en applikation forventer og håndterer, f.eks. en mislykket databaseforbindelse. Error står for PHP's interne fejl som en TypeError eller en division med nul.

**Bør display_errors være slået til i produktion?**  
Nej. Fejlmeddelelser i browseren afslører stier, forespørgsler og nogle gange adgangsdata. I produktion hører display_errors på Off og log_errors på On, så fejl havner i loggen i stedet for hos den besøgende.

**Kan man fange et hukommelsesoverløb med try/catch?**  
Nej. Overskrider et script memory_limit, afslutter PHP det med en fatal fejl, som ikke kan fanges som exception. Den vises kun i fejlloggen, hvis log_errors er aktiv.

**Hvad er fatal_error_backtraces?**  
En indstilling, som findes siden PHP 8.5.0, og som føjer et backtrace til fatale fejl. Den er som standard aktiv. Som midlertidig foranstaltning mod PHP2Shell nævner php.net for PHP 8.5 at sætte den til 0 og derefter genstarte PHP.

## Kilder

- [PHP-manualen: Konfiguration af fejlhåndtering](https://www.php.net/manual/de/errorfunc.configuration.php)
- [PHP-manualen: Klassen Error](https://www.php.net/manual/de/class.error.php)
- [PHP-manualen: ErrorException](https://www.php.net/manual/en/class.errorexception.php)
- [PHP-manualen: set_exception_handler](https://www.php.net/manual/en/function.set-exception-handler.php)
- [php.net: Sikkerhedsmeddelelser](https://www.php.net/)
