Dove dovrebbero essere gestite le PDOException?

2

Ho scritto una classe wrapper del database per aiutare con le query e sto cercando di capire il modo migliore per implementare i blocchi try-catch.

Nella mia classe di database, ho una funzione di supporto;

public function fetchAll($sql, $bind = array()) {

        $stmt = $this->query($sql, $bind);
        $result = $stmt->fetchAll($this->_fetch_mode);

        return $result;
    }

che posso chiamare al di fuori della classe:

$this->db->fetchAll($sql,[$var1,$var2]);

La mia domanda è dove mettere i blocchi try-catch. Sarebbe "più facile" se li inserissi semplicemente nella mia classe di database:

    public function fetchAll($sql, $bind = array()) {

            $stmt = $this->query($sql, $bind);
            try {
                  $result = $stmt->fetchAll($this->_fetch_mode);
                } catch (PDOException $e) {
                   //log the error...give some message
                }

            return $result;
        }

invece di avvolgere ogni chiamata nella classe del database:

try {
     $this->db->fetchAll($sql,[$var1,$var2]);
    } (catch PDOException $e) {
     //log the error...give some message
    }

Tuttavia, il mio sentimento (senza essere in grado di fare un esempio concreto ...) è che anche se sarebbe più un lavoro per avvolgere ogni chiamata alla classe del database, se faccio codice duro il blocco try-catch nella mia classe di database, non sarà così flessibile in termini di errori di gestione.

Oppure, sarebbe meglio avere un "catch-all" nel mio index.php (io uso MVC) per catturare le eccezioni generali (per esempio, relative al PDO), e poi se I bisogno di più specifici nella mia applicazione (ad esempio, quelli lanciati quando ci si connette a servizi di terze parti), quindi aggiungere ulteriori catch try-block per loro:

Nel mio index.php, che tutte le richieste passano attraverso il mio file .htaccess:

try {
    $db = new Db();
    $db->connect(['host'=>DB_HOST,'dbname'=>DB_NAME,'username'=>DB_USERNAME,'password'=>DB_PASSWORD]);

    $bootstrap = new Bootstrap();

    $bootstrap->setControllerPath('application/controllers');
    $bootstrap->setModelPath('application/models');

    $bootstrap->init(); //load the controller/model/classes
} (catch Exception $e) {
    //log error
    //if ajax request return json_encoded message other redirect to error page
}
    
posta Eric 02.07.2015 - 13:47
fonte

4 risposte

2

Non dovresti assolutamente prendere il PDOException.

Ora, dovresti avere una specie di gestore di eccezioni di ultima istanza che raccoglie tutte le eccezioni e le registra. Le PDOException dovrebbero essere catturate lì. Ma non dovresti prenderli da nessun'altra parte.

A mio avviso, PDOException indica che qualcosa è andato storto con la connessione al database, o che hai scritto male SQL. È utile solo tentare di gestire l'eccezione se si può fare qualcosa di utile come alternativa. Ma se non riesci a ottenere le informazioni dal database, la tua applicazione è probabilmente inutile e non ha molto senso tentare di recuperare.

    
risposta data 03.07.2015 - 17:21
fonte
2

Direi che è impossibile gestire tutte le eccezioni allo stesso modo. Penso che dovresti lasciarlo propagare al codice chiamante solo perché alla fine sa come gestire la situazione eccezionale .

