Ukratko: PHP prijavljuje probleme na dva načina: kao klasične pogreške poput upozorenja i napomena, kojima se upravlja putem error_reporting, te kao iznimke koje se mogu uhvatiti s try i catch. Od PHP-a 7 i mnoge interne pogreške bacaju objekt klase Error; zajedno s Exception implementira sučelje Throwable. U produkciji vrijedi: pogreške zapisivati, nikad ih ne prikazivati u pregledniku. Na PHP-u 8.5 zbog PHP2Shella među prijelazne mjere spada i fatal_error_backtraces=0.
Dva svijeta: klasične pogreške i iznimke
PHP potječe iz vremena u kojem su se pogreške ponajprije ispisivale kao poruke: napomene (E_NOTICE), upozorenja (E_WARNING), zastarjele funkcije (E_DEPRECATED) i fatalne pogreške (E_ERROR). Mnoge od tih poruka dopuštaju da skripta nastavi raditi, često s pogrešnim ili praznim podacima.
Iznimke rade drukčije. Prekidaju uobičajeni tijek i skaču na sljedeći odgovarajući catch blok. Ako ga nema, skripta završava. Za donositelje odluka razlika je važna: tiho upozorenje može dovesti do toga da se narudžba spremi nepotpuno, a da to nitko ne primijeti. Iznimka problem čini vidljivim i obradivim.
Podcijenjena korist klasičnih poruka su napomene tipa E_DEPRECATED. One najavljuju da će jezično sredstvo otpasti u budućoj verziji PHP-a. Tko ih skuplja u zapisniku i redovito analizira, prije nadogradnje zna koja mjesta treba prilagoditi i kod hitne sigurnosne ranjivosti ne mora dugo testirati.
Hijerarhija Throwable
Sve što se može baciti s throw i uhvatiti s catch implementira Throwable. Ispod njega postoje dvije grane:
| Klasa | Svrha | Primjeri potklasa |
|---|---|---|
Exception |
pogreške koje se mogu očekivati u aplikaciji i ekstenzijama | ErrorException, PDOException, vlastite klase |
Error |
interne pogreške PHP-a | TypeError, ValueError, ArgumentCountError, ArithmeticError, DivisionByZeroError, UnhandledMatchError |
Vlastite klase pogrešaka nasljeđuju Exception, a ne Error. Od PHP-a 8.0 većina internih funkcija pri nevaljanim parametrima baca TypeError ili ValueError, umjesto da samo ispiše upozorenje. strlen([]) je jedan takav slučaj.
try, catch i finally u praksi
<?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) {
// od PHP-a 8.0 varijabla u catchu nije obavezna
$log->error('Zahlungsdienst nicht erreichbar', ['bestellung' => $b->id()]);
return false;
} finally {
$dienst->verbindungSchliessen();
}
}
Više se klasa može objediniti znakom | u jednom catchu. Blok finally izvršava se uvijek, bez obzira je li došlo do pogreške. Od PHP-a 8.0 throw je uz to izraz i može stajati, primjerice, iza ??: $id = $_GET['id'] ?? throw new InvalidArgumentException('id fehlt');.
Globalni handleri: set_error_handler i set_exception_handler
Dvije funkcije hvataju ono što bi inače promaknulo. set_exception_handler() registrira funkciju za neuhvaćene Throwable objekte. Prema priručniku skripta završava nakon što se ona pozove; pogodna je dakle za zapisivanje i neutralnu stranicu s pogreškom, a ne za nastavak rada.
Dobro se pokazao handler koji potpunu pogrešku s vremenom i slučajnom oznakom upisuje u zapisnik, a posjetitelju prikazuje samo neutralnu stranicu s tom oznakom. Podrška tada slučaj može pronaći u zapisniku, a da interni detalji ne procure prema van.
set_error_handler() hvata klasične pogreške. Priručnik pokazuje kako ih pretvoriti u ErrorException kako bi se upozorenja obrađivala kao iznimke:
<?php
set_error_handler(function (int $nr, string $text, string $datei, int $zeile): bool {
if (!(error_reporting() & $nr)) {
return false; // isključeno putem error_reporting
}
if ($nr === E_DEPRECATED || $nr === E_USER_DEPRECATED) {
return false; // napomene o zastarjelom ne eskalirati
}
throw new ErrorException($text, 0, $nr, $datei, $zeile);
});
Fatalne pogreške poput prekoračenja memorije ovim se putem ne mogu uhvatiti. Što se pri prekoračenju događa iznutra, objašnjava stranica memory_limit i Out-of-Memory.
Konfiguriranje ispisa pogrešaka i zapisivanja
Najvažnije postavke sa zadanim vrijednostima prema priručniku:
| Postavka | Zadano | Preporuka za produkciju |
|---|---|---|
error_reporting |
E_ALL od PHP-a 8.0.0 |
E_ALL |
display_errors |
1 |
Off |
display_startup_errors |
1 od PHP-a 8.0.0 |
Off |
log_errors |
0 |
On |
error_log |
prazno (zapisnik web-poslužitelja odnosno SAPI-ja) | vlastita datoteka izvan webrootea |
fatal_error_backtraces |
1, novo u PHP-u 8.5.0 |
privremeno 0 (PHP2Shell) |
Od PHP-a 8.2.0 error_log_mode određuje prava datoteke zapisnika, prema zadanim postavkama 0644. Ako zapisnici sadrže osobne podatke ili putanje, smislena je stroža vrijednost kako ih drugi korisnici na poslužitelju ne bi mogli čitati. S ignore_repeated_errors mogu se sažeti identične poruke iz iste datoteke i retka, kako petlja pogrešaka ne bi preplavila zapisnik.
Isporučeni php.ini-production već postavlja display_errors na Off. Prema priručniku ini_set('display_errors', ...) tijekom izvođenja ne djeluje na fatalne pogreške, a sintaksne pogreške nastaju prije nego što se ini_set() uopće izvrši. Te vrijednosti zato pripadaju u konfiguraciju poslužitelja. Pozadinu donosi članak Isključivanje ispisa pogrešaka u produkciji; kako voditi zapisnike bez curenja podataka, opisuje članak Sigurno zapisivanje.
Obrada pogrešaka i PHP2Shell
Za PHP 8.5 php.net kao jednu od prijelaznih mjera protiv PHP2Shella navodi postavljanje fatal_error_backtraces=0 i potom ponovno pokretanje PHP-a. Ostale mjere tiču se memory_limit, post_max_size i session.upload_progress.enabled. Nijedna od njih ne zamjenjuje ažuriranje na PHP 8.5.12, 8.4.27, 8.3.36 ili 8.2.35.
; samo PHP 8.5, u php.ini ili FPM poolu, zatim ponovno pokrenuti sve workere
fatal_error_backtraces = 0
Zapisnici pogrešaka nakon PHP2Shella uz to su važan dokaz. Ako se od 20. 10. 2026. nagomilaju pogreške memorije ili prekinuti workeri, to može upućivati na pokušaje napada, ali može imati i bezazlene uzroke. Sačuvajte zapisnike i analizirajte ih zajedno sa zapisnicima pristupa, kako je opisano u vodiču Analiza zapisnika web-poslužitelja nakon napada.
Kako PHP SOS konkretno pomaže?
- Provjeravamo djelotvorne postavke za ispis pogrešaka i zapisivanje po web-stranici i podešavamo ih za produkciju.
- Postavljamo prijelazne mjere PHP2Shella u skladu s verzijom PHP-a i ponovno pokrećemo sve workere.
- Osiguravamo zapisnike pogrešaka i pristupa od 20. 10. 2026. i analiziramo ih zbog neobičnosti.
- U okviru sigurnosnog nadzora prijavljujemo neobična nagomilavanja pogrešaka, umjesto da ih pustimo da se izgube u zapisniku.
Česta pitanja
Koja je razlika između Error i Exception?
Oba implementiraju sučelje Throwable. Exception je namijenjen pogreškama koje aplikacija očekuje i obrađuje, primjerice neuspjelom spajanju na bazu podataka. Error označava interne pogreške PHP-a poput TypeErrora ili dijeljenja s nulom.
Treba li display_errors u produkciji biti uključen?
Ne. Poruke o pogreškama u pregledniku otkrivaju putanje, upite, a ponekad i pristupne podatke. U produkciji display_errors treba biti Off, a log_errors On, kako bi pogreške završavale u zapisniku, a ne kod posjetitelja.
Može li se prekoračenje memorije uhvatiti s try/catch?
Ne. Ako skripta prekorači memory_limit, PHP je prekida fatalnom pogreškom koja se ne može uhvatiti kao iznimka. Pojavljuje se samo u zapisniku pogrešaka, uz uvjet da je log_errors aktivan.
Što je fatal_error_backtraces?
Postavka koja postoji od PHP-a 8.5.0 i fatalnim pogreškama dodaje backtrace. Prema zadanim je postavkama aktivna. Kao prijelaznu mjeru protiv PHP2Shella php.net za PHP 8.5 navodi da je treba postaviti na 0 i zatim ponovno pokrenuti PHP.