Gestione degli errori in PHP quando si utilizza MVC

12

Ho usato Codeigniter molto recentemente, ma una cosa che mi dà fastidio è gestire gli errori e mostrarli all'utente. Non sono mai stato bravo a gestire gli errori senza che si sia complicato. La mia preoccupazione principale è quando si restituiscono errori all'utente.

È buona norma utilizzare eccezioni e generare / catturare eccezioni piuttosto che restituire 0 o 1 dalle funzioni e quindi utilizzare if / else per gestire gli errori. In questo modo, è più semplice informare l'utente del problema.

Tendo ad allontanarmi dalle eccezioni. Alcuni anni fa il mio tutor Java all'università mi ha detto che "le eccezioni non dovrebbero essere usate nel codice di produzione, ma è più 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, come un problema di database, una voce duplicata, un problema del server, ecc. Quando si verifica un problema durante la registrazione, l'utente deve saperlo.

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

    
posta James Jeffery 04.02.2012 - 21:38
fonte

3 risposte

13

Is it good practice to use exceptions and throw/catch exceptions rather than returning 0 or 1 from functions and then using if/else to handle the errors. Thus, making it easier to inform the user about the issue.

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 sono privi di significato. Sono quasi sempre legati a delle costanti numeriche che non rivelano molte informazioni. Può rendere il tuo codice illeggibile e renderà difficile propagare i dati accanto all'errore.

Le eccezioni, tuttavia, sono classi e possono contenere tutte le informazioni che ti piacciono. Quindi l'utente ha inserito un input sbagliato, come 'abc' per un campo numerico. Con un codice di errore non sarebbe possibile diffondere queste informazioni al gestore dell'errore senza un sacco di bolle. Qualcosa che le eccezioni forniscono gratuitamente. Inoltre, le eccezioni consentono di avere valori di ritorno significativi in funzioni e metodi pur avendo un modo per fallire elegantemente. Ancora meglio, le eccezioni vengono propagate 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 uno o due livelli di gestore sopra.

Inoltre, le eccezioni esprimono molto più semanticamente dei codici di errore. I codici di errore portano al codice spaghetti in cui la gestione delle eccezioni porta al codice pulito.

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

What's the best way to handle errors in PHP, keeping in mind that I'm using an MVC framework.

Utilizza le eccezioni e gestiscile nei tuoi controller.

    
risposta data 04.02.2012 - 23:20
fonte
13

Is it good practice to use exceptions and throw/catch exceptions rather than returning 0 or 1 from functions and then using if/else to handle the errors. Thus, making it easier to inform the user about the issue.

No, no, no!

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

Questo significa anche che l'utente non si preoccupa delle 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 rischiare di mostrare questo messaggio a tutti?

more than 1 thing could go wrong, such as a database issue, a duplicate entry, a server issue, etc. When an issue happens during registration the user needs to know about it.

sbagliato. Come utente, non ho bisogno di conoscere i tuoi problemi di database, le voci duplicate, ecc. Non mi preoccupo veramente dei tuoi problemi. Cosa io faccio è necessario sapere che ho inserito un nome utente che esiste già. Come già detto, un input sbagliato da parte mia deve attivare un errore, non un'eccezione.

Come generare quegli errori? Dipende dal contesto. Per un nome utente già utilizzato, mi piacerebbe vedere una piccola bandiera rossa che appare vicino 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.

Peraltrierrori,visualizzeraiunapaginainteraconunerroreoscegliunaltromodoperinformarel'utentechequalcosaèandatostorto(adesempiounmessaggiocheapparirà,quindisvanirànellapartesuperioredellapagina).Ladomandaèquindicorrelatapiùa esperienza utente che alla programmazione.

Dal punto di vista dei programmatori, a seconda del tipo di errore, lo si propagherà in modi diversi. Ad esempio, in caso di un nome utente già utilizzato, una richiesta AJAX a http://example.com/?ajax=1&user-exists=John restituirà 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 venga visualizzato 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 lato server e in JavaScript!

Questa è in realtà la tecnica utilizzata dai siti Web di Stack Exhange. Ad esempio, se provo ad aggiornare 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 riempito. 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 gli errori possibili.

    
risposta data 04.02.2012 - 22:11
fonte
6

Considera questa comoda 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 perfetto delle eccezioni. Dalla prospettiva FunkyFile's non c'è assolutamente nulla che possa essere fatto per porre rimedio alla situtation se il percorso non è valido o file_get_contents fallisce. Una situazione davvero eccezionale;)

Ma c'è qualche valore per il tuo utente per sapere che sei incappato in un percorso di file sbagliato, da qualche parte nel tuo codice? Ad 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. Fallback

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

  2. È colpa dell'utente?

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

  3. È colpa tua?

    E da te, intendo qualsiasi cosa che sia non l'utente, quindi va da te che digiti un percorso di file sbagliato, a qualcosa che va storto nel tuo server. A rigor di termini, è giunto il momento per un errore HTTP 503 , in quanto, beh, il servizio non è disponibile. L'elemento grafico ha una funzione show_404() , puoi facilmente creare show_503() .

Parola di consiglio, dovresti prendere in considerazione le eccezioni canaglia. CodeIgniter è un pezzo di codice disordinato e non si sa mai quando verrà visualizzata un'eccezione. Allo stesso modo, potresti dimenticare le tue eccezioni e l'opzione più sicura è implementare un catch all exception handler. 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 delle eccezioni, o in alternativa convertire tutti gli errori in ErrorException e lasciare che il gestore delle eccezioni gestisca con loro:

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");
    
risposta data 04.02.2012 - 23:21
fonte

Leggi altre domande sui tag