Kort sagt: PHP rapporterar problem på två sätt: som klassiska fel som varningar och notiser, vilka styrs via error_reporting, och som exceptions, vilka kan fångas med try och catch. Sedan PHP 7 kastar många interna fel också ett objekt av klassen Error; tillsammans med Exception implementerar det gränssnittet Throwable. I produktion gäller: Logga fel, visa dem aldrig i webbläsaren. På PHP 8.5 hör på grund av PHP2Shell dessutom fatal_error_backtraces=0 till de tillfälliga åtgärderna.
Två världar: klassiska fel och exceptions
PHP kommer från en tid då fel framför allt skrevs ut som meddelanden: notiser (E_NOTICE), varningar (E_WARNING), föråldrade funktioner (E_DEPRECATED) och fatala fel (E_ERROR). Många av de här meddelandena låter skriptet fortsätta köra, ofta med felaktiga eller tomma data.
Exceptions fungerar annorlunda. De avbryter det normala förloppet och hoppar till nästa passande catch-block. Hittas inget tar skriptet slut. För beslutsfattare är skillnaden viktig: En tyst varning kan leda till att en beställning sparas ofullständigt utan att någon märker det. En exception gör problemet synligt och hanterbart.
En underskattad nytta med de klassiska meddelandena är notiser av typen E_DEPRECATED. De förvarnar om att ett språkmedel försvinner i en kommande PHP-version. Den som samlar dem i loggen och regelbundet går igenom dem vet före en uppgradering vilka ställen som måste anpassas och behöver inte testa länge när en akut säkerhetslucka dyker upp.
Throwable-hierarkin
Allt som kan kastas med throw och fångas med catch implementerar Throwable. Under det finns två grenar:
| Klass | Syfte | Exempel på underklasser |
|---|---|---|
Exception |
förväntbara fel i applikationen och i tillägg | ErrorException, PDOException, egna klasser |
Error |
interna fel i PHP | TypeError, ValueError, ArgumentCountError, ArithmeticError, DivisionByZeroError, UnhandledMatchError |
Egna felklasser ärver från Exception, inte från Error. Sedan PHP 8.0 kastar de flesta interna funktioner ett TypeError eller ValueError vid ogiltiga parametrar, i stället för att bara skriva ut en varning. strlen([]) är ett sådant fall.
try, catch och finally i praktiken
<?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) {
// sedan PHP 8.0 är variabeln i catch valfri
$log->error('Zahlungsdienst nicht erreichbar', ['bestellung' => $b->id()]);
return false;
} finally {
$dienst->verbindungSchliessen();
}
}
Flera klasser kan slås ihop i en catch med |. Blocket finally körs alltid, oavsett om ett fel har uppstått eller inte. Sedan PHP 8.0 är throw dessutom ett uttryck och kan till exempel stå efter ??: $id = $_GET['id'] ?? throw new InvalidArgumentException('id fehlt');.
Globala hanterare: set_error_handler och set_exception_handler
Två funktioner fångar upp det som annars skulle slinka igenom. set_exception_handler() registrerar en funktion för Throwables som inte har fångats. Enligt manualen tar skriptet slut sedan den har anropats; den lämpar sig alltså för loggning och en neutral felsida, inte för att arbeta vidare.
En hanterare som skriver hela felet med tidpunkt och ett slumpmässigt id till loggen och bara visar besökaren en neutral sida med det idt har fungerat väl. Supporten kan då hitta fallet i loggen utan att interna detaljer läcker ut.
set_error_handler() fångar klassiska fel. Manualen visar hur de omvandlas till en ErrorException, så att varningar hanteras som exceptions:
<?php
set_error_handler(function (int $nr, string $text, string $datei, int $zeile): bool {
if (!(error_reporting() & $nr)) {
return false; // utesluten av error_reporting
}
if ($nr === E_DEPRECATED || $nr === E_USER_DEPRECATED) {
return false; // eskalera inte hänvisningar till föråldrat
}
throw new ErrorException($text, 0, $nr, $datei, $zeile);
});
Fatala fel som ett minnesöverskridande går inte att fånga på det här sättet. Vad som händer inuti vid ett överskridande förklarar sidan memory_limit och Out-of-Memory.
Konfigurera felutskrift och loggning
De viktigaste inställningarna med sina standardvärden enligt manualen:
| Inställning | Standard | Rekommendation produktion |
|---|---|---|
error_reporting |
E_ALL sedan PHP 8.0.0 |
E_ALL |
display_errors |
1 |
Off |
display_startup_errors |
1 sedan PHP 8.0.0 |
Off |
log_errors |
0 |
On |
error_log |
tom (webbserverns respektive SAPI:ns logg) | egen fil utanför webbroten |
fatal_error_backtraces |
1, nytt i PHP 8.5.0 |
tillfälligt 0 (PHP2Shell) |
Sedan PHP 8.2.0 anger error_log_mode loggfilens filrättigheter, som standard 0644. Innehåller loggarna personuppgifter eller sökvägar är ett striktare värde lämpligt, så att andra användare på servern inte kan läsa dem. Med ignore_repeated_errors kan identiska meddelanden från samma fil och rad slås ihop, så att en felloop inte översvämmar loggen.
Den medföljande php.ini-production sätter redan display_errors till Off. Enligt manualen verkar ini_set('display_errors', ...) vid körning inte på fatala fel, och syntaxfel uppstår innan något ini_set() ens körs. De här värdena hör därför hemma i serverkonfigurationen. Bakgrund finns i inlägget Stäng av felutskrifter i produktion; hur loggar förs utan dataläckor beskrivs i inlägget Logga säkert.
Felhantering och PHP2Shell
För PHP 8.5 nämner php.net som en av de tillfälliga åtgärderna mot PHP2Shell att sätta fatal_error_backtraces=0 och starta om PHP därefter. De övriga åtgärderna gäller memory_limit, post_max_size och session.upload_progress.enabled. Ingen av dem ersätter uppdateringen till PHP 8.5.12, 8.4.27, 8.3.36 eller 8.2.35.
; endast PHP 8.5, i php.ini eller FPM-poolen, starta sedan om alla workers
fatal_error_backtraces = 0
Felloggar är efter PHP2Shell dessutom ett viktigt bevismaterial. Ökar minnesfel eller avbrutna workers från och med den 20 oktober 2026 kan det tyda på attackförsök, men det kan också ha ofarliga orsaker. Spara loggarna och analysera dem tillsammans med åtkomstloggarna, som beskrivs i guiden Analysera webbserverloggar efter en attack.
Hur hjälper PHP SOS konkret?
- Vi kontrollerar de gällande inställningarna för felutskrift och loggning per webbplats och ställer in dem för produktion.
- Vi sätter PHP2Shell-åtgärderna så att de passar PHP-versionen och startar om alla workers.
- Vi säkrar fel- och åtkomstloggar från och med den 20 oktober 2026 och analyserar dem efter avvikelser.
- Inom ramen för säkerhetsövervakningen rapporterar vi ovanliga felansamlingar i stället för att låta dem försvinna i loggen.
Vanliga frågor
Vad är skillnaden mellan Error och Exception?
Båda implementerar gränssnittet Throwable. Exception är avsedd för fel som en applikation förväntar sig och hanterar, till exempel en misslyckad databasanslutning. Error står för interna fel i PHP, som ett TypeError eller division med noll.
Ska display_errors vara på i produktion?
Nej. Felmeddelanden i webbläsaren avslöjar sökvägar, frågor och ibland inloggningsuppgifter. I produktion ska display_errors vara Off och log_errors On, så att fel hamnar i loggen i stället för hos besökaren.
Går det att fånga ett minnesöverskridande med try/catch?
Nej. Överskrider ett skript memory_limit avslutar PHP det med ett fatalt fel som inte går att fånga som exception. Det syns bara i felloggen, förutsatt att log_errors är aktivt.
Vad är fatal_error_backtraces?
En inställning som finns sedan PHP 8.5.0 och som lägger till en backtrace vid fatala fel. Den är aktiv som standard. Som tillfällig åtgärd mot PHP2Shell nämner php.net för PHP 8.5 att sätta den till 0 och starta om PHP därefter.