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.