# Napake in izjeme v PHP

> Napake in izjeme v PHP: Throwable, Error in Exception, try/catch/finally, globalni handlerji ter varne nastavitve za izpis napak in dnevnike.

Vir: https://phpsos.de/sl/php/fehler-und-exceptions/  
Stanje: 25.10.2026  
Ponudnik: PHP SOS, Nujna pomoč za PHP in varnost strežnikov. Takojšnja pomoč po WhatsAppu: https://wa.me/491736494914?text=Pozdravljeni%2C%20PHP%20SOS%2C%20potrebujemo%20pomo%C4%8D%20z%20na%C5%A1im%20stre%C5%BEnikom%20PHP.%20Podjetje%3A%20%E2%80%A6%20Spletna%20stran%3A%20%E2%80%A6

**Na kratko:** PHP težave sporoča na dva načina: kot klasične napake, na primer opozorila in namige, ki jih nadzira `error_reporting`, in kot izjeme, ki jih je mogoče ujeti z `try` in `catch`. Od PHP 7 tudi številne notranje napake sprožijo objekt razreda `Error`; skupaj z `Exception` implementira vmesnik `Throwable`. V produkciji velja: napake beležiti, jih nikoli ne prikazovati v brskalniku. Na PHP 8.5 med prehodne ukrepe zaradi PHP2Shell dodatno sodi `fatal_error_backtraces=0`.

## Dva sveta: klasične napake in izjeme

PHP izvira iz časa, ko so se napake izpisovale predvsem kot sporočila: namigi (`E_NOTICE`), opozorila (`E_WARNING`), zastarele funkcije (`E_DEPRECATED`) in smrtne napake (`E_ERROR`). Številna od teh sporočil skripti dovolijo, da teče naprej, pogosto z napačnimi ali praznimi podatki.

Izjeme delujejo drugače. Prekinejo običajni potek in skočijo na naslednji ustrezni blok `catch`. Če ga ni, se skripta konča. Za odločevalce je razlika pomembna: tiho opozorilo lahko povzroči, da se naročilo shrani nepopolno, ne da bi kdo opazil. Izjema težavo naredi vidno in obravnavno.

Premalo cenjena korist klasičnih sporočil so namigi tipa `E_DEPRECATED`. Napovedujejo, da bo neko jezikovno sredstvo v prihodnji različici PHP izpadlo. Kdor jih zbira v dnevniku in redno ovrednoti, pred nadgradnjo ve, katera mesta je treba prilagoditi, in pri nujni varnostni vrzeli ni treba šele dolgo testirati.

## Hierarhija Throwable

Vse, kar je mogoče vreči z `throw` in ujeti s `catch`, implementira `Throwable`. Pod njim sta dve veji:

| Razred | Namen | Primeri podrazredov |
|---|---|---|
| `Exception` | pričakovane napake aplikacije in razširitev | `ErrorException`, `PDOException`, lastni razredi |
| `Error` | notranje napake PHP | `TypeError`, `ValueError`, `ArgumentCountError`, `ArithmeticError`, `DivisionByZeroError`, `UnhandledMatchError` |

Lastni razredi napak izhajajo iz `Exception`, ne iz `Error`. Od PHP 8.0 večina notranjih funkcij pri neveljavnih parametrih sproži `TypeError` ali `ValueError`, namesto da bi samo izpisala opozorilo. `strlen([])` je en tak primer.

## try, catch in finally v praksi

```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) {
        // od PHP 8.0 je spremenljivka v bloku catch neobvezna
        $log->error('Zahlungsdienst nicht erreichbar', ['bestellung' => $b->id()]);
        return false;
    } finally {
        $dienst->verbindungSchliessen();
    }
}
```

Več razredov je mogoče z `|` združiti v enem bloku `catch`. Blok `finally` se izvede vedno, ne glede na to, ali je prišlo do napake. Od PHP 8.0 je `throw` tudi izraz in lahko stoji na primer za `??`: `$id = $_GET['id'] ?? throw new InvalidArgumentException('id fehlt');`.

## Globalni handlerji: set_error_handler in set_exception_handler

Dve funkciji ujameta to, kar bi sicer ušlo. `set_exception_handler()` registrira funkcijo za neujete Throwable. Po priročniku se skripta konča, ko je funkcija poklicana; primerna je torej za beleženje in nevtralno stran z napako, ne za nadaljnje delo.

Obnesel se je handler, ki popolno napako z uro in naključno oznako zapiše v dnevnik, obiskovalcu pa pokaže samo nevtralno stran s to oznako. Podpora lahko primer nato najde v dnevniku, ne da bi notranje podrobnosti prišle navzven.

`set_error_handler()` ujame klasične napake. Priročnik pokaže, kako jih pretvoriti v `ErrorException`, da se opozorila obravnavajo kot izjeme:

```php
<?php
set_error_handler(function (int $nr, string $text, string $datei, int $zeile): bool {
    if (!(error_reporting() & $nr)) {
        return false; // izključeno z error_reporting
    }
    if ($nr === E_DEPRECATED || $nr === E_USER_DEPRECATED) {
        return false; // namigov na zastarelo ne stopnjevati
    }
    throw new ErrorException($text, 0, $nr, $datei, $zeile);
});
```

Smrtnih napak, kot je prekoračitev pomnilnika, na ta način ni mogoče ujeti. Kaj se ob prekoračitvi dogaja v ozadju, razlaga stran [memory_limit in Out-of-Memory](https://phpsos.de/sl/php/out-of-memory/).

## Konfiguracija izpisa napak in beleženja

Najpomembnejše nastavitve s privzetimi vrednostmi po priročniku:

| Nastavitev | Privzeto | Priporočilo za produkcijo |
|---|---|---|
| `error_reporting` | `E_ALL` od PHP 8.0.0 | `E_ALL` |
| `display_errors` | `1` | `Off` |
| `display_startup_errors` | `1` od PHP 8.0.0 | `Off` |
| `log_errors` | `0` | `On` |
| `error_log` | prazno (dnevnik spletnega strežnika oz. SAPI) | lastna datoteka zunaj korena spleta (webroot) |
| `fatal_error_backtraces` | `1`, novo v PHP 8.5.0 | začasno `0` (PHP2Shell) |

Od PHP 8.2.0 `error_log_mode` določa pravice dostopa do datoteke dnevnika, privzeto `0644`. Če dnevniki vsebujejo osebne podatke ali poti, je smiselna strožja vrednost, da jih drugi uporabniki na strežniku ne morejo brati. Z `ignore_repeated_errors` je mogoče enaka sporočila iz iste datoteke in vrstice strniti, da zanka napak dnevnika ne preplavi.

Priložena `php.ini-production` `display_errors` že nastavi na `Off`. Po priročniku `ini_set('display_errors', ...)` med izvajanjem ne vpliva na smrtne napake, sintaksne napake pa nastopijo, še preden se `ini_set()` sploh izvede. Te vrednosti zato sodijo v konfiguracijo strežnika. Ozadje je v prispevku [Izklop izpisa napak v produkciji](https://phpsos.de/sl/blog/display-errors-produktion/); kako voditi dnevnike brez uhajanja podatkov, pa v prispevku [Varno beleženje](https://phpsos.de/sl/blog/sicher-loggen-php/).

## Obravnava napak in PHP2Shell

Za PHP 8.5 php.net kot enega od prehodnih ukrepov proti PHP2Shell navaja nastavitev `fatal_error_backtraces=0` in nato ponovni zagon PHP. Drugi ukrepi zadevajo `memory_limit`, `post_max_size` in `session.upload_progress.enabled`. Noben od njih ne nadomesti posodobitve na PHP 8.5.12, 8.4.27, 8.3.36 ali 8.2.35.

```ini
; samo PHP 8.5, v php.ini ali poolu FPM, nato znova zaženite vse workerje
fatal_error_backtraces = 0
```

Dnevniki napak so po PHP2Shell poleg tega pomembno dokazno gradivo. Če se od 20. 10. 2026 kopičijo napake pomnilnika ali prekinjeni workerji, to lahko kaže na poskuse napadov, lahko pa ima tudi neškodljive vzroke. Dnevnike shranite in jih ovrednotite skupaj z dnevniki dostopov, kot je opisano v vodniku [Analiza dnevnikov spletnega strežnika po napadu](https://phpsos.de/sl/ratgeber/logfiles-auswerten/).

## Kako PHP SOS pomaga konkretno?

- Preverimo veljavne nastavitve za izpis napak in beleženje pri vsaki spletni strani in jih nastavimo za produkcijo.
- Prehodne ukrepe PHP2Shell nastavimo glede na različico PHP in znova zaženemo vse workerje.
- Dnevnike napak in dostopov od 20. 10. 2026 zavarujemo in jih pregledamo glede nepravilnosti.
- V okviru [varnostnega nadzora](https://phpsos.de/sl/leistungen/security-monitoring/) javimo nenavadna kopičenja napak, namesto da bi se izgubila v dnevniku.


## Pogosta vprašanja

**Kakšna je razlika med Error in Exception?**  
Oba implementirata vmesnik Throwable. Exception je namenjen napakam, ki jih aplikacija pričakuje in obravnava, na primer neuspeli povezavi s podatkovno bazo. Error pomeni notranje napake PHP, kot sta TypeError ali deljenje z ničlo.

**Ali naj bo display_errors v produkciji vklopljen?**  
Ne. Sporočila o napakah v brskalniku izdajo poti, poizvedbe in včasih dostopne podatke. V produkciji mora biti display_errors nastavljen na Off, log_errors pa na On, da napake pristanejo v dnevniku in ne pri obiskovalcu.

**Ali je prekoračitev pomnilnika mogoče ujeti s try/catch?**  
Ne. Če skripta preseže memory_limit, jo PHP konča s smrtno napako, ki je ni mogoče ujeti kot izjemo. Pojavi se samo v dnevniku napak, če je log_errors aktiven.

**Kaj je fatal_error_backtraces?**  
Nastavitev, ki obstaja od PHP 8.5.0 in smrtnim napakam doda sled klicev (backtrace). Privzeto je aktivna. Kot prehodni ukrep proti PHP2Shell php.net za PHP 8.5 navaja, da jo nastavite na 0 in nato znova zaženete PHP.

## Viri

- [Priročnik PHP: konfiguracija obravnave napak](https://www.php.net/manual/de/errorfunc.configuration.php)
- [Priročnik PHP: razred Error](https://www.php.net/manual/de/class.error.php)
- [Priročnik PHP: ErrorException](https://www.php.net/manual/en/class.errorexception.php)
- [Priročnik PHP: set_exception_handler](https://www.php.net/manual/en/function.set-exception-handler.php)
- [php.net: varnostna obvestila](https://www.php.net/)
