Aktuel situation: PHP2Shell. To kritiske PHP-sårbarheder (CVSS 9,2) tillader skadelig kode uden login. Rettelser siden 21.10.2026.Hvad der skal gøres nu →

Fejl og exceptions i PHP

PHP kender klassiske fejlmeddelelser og exceptions. Siden forklarer Throwable-hierarkiet, ren fejlhåndtering, sikre indstillinger til produktion og PHP2Shell-foranstaltningen om fatal_error_backtraces.

Status 5 min. læsetidPHP-viden

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
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
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.

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; hvordan logs føres uden datalæk, står i indlægget Log sikkert.

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.

; 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.

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 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

Akut kontakt

Hver time tæller. Skriv til os nu.

Beskriv kort system, PHP-version og observation. Vi vender tilbage, afklarer situationen og arbejder på afstand, indtil hændelsen er afsluttet og dokumenteret.

Akut hjælp