try/catch versus die
Bij het aanmaken van de objecten die je in je applicatie gebruikt (dus kort door de bocht: het aanroepen van de __construct() methoden) wordt bij het database-object informatie gebruikt uit een externe configuratie-bron (een object, een array, een bestand, doet er eigenlijk niet toe).
Nu zit er een fout in de parameters voor het maken van een verbinding met de database.
Als het maken van een connectie mislukt staak ik de hele applicatie met die() (met een duidelijke omschrijving van het euvel, uiteraard).
Dit omdat dit een configuratie-fout is die handmatig moet worden verbeterd. Daarnaast omdat de rest van de applicatie valt of staat met het correct werken van de database, het heeft simpelweg geen zin om proberen te "herstellen" van deze situatie omdat 'ie in wezen gewoon "kapot" is door de foutieve configuratie.
Dit doe ik ook bij het aanmaken van de andere objecten - de applicatie is in wezen bezig met initialisatie. Als er daar iets mis gaat dan kan een verdere correcte werking niet gegarandeerd worden.
Ik noemde al een aantal argumenten voor het gebruiken van die() in plaats van een try/catch blok. Ik gebruik overigens geen PDO, het is dan wel verstandig om alles in een try-catch blok te zetten ;-).
Mijn vraag: wat zijn de argumenten voor (of tegen) het gebruik van die/exit versus try-catch in dit stadium van je applicatie onder deze omstandigheden (foutieve configuratie)?
---
Het is eigenlijk een beetje een kip-ei situatie in mijn geval. Bezoekers van de site (applicatie) krijgen een nette fout-pagina te zien wanneer er iets misgaat ("runtime" errors worden wel gethrowd en gevangen). Maar alle pagina's / content komen uit de database. Als ik geen database heb, kan ik ook geen pagina('s) tonen, dus ook geen foutpagina. Je ziet mijn dilemma :).
Je kan namelijk in een try/catch een exception catchen en netjes afhandelen.
Bijvoorbeeld;
Code (php)
Ook hier gebruik ik een die() maar ik krijg wel meer info uit het Exception object.
Het is dynamischer vindt ik zelf.
Als mijn applicatie eenmaal correct en volledig is geïnitialiseerd schakel ik over op try-catch zodat ik ook mijn exceptions netjes kan afhandelen.
Ik maak onderscheid tussen wat "developers" zien en wat "gewone bezoekers" te zien krijgen, ik ga een bezoeker niet lastig vallen met een cryptische foutmelding. Ik krijg (als developer) de foutmelding rauw te zien (die() met exception boodschap, zoals jij ook doet), bezoekers krijgen een nette foutpagina voorgeschoteld die gegenereerd wordt met behulp van informatie uit de database.
De opbouw is in grote lijnen als volgt:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
// laden van benodigde objecten / init
// ...
try {
// uitvoeren applicatie acties
// ...
} catch (Exception $e) {
// log error
// ...
if (<developer check>) {
die(<boodschap met details exception>);
} else {
try {
// probeer de error pagina te laden, maar ook dit kan foutgaan
// omdat er ergens in een pagina-element dezelfde/een andere fout zit
// dit is in feite het opnieuw uitvoeren van applicatie acties
// ...
} catch (Exception $e) {
// log error
// ...
// ik heb mijn best gedaan, maar kan hier verder niets meer mee/aan doen
die('[error] something seriously broke');
}
}
}
?>
// laden van benodigde objecten / init
// ...
try {
// uitvoeren applicatie acties
// ...
} catch (Exception $e) {
// log error
// ...
if (<developer check>) {
die(<boodschap met details exception>);
} else {
try {
// probeer de error pagina te laden, maar ook dit kan foutgaan
// omdat er ergens in een pagina-element dezelfde/een andere fout zit
// dit is in feite het opnieuw uitvoeren van applicatie acties
// ...
} catch (Exception $e) {
// log error
// ...
// ik heb mijn best gedaan, maar kan hier verder niets meer mee/aan doen
die('[error] something seriously broke');
}
}
}
?>
EDIT: met als "verschil" dus dat in het init-deel (regel 3) mogelijk de applicatie stopgezet wordt als er configuratie-fouten zijn.
Gewijzigd op 17/02/2015 16:56:41 door Thomas van den Heuvel
Dat kan wel als je een ErrorDocument instelt in bijvoorbeeld .htaccess en je de reeks exceptions vervolgens laat eindigen in een nette HTTP-fout (meestal 500 en bij een overvraagde databaseserver eventueel een 503). Helaas moet dat dan meestal wel een statische HTML-pagina zijn.
Als je de logica van de SPL-exceptions volgt of nabouwt, maak je ook een onderscheid tussen logic exceptions en runtime exceptions.
Volgens mij dekken beide Exception (sub)types niet echt de lading.
Statische HTML-pagina... tsja. De error pagina zou editbaar moeten zijn.
Wat is er mis mee om een configuratie-fout als fatal error te behandelen?
Of een configuratiefout een fatal error is, is ook niet zomaar te zeggen. Alles is configureerbaar te maken, maar niet elke configuratiefout is dus fataal.
Maar de algemene aanpak is dus een beetje: staak de applicatie als het opzetten van een of meer objecten niet lukt lijkt mij niet verkeerd?
Vergelijk het met de lancering van een raket: als een van de stuwraketten niet vuurt, wil jij de lancering dan toch voortzetten? Ik kan wel een redelijke voorspelling doen waar dat dan in uitmondt.
Exceptions zijn ook niet altijd de oplossing. Als je weet dat een correcte operatie van je applicatie op enig moment niet haalbaar is kun je beter direct stoppen lijkt mij. Te meer als er overduidelijke mankementen zijn.
"Onze autoband staat plat, maar laten we toch op skivakantie gaan."
:)
EDIT:
Quote:
Zonder mailserver kun je (even) niet mailen, maar moet een complete applicatie daardoor onderuit gaan? Zonder toegang tot de databaseserver heb je ook (even) geen data, maar moet een site dan helemaal plat gaan?
Leuk voorbeeld :). Het eerste probleem kun je ondervangen door berichten tijdelijk op te slaan in je database, en het verzenden later nogmaals te proberen. Dat wordt wel een beetje lastig als je database eruit ligt ;-). Tenzij je een fallback hiervoor hebt via tekst-bestanden :). Maar wat nu als je diskruimte vol is? :) etc.
Soms kun je problemen niet voor je uitschuiven, of is het onverstandig om dit te doen.
Gewijzigd op 17/02/2015 18:09:48 door Thomas van den Heuvel
Bij schaarse resources heb je namelijk altijd het probleem dat de ene emmer net iets eerder vol/leeg is dan de andere emmer.
Met exceptions koppel je de fout los van de applicatie. De database connector class kan niet connecten, resultaat: CannotConnectException. De connector stopt, maar wat er verder gebeurd ligt buiten het bereik van deze database connector en dat hoort ook zo. De applicatie kan een exception handler hebben die bij een CannotConnectException die() uitvoert, maar de class die de connector aanroept zou ook kunnen beslissen om de CannotConnectException op te vangen en in plaats daarvan de cache als fallback te gebruiken. In weer een ander scenario zou de class die de connector aanroept nog een keer de connector aanroepen, om te kijken of het dan wel werkt.
Dat is het mooie en grote voordeel van exceptions: Een scheiding tussen error en afhandeling. Precies in de lijn met de OO gedachte.