Ad esempio, immagina di avere un blog. Ogni ora si interroga il database per nuovi articoli per popolare una cache. Ma la fortuna non è con te e il database è temporaneamente non disponibile. Puoi lasciare che l'eccezione non venga gestita e l'intero sito non è disponibile. È anche possibile gestire l'eccezione all'interno del livello di astrazione del database e restituire un risultato predefinito (nascondendo effettivamente l'eccezione). Ma restituire un valore predefinito sovrascriverebbe la cache con niente. Il sito è attivo, ma non funziona correttamente. È anche possibile registrare l'errore, rilanciare l'eccezione per consentire al codice chiamante di decidere cosa fare. Il codice chiamante può riutilizzare i risultati della query già memorizzati nella cache e prolungare la durata della cache di un'altra ora sperando che il database ritorni. In questo modo i tuoi utenti subirebbero interruzioni minime del tuo sito e potrebbero non notare nemmeno i problemi.

Tuttavia, non è sempre facile determinare cosa fare nei casi eccezionali e talvolta è preferibile lasciare che l'eccezione si apra fino a quando non raggiunge un gestore di eccezioni generico, che visualizza un errore generico pagina. È inoltre possibile implementare alcune procedure generali in caso di errore di una query, ad esempio la registrazione degli errori in questo gestore di eccezioni generico. Questo sarebbe preferibile in quanto può essere considerato un codice boilerplate che non cambia nulla. E avere le eccezioni non gestite inviate via e-mail all'utente (ad esempio) ti consente di risolvere il problema più rapidamente rispetto a quando dovessi scoprire personalmente il problema.

Hai anche un bonus aggiuntivo raggruppando il codice di registrazione generico in un unico posto. Puoi gestire la registrazione specifica insieme al codice pertinente (dove sono disponibili i dettagli) e se la tua procedura di registrazione generica dovesse cambiare, devi solo cambiarla una volta.

Usando il tuo metodo fetchAll() la struttura potrebbe essere simile a:

public function fetchAll($sql, $bind = array()) {

    // Do something awesome before try-catch block.

    try {

        // Execute query...

    }catch(PDOException $exception) {

        $this->logger->log('SQL query failed: ' . $exception->getMessage());

        /*
         * Re-throw the exception to let the calling code
         * handle the situation.
         */
        throw $exception

    }

    // Continue as everything executed as expected.

}
    
risposta data 03.07.2015 - 16:43
fonte
1

La risposta dipende dalle responsabilità della tua classe di wrapping. Se tutto ciò che fa è incapsulare le impostazioni di connessione per comodità, è preferibile lasciare passare PDOException . Se il tuo wrapper ha più responsabilità, come incapsulare alcune query o il pool di connessioni, potresti sviluppare un'eccezione o due per questo wrapper, che a sua volta avvolge PDOException per aiutarti a sapere da dove proviene l'errore.

Una buona regola empirica per aiutare a rispondere alla domanda, "dove dovrebbero essere catturate le eccezioni," sarebbe catturarli non appena l'applicazione può spostarsi da essa .

Supponiamo che un metodo in classe A abbia bisogno di un valore intero e chiama un metodo in classe B che a sua volta si aspetta un array da un metodo nel wrapper db. Se restituire un valore vuoto dal wrapper db (come array() ) consente all'applicazione di continuare in modo sensato con risultati degradati, quindi l'acquisizione di B è OK. Ma se il array vuoto è un valore previsto, lasciare che l'eccezione aumenti lo stack di chiamate su A è ragionevole. Assicurati di interrompere non appena mentre raggiungi un frame in cui puoi passare dall'errore. In un'applicazione ben progettata, questo non dovrebbe essere più di 2 fotogrammi.

Rilevare le eccezioni al più presto possibile porta a problemi nel comunicare l'errore al chiamante, quando l'eccezione può fare esattamente questo. Ad esempio, se un metodo restituisce i dati da una colonna integer nullable da un database, non si ha un valore di ritorno ragionevole per contrassegnare un errore. Ma l'eccezione indicherà sempre chiaramente l'errore.

Or, would it be better to have a "catch-all" in my index.php

In generale, dovresti avere uno di quelli per assicurarti di avere il controllo delle eccezioni altrimenti non gestite e per assicurarti di fare cose come la registrazione e il rilascio corretto delle risorse che hai acquisito. Evitalo altrove nella tua applicazione per assicurarti di non ingerire informazioni vitali per spiegare come si comporta la tua applicazione.

    
risposta data 05.07.2015 - 11:27
fonte
1

My question is where to put the try-catch blocks.

Domanda semplice; risposta semplice

Metti blocchi catch in cui il tuo codice può fare qualcosa di utile su un'eccezione.

Idealmente, quel "qualcosa di utile" sarebbe quello di recuperare dalla circostanza eccezionale, permettendo così al programma di continuare da quel punto come se nulla fosse accaduto .

IMHO, questa è la parte più importante di Structured Exception Handling .

Se il codice non può fare nulla di "utile" su un'eccezione, non dovrebbe nemmeno riconoscerlo, figuriamoci prenderlo, permettendo all'eccezione di propagarsi in alto e in uscita verso qualche altro codice che può gestirlo, anche se è solo "Oops; qualcosa è andato storto!", gestore di eccezioni catch-all, fornito dal run-time o codificato al livello più alto delle pagine PHP .

    
risposta data 06.06.2018 - 12:35
fonte

Leggi altre domande sui tag