Εν συντομία: Η PHP αναφέρει προβλήματα με δύο τρόπους: ως κλασικά σφάλματα, όπως προειδοποιήσεις και υποδείξεις, που ελέγχονται μέσω του error_reporting, και ως exceptions, που μπορούν να πιαστούν με try και catch. Από την PHP 7 πολλά εσωτερικά σφάλματα πετούν επίσης ένα αντικείμενο της κλάσης Error· μαζί με το Exception υλοποιεί τη διεπαφή Throwable. Στην παραγωγή ισχύει: καταγράφετε τα σφάλματα, δεν τα εμφανίζετε ποτέ στο πρόγραμμα περιήγησης. Στην PHP 8.5, λόγω PHP2Shell, το fatal_error_backtraces=0 ανήκει επιπλέον στα προσωρινά μέτρα.
Δύο κόσμοι: κλασικά σφάλματα και exceptions
Η PHP προέρχεται από μια εποχή όπου τα σφάλματα εμφανίζονταν κυρίως ως μήνυμα: υποδείξεις (E_NOTICE), προειδοποιήσεις (E_WARNING), παρωχημένες συναρτήσεις (E_DEPRECATED) και μοιραία σφάλματα (E_ERROR). Πολλά από αυτά τα μηνύματα αφήνουν το σενάριο να συνεχίσει, συχνά με λανθασμένα ή κενά δεδομένα.
Τα exceptions λειτουργούν διαφορετικά. Διακόπτουν την κανονική ροή και μεταπηδούν στο επόμενο κατάλληλο μπλοκ catch. Αν δεν βρεθεί κανένα, το σενάριο τελειώνει. Για όσους αποφασίζουν η διαφορά είναι σημαντική: μια σιωπηλή προειδοποίηση μπορεί να οδηγήσει στην ατελή αποθήκευση μιας παραγγελίας χωρίς να το αντιληφθεί κανείς. Ένα exception κάνει το πρόβλημα ορατό και διαχειρίσιμο.
Ένα υποτιμημένο όφελος των κλασικών μηνυμάτων είναι οι υποδείξεις τύπου E_DEPRECATED. Ανακοινώνουν ότι ένα γλωσσικό μέσο θα αφαιρεθεί σε μελλοντική έκδοση της PHP. Όποιος τις συλλέγει στο log και τις αξιολογεί τακτικά, γνωρίζει πριν από μια αναβάθμιση ποια σημεία πρέπει να προσαρμοστούν και δεν χρειάζεται να δοκιμάζει πολύ ώρα όταν προκύψει επείγουσα ευπάθεια ασφαλείας.
Η ιεραρχία Throwable
Ό,τι μπορεί να πεταχτεί με throw και να πιαστεί με catch υλοποιεί το Throwable. Από κάτω υπάρχουν δύο κλάδοι:
| Κλάση | Σκοπός | Παραδείγματα υποκλάσεων |
|---|---|---|
Exception |
αναμενόμενα σφάλματα της εφαρμογής και των επεκτάσεων | ErrorException, PDOException, δικές σας κλάσεις |
Error |
εσωτερικά σφάλματα της PHP | TypeError, ValueError, ArgumentCountError, ArithmeticError, DivisionByZeroError, UnhandledMatchError |
Οι δικές σας κλάσεις σφαλμάτων κληρονομούν από το Exception, όχι από το Error. Από την PHP 8.0 οι περισσότερες εσωτερικές συναρτήσεις πετούν σε άκυρες παραμέτρους ένα TypeError ή ValueError, αντί να εμφανίζουν μόνο μια προειδοποίηση. Το strlen([]) είναι μία τέτοια περίπτωση.
try, catch και finally στην πράξη
<?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) {
// από την PHP 8.0 η μεταβλητή στο catch είναι προαιρετική
$log->error('Zahlungsdienst nicht erreichbar', ['bestellung' => $b->id()]);
return false;
} finally {
$dienst->verbindungSchliessen();
}
}
Περισσότερες κλάσεις μπορούν να συγκεντρωθούν με | σε ένα catch. Το μπλοκ finally εκτελείται πάντα, είτε προέκυψε σφάλμα είτε όχι. Από την PHP 8.0 το throw είναι επιπλέον έκφραση και μπορεί να βρίσκεται, για παράδειγμα, μετά από ??: $id = $_GET['id'] ?? throw new InvalidArgumentException('id fehlt');.
Καθολικοί handlers: set_error_handler και set_exception_handler
Δύο συναρτήσεις πιάνουν ό,τι θα ξέφευγε. Η set_exception_handler() καταχωρίζει μια συνάρτηση για Throwables που δεν πιάστηκαν. Σύμφωνα με το εγχειρίδιο, το σενάριο τερματίζεται αφού κληθεί· ενδείκνυται επομένως για καταγραφή και μια ουδέτερη σελίδα σφάλματος, όχι για να συνεχιστεί η εργασία.
Αποδεδειγμένα καλή λύση είναι ένας handler που γράφει στο log ολόκληρο το σφάλμα με χρονική στιγμή και τυχαίο αναγνωριστικό, και δείχνει στον επισκέπτη μόνο μια ουδέτερη σελίδα με αυτό το αναγνωριστικό. Η υποστήριξη μπορεί τότε να βρει την περίπτωση στο log, χωρίς να διαρρεύσουν εσωτερικές λεπτομέρειες προς τα έξω.
Η set_error_handler() πιάνει κλασικά σφάλματα. Το εγχειρίδιο δείχνει πώς μετατρέπονται σε ErrorException, ώστε οι προειδοποιήσεις να αντιμετωπίζονται όπως τα exceptions:
<?php
set_error_handler(function (int $nr, string $text, string $datei, int $zeile): bool {
if (!(error_reporting() & $nr)) {
return false; // εξαιρείται από το error_reporting
}
if ($nr === E_DEPRECATED || $nr === E_USER_DEPRECATED) {
return false; // να μην κλιμακώνονται οι υποδείξεις για παρωχημένα
}
throw new ErrorException($text, 0, $nr, $datei, $zeile);
});
Μοιραία σφάλματα, όπως μια υπέρβαση μνήμης, δεν μπορούν να πιαστούν με αυτόν τον τρόπο. Τι συμβαίνει εσωτερικά σε μια υπέρβαση, εξηγεί η σελίδα memory_limit και Out-of-Memory.
Διαμόρφωση εμφάνισης σφαλμάτων και καταγραφής
Οι σημαντικότερες ρυθμίσεις με τις προεπιλεγμένες τιμές τους σύμφωνα με το εγχειρίδιο:
| Ρύθμιση | Προεπιλογή | Σύσταση για παραγωγή |
|---|---|---|
error_reporting |
E_ALL από την PHP 8.0.0 |
E_ALL |
display_errors |
1 |
Off |
display_startup_errors |
1 από την PHP 8.0.0 |
Off |
log_errors |
0 |
On |
error_log |
κενό (log του διακομιστή ιστού ή του SAPI) | δικό σας αρχείο εκτός του webroot |
fatal_error_backtraces |
1, νέο στην PHP 8.5.0 |
προσωρινά 0 (PHP2Shell) |
Από την PHP 8.2.0 το error_log_mode ορίζει τα δικαιώματα του αρχείου log, από προεπιλογή 0644. Αν τα logs περιέχουν προσωπικά δεδομένα ή διαδρομές, μια αυστηρότερη τιμή έχει νόημα, ώστε άλλοι χρήστες στον διακομιστή να μην μπορούν να τα διαβάσουν. Με το ignore_repeated_errors μπορούν να συγκεντρωθούν πανομοιότυπα μηνύματα από το ίδιο αρχείο και την ίδια γραμμή, ώστε ένας βρόχος σφαλμάτων να μην πλημμυρίσει το log.
Το συνοδευτικό php.ini-production ορίζει ήδη το display_errors σε Off. Σύμφωνα με το εγχειρίδιο, το ini_set('display_errors', ...) δεν επηρεάζει κατά την εκτέλεση τα μοιραία σφάλματα, ενώ τα συντακτικά σφάλματα εμφανίζονται πριν εκτελεστεί οποιοδήποτε ini_set(). Γι' αυτό αυτές οι τιμές ανήκουν στη διαμόρφωση του διακομιστή. Το υπόβαθρο υπάρχει στο άρθρο Απενεργοποίηση της εμφάνισης σφαλμάτων στην παραγωγή· πώς τηρούνται logs χωρίς διαρροή δεδομένων, στο άρθρο Ασφαλής καταγραφή.
Χειρισμός σφαλμάτων και PHP2Shell
Για την PHP 8.5 το php.net αναφέρει ως ένα από τα προσωρινά μέτρα κατά του PHP2Shell τον ορισμό του fatal_error_backtraces=0 και την επανεκκίνηση της PHP στη συνέχεια. Τα υπόλοιπα μέτρα αφορούν τα memory_limit, post_max_size και session.upload_progress.enabled. Κανένα από αυτά δεν αντικαθιστά την ενημέρωση στην PHP 8.5.12, 8.4.27, 8.3.36 ή 8.2.35.
; μόνο PHP 8.5, στο php.ini ή στο pool του FPM, στη συνέχεια επανεκκίνηση όλων των workers
fatal_error_backtraces = 0
Τα logs σφαλμάτων αποτελούν μετά το PHP2Shell και σημαντικό αποδεικτικό στοιχείο. Αν από τις 20.10.2026 πληθαίνουν τα σφάλματα μνήμης ή οι διακοπτόμενοι workers, αυτό μπορεί να υποδηλώνει απόπειρες επίθεσης, μπορεί όμως να έχει και αθώες αιτίες. Φυλάξτε τα logs και αξιολογήστε τα μαζί με τα logs πρόσβασης, όπως περιγράφεται στον οδηγό Αξιολόγηση των logs του διακομιστή ιστού μετά από επίθεση.
Πώς βοηθά συγκεκριμένα η PHP SOS;
- Ελέγχουμε τις ενεργές ρυθμίσεις για την εμφάνιση σφαλμάτων και την καταγραφή ανά ιστότοπο και τις προσαρμόζουμε για την παραγωγή.
- Ορίζουμε τα προσωρινά μέτρα PHP2Shell ανάλογα με την έκδοση PHP και επανεκκινούμε όλους τους workers.
- Διασφαλίζουμε τα logs σφαλμάτων και πρόσβασης από τις 20.10.2026 και τα αξιολογούμε για ανωμαλίες.
- Στο πλαίσιο της παρακολούθησης ασφαλείας αναφέρουμε ασυνήθιστες συγκεντρώσεις σφαλμάτων, αντί να αφήνουμε να χάνονται στο log.
Συχνές ερωτήσεις
Ποια είναι η διαφορά μεταξύ Error και Exception;
Και τα δύο υλοποιούν τη διεπαφή Throwable. Το Exception προορίζεται για σφάλματα που μια εφαρμογή αναμένει και χειρίζεται, όπως μια αποτυχημένη σύνδεση με βάση δεδομένων. Το Error αντιπροσωπεύει εσωτερικά σφάλματα της PHP, όπως ένα TypeError ή μια διαίρεση με το μηδέν.
Πρέπει το display_errors να είναι ενεργό στην παραγωγή;
Όχι. Τα μηνύματα σφάλματος στο πρόγραμμα περιήγησης αποκαλύπτουν διαδρομές, ερωτήματα και μερικές φορές διαπιστευτήρια. Στην παραγωγή το display_errors πρέπει να είναι Off και το log_errors On, ώστε τα σφάλματα να καταλήγουν στο log αντί στον επισκέπτη.
Μπορεί μια υπέρβαση μνήμης να πιαστεί με try/catch;
Όχι. Αν ένα σενάριο υπερβεί το memory_limit, η PHP το τερματίζει με μοιραίο σφάλμα που δεν μπορεί να πιαστεί ως exception. Εμφανίζεται μόνο στο log σφαλμάτων, εφόσον το log_errors είναι ενεργό.
Τι είναι το fatal_error_backtraces;
Μια ρύθμιση που υπάρχει από την PHP 8.5.0 και προσθέτει backtrace στα μοιραία σφάλματα. Είναι ενεργή από προεπιλογή. Ως προσωρινό μέτρο κατά του PHP2Shell, το php.net προτείνει για την PHP 8.5 να οριστεί σε 0 και να γίνει στη συνέχεια επανεκκίνηση της PHP.