Gestione degli errori in PHP quando si utilizza MVC


12

Ho usato Codeigniter molto di recente, ma una cosa che mi fa innervosire è gestire gli errori e mostrarli all'utente. Non sono mai stato bravo a gestire gli errori senza diventare disordinato. La mia preoccupazione principale è quando si restituiscono errori all'utente.

È buona norma utilizzare le eccezioni e generare / intercettare le eccezioni anziché restituire 0 o 1 dalle funzioni e quindi utilizzare if / else per gestire gli errori. Pertanto, rendendo più semplice informare l'utente del problema.

Tendo ad andare via dalle eccezioni. Il mio tutor Java all'università qualche anno fa mi ha detto che "le eccezioni non dovrebbero essere utilizzate nel codice di produzione, ma piuttosto per il debug". Ho la sensazione che stesse mentendo.

Ma, ad esempio, ho un codice che aggiunge un utente a un database. Durante il processo più di 1 cosa potrebbe andare storta, ad esempio un problema di database, una voce duplicata, un problema del server, ecc. Quando si verifica un problema durante la registrazione, l'utente deve conoscerlo.

Qual è il modo migliore per gestire gli errori in PHP, tenendo presente che sto usando un framework MVC.

Risposte:


14

È buona norma utilizzare le eccezioni e generare / intercettare le eccezioni anziché restituire 0 o 1 dalle funzioni e quindi utilizzare if / else per gestire gli errori. Pertanto, rendendo più semplice informare l'utente del problema.

No, no, no!

Non mescolare eccezioni ed errori. Le eccezioni sono, beh, eccezionali. Gli errori no. Quando chiedi a un utente di inserire una quantità di un prodotto e l'utente inserisce "ciao", si tratta di un errore. Non fa eccezione: non c'è niente di eccezionale nel vedere un input non valido da parte dell'utente. Perché non è possibile utilizzare le eccezioni in casi non eccezionali, come quando si convalida l'input? Altre persone lo hanno già spiegato e hanno mostrato una valida alternativa per la convalida dell'input.

Ciò significa anche che l'utente non si preoccupa delle tue eccezioni e mostrare le eccezioni è sia ostile che pericoloso . Ad esempio, un'eccezione durante l'esecuzione di una query SQL rivela spesso la query stessa. Sei sicuro di voler correre il rischio di mostrare questo messaggio a tutti?

più di 1 cosa potrebbe andare storta, ad esempio un problema di database, una voce duplicata, un problema del server, ecc. Quando si verifica un problema durante la registrazione, l'utente deve conoscerlo.

Sbagliato. Come utente, non ho bisogno di conoscere i tuoi problemi di database, voci duplicate, ecc. Non mi interessa davvero i tuoi problemi. Quello che faccio c'è da sapere è che ho inserito un nome utente già esistenti. Come già detto, un mio input errato deve innescare un errore, non un'eccezione.

Come produrre quegli errori? Dipende dal contesto. Per un nome utente già utilizzato, vorrei vedere una piccola bandiera rossa che appare accanto al nome utente, prima ancora di inviare il modulo, dicendo che il nome utente è già utilizzato. Senza JavaScript, lo stesso flag deve apparire dopo l'invio.

Un esempio di errore abilitato per AJAX

Per altri errori, visualizzerai un'intera pagina con un errore o scegli un altro modo per informare l'utente che qualcosa è andato storto (ad esempio un messaggio che apparirà, quindi svanire nella parte superiore della pagina). La domanda è quindi legata più all'esperienza dell'utente che alla programmazione.

Dal punto di vista dei programmatori, a seconda del tipo di errore, lo propagherai in diversi modi. Ad esempio, nel caso di un nome utente già utilizzato, una richiesta AJAX http://example.com/?ajax=1&user-exists=Johnrestituirà un oggetto JSON che indica:

  • Che l'utente esiste già,
  • Il messaggio di errore da mostrare all'utente.

Il secondo punto è importante: vuoi essere sicuro che lo stesso messaggio compaia sia quando invii il modulo con JavaScript disabilitato sia quando digiti un nome utente duplicato con JavaScript abilitato. Non vuoi duplicare il testo del messaggio di errore nel codice sorgente sul lato server e in JavaScript!

Questa è in realtà la tecnica utilizzata dai siti Web Stack Exhange. Ad esempio, se provo a votare la mia risposta, la risposta AJAX contiene l'errore da visualizzare:

{"Success":false,"Warning":false,"NewScore":0,"Message":"You can't vote for your own post.",
"Refresh":false}

Puoi anche scegliere un altro approccio e preimpostare gli errori nella pagina HTML prima che il modulo sia compilato. Pro: non è necessario inviare il messaggio di errore nella risposta AJAX. Contro: che dire dell'accessibilità? Prova a sfogliare la pagina senza CSS e vedrai apparire tutti i possibili errori.


Apprezzo la risposta. Questo è esattamente ciò con cui sto lottando. Hai delle risorse per segnalare errori, soprattutto in termini di esperienza utente?
James Jeffery,

Bene, come ho detto, dipende in realtà dall'errore e la segnalazione di errori all'utente è fortemente legata all'interfaccia utente. Ho anche messo in evidenza due modi principali per segnalare gli errori: stretta integrazione (una bandiera rossa abilitata AJAX vicino all'ingresso con un valore errato) ed errori a pagina intera, molto meno amichevoli, usati per casi più gravi. Questo non risponde alla tua domanda?
Arseni Mourzenko,

2
+1 perché è più un problema di esperienza utente e non tecnico
Charles Sprayberry

2
Cazzate, MainMa. Solo cazzate. I codici di errore sono quindi anni '80 e '90. Le eccezioni sono un modo molto più chiaro di gestire circostanze speciali come input errati (ad esempio ValidationException). Non sei obbligato a visualizzare tutte le eccezioni per l'utente. Ho visto le tue risposte migliori.
Falcon,

2
E nel caso in cui non lo sapessi: puoi controllare quali eccezioni vuoi presentare all'utente e quali non vuoi presentare. Quindi non è proprio un argomento.
Falcon,

13

È buona norma utilizzare le eccezioni e generare / intercettare le eccezioni anziché restituire 0 o 1 dalle funzioni e quindi utilizzare if / else per gestire gli errori. Pertanto, rendendo più semplice informare l'utente del problema.

Sì sì sì!

Se vuoi avere un codice pulito, dovresti usare quasi esclusivamente le eccezioni e non preoccuparti di usare i codici di errore. I codici di errore non hanno significato. Sono quasi sempre legati a qualche costante numerica che non svela molte informazioni. Può rendere illeggibile il tuo codice e renderanno difficile la propagazione dei dati insieme all'errore.

Le eccezioni, tuttavia, sono classi e possono contenere qualsiasi informazione che ti piace. Quindi l'utente ha inserito un input errato, come 'abc' per un campo numerico. Con un codice di errore non saresti in grado di propagare queste informazioni al gestore dell'errore senza molto gorgoglio. Qualcosa che le eccezioni forniscono gratuitamente. Inoltre, le eccezioni consentono di avere valori di ritorno significativi in ​​funzioni e metodi pur avendo un modo di fallire elegantemente. Ancora meglio, le eccezioni si propagano direttamente nel luogo in cui si desidera gestirle! Immagina la quantità di codice spaghetti di cui avrai bisogno per propagare un codice di errore con dati significativi a un gestore uno o due livelli sopra.

Inoltre, le eccezioni esprimono molto più semanticamente rispetto ai codici di errore. I codici di errore portano al codice spaghetti dove la gestione delle eccezioni porta al codice pulito.

Inoltre, è facile dimenticare di controllare i codici di stato. In lingue come Java sei costretto a gestire le eccezioni (cosa che ad esempio manca a C #).

Qual è il modo migliore per gestire gli errori in PHP, tenendo presente che sto usando un framework MVC.

Usa le eccezioni e gestiscile nei tuoi controller.


Sono molto d'accordo con te !! Nonostante le eccezioni non siano applicate in PHP come in altre lingue, è bene sapere che molte persone le stanno confermando ...
David Conde,

6
Concordo sul fatto che nella maggior parte dei casi i codici di errore sono abbastanza insignificanti. Tuttavia, lanciare eccezioni volenti o nolenti è estremamente negativo! Le eccezioni dovrebbero essere riservate solo a circostanze eccezionali. Le eccezioni causano un flusso di programma imprevedibile, possono rendere il codice difficile da seguire (e quindi mantenere) e in PHP comportano una penalità piuttosto significativa rispetto a IF / THEN / ELSE. Tendo a preferire metodi per restituire vero in caso di successo, falso in caso di fallimento, e solo un'eccezione di qualcosa va egregiamente in errore.
GordonM,

6

Considera questa pratica piccola classe:

class FunkyFile {               

    private $path;
    private $contents = null;

    public function __construct($path) { 
        $this->setPath($path); 
    }

    private function setPath($path) {
        if( !is_file($path) || !is_readable($path) ) 
            throw new \InvalidArgumentException("Hm, that's not a valid file!");

        $this->path = realpath($path);
        return $this; 
    }

    public function getContents() {
        if( is_null($this->contents) ) {
            $this->contents = @file_get_contents( $this->path );
            if($this->contents === false) 
                throw new \Exception("Hm, I can't read the file, for some reason!");                                 
        }

        return $this->contents;            
    }

}

Questo è un uso perfettamente perfetto delle eccezioni. Dal FunkyFile'spunto di vista non c'è assolutamente nulla che possa essere fatto per porre rimedio alla situtazione se il percorso non è valido o file_get_contentsfallisce. Una situazione davvero eccezionale;)

Ma c'è qualche valore per il tuo utente sapere che ti sei imbattuto in un percorso di file errato, da qualche parte nel tuo codice? Per esempio:

class Welcome extends Controller {

    public function index() {

        /**
         * Ah, let's show user this file she asked for
         */                 
        try {
            $file = new File("HelloWorld.txt");
            $contents = $file->getContents();   
            echo $contents;
        } catch(\Exception $e) {
            log($e->getMessage());

            echo "Sorry, I'm having a bad day!"; 
        }                           
    }        
}

Oltre a dire alle persone che stai passando una brutta giornata, le tue opzioni sono:

  1. Ricaderci

    Hai un altro modo per ottenere le informazioni? Nel mio semplice esempio sopra, non sembra probabile, ma considera uno schema di database master / slave. Il master potrebbe non aver risposto, ma forse, forse, lo slave è ancora là fuori (o viceversa).

  2. È colpa dell'utente?

    L'utente ha inviato input errati? Beh, dille a questo proposito. Puoi abbaiare un messaggio di errore o essere gentile e accompagnare quel messaggio di errore con un modulo in modo che possa digitare il percorso corretto.

  3. È colpa tua?

    E per te intendo tutto ciò che non è l'utente, quindi va da te digitando un percorso di file errato a qualcosa che non va nel tuo server. A rigor di termini, è tempo di un errore HTTP 503 , poiché anche il servizio non è disponibile. CI ha una show_404()funzione che puoi facilmente creare show_503().

Un consiglio: dovresti prendere in considerazione le eccezioni canaglia. CodeIgniter è un pezzo di codice disordinato e non si sa mai quando apparirà un'eccezione. Allo stesso modo, potresti dimenticare le tue eccezioni e l'opzione più sicura è implementare un gestore di tutte le eccezioni catch. In PHP puoi farlo con set_exception_handler :

function FunkyExceptionHandler($exception) {
    if(ENVIRONMENT == "production") {
        log($e->getMessage());
        show_503();
    } else {
        echo "Uncaught exception: " , $exception->getMessage(), "\n";
    }   
}

set_exception_handler("FunkyExceptionHandler");

E puoi anche occuparti degli errori canaglia, tramite set_error_handler . Puoi scrivere lo stesso gestore come per le eccezioni oppure in alternativa convertire tutti gli errori ErrorExceptione lasciare che il gestore delle eccezioni li gestisca:

function FunkyErrorHandler($errno, $errstr, $errfile, $errline) {
    // will be caught by FunkyExceptionHandler if not handled
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}

set_error_handler("FunkyErrorHandler");

Questo è stato davvero istruttivo, evviva!
James,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.