Waar en wanneer Exceptions afhandelen?

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Senior DevOps-ontwikkelaar eIDAS

Functie­omschrijving Burgers en bedrijven veilig en betrouwbaar digitaal toegang geven tot diensten en producten van het ministerie van Economische Zaken en Klimaat. Als senior DevOps-ontwikkelaar bouw je daar letterlijk aan mee. En dat doe je bij DICTU: een van de grootste en meest vooruitstrevende ICT-dienstverleners van de Rijksoverheid. Jij werkt mee aan de doorontwikkeling van eIDAS, dat staat voor Electronic IDentification Authentication and trust Services. Deze koppeling maakt de grensoverschrijdende authenticatie op overheidswebsites binnen de Europese Unie mogelijk. Het ministerie van Economische Zaken en Klimaat heeft één moderne toegangspoort voor zijn diensten en inspecties. Enkele daarvan zijn dankzij eIDAS inmiddels

Bekijk vacature »

Pagina: 1 2 volgende »

Write Down

Write Down

18/07/2012 17:52:40
Quote Anchor link
Hi

Ik zit een beetje met de vraag waar en wanneer ga ik exceptions opvangen. Ik ben bij deze vraag gekomen nu ik bezig ben aan een Database class. (met PDO)

Wanneer de verbinding met de database niet kan gemaakt worden, wordt er een exception gegooid. Leuk, maar waar en wanneer van je hem op? Ik zou eerder geneigd zijn om voor de GUI te gaan. Door specifiek die van de database te catchen zou ik dan naar de gebruiker toe kunnen melden dat er een fatale fout optrad.

Langs de andere kant, moeten fouten die optreden met de database zelf niet binnen de database class afgehandeld worden? Mijn constructor van mijn database class die ziet er als volgt uit:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
<?php
    public function __construct(Log $log, Config $config) {
     //code
    }
?>


Nu begin mij echter sterk af te vragen of het wel verstandig is om dat al dan niet zo te doen. Graag jullie meningen!
 
PHP hulp

PHP hulp

01/10/2020 00:56:56
 
Write Down

Write Down

23/07/2012 12:31:26
Quote Anchor link
Bump
 
Erwin H

Erwin H

23/07/2012 13:00:33
Quote Anchor link
Interessant item waarop wat mij betreft niet een eenduidig antwoord te geven is. Mijn vuistregel is om de exception zo vroeg mogelijk af te vangen waar de exception ook daadwerkelijk afgehandeld kan worden.

voorbeeld 1: je hebt een request class die alle user parameters inleest en controleert. Tijdens een check merk je dat de parameter een string is en geen integer. De request class gooit een exception om hiervan melding te maken. De class zelf vangt die direct weer af, want bij een foute invoer geef je gewoon een default waarde terug. De rest van de applicatie merkt hier dus helemaal niets van. Die krijgt gewoon een correcte waarde terug waarmee verder kan worden gegaan.

voorbeeld 2: je hebt een query in je database waarvoor je bepaalde parameters absoluut nodig hebt. Nu blijkt in de method waarin je de query wilt uitvoeren dat een parameter geen correcte waarde heeft. Hier kan je dan een exception gooien, want de query kan niet worden uitgevoerd. De aanroepende class vangt die op en bepaalt dat er dan maar een lege resultset teruggestuurd wordt. De rest van je applicatie kan gewoon door, maar laat alleen een 'lege' pagina zien (wat in veel omstandigheden best acceptabel kan zijn).

voorbeeld 3: je database connectie werkt niet. Hier laat je de database class ook een exception gooien. Dit kan de database class zelf niet verder afhandelen. De aanroepende class ook niet, want dit heeft meer gevolgen dan alleen een lege pagina (bijvoorbeeld je kan een gebruiker ook niet inloggen, je kan geen pageviews registreren etc etc). Deze exception borrelt dus helemaal op naar de applicatie/controller class die dan bepaalt dat er een specifieke foutpagina moet worden getoond.
 
Write Down

Write Down

23/07/2012 13:45:30
Quote Anchor link
Bedankt voor je reactie. Trek je deze lijn ook door naar het loggen? Ik bedoel, stel de database connectie lukt niet. Je vangt de exception in de GUI geeft weer dat er een fatale fout optrad en logt deze fout. Dit lijkt me vrij logisch.

Maar heeft het dan zin om een log-object mee te geven aan de database class? Ik twijfel hier een beetje aan.
 
Erwin H

Erwin H

23/07/2012 15:06:01
Quote Anchor link
Het loggen kan je op verschillende manieren doen. Op de een of andere manier wil je dit soort fouten in elk geval wel gelogd hebben. Wat ik echter niet zou doen is het logobject in de constructor meegeven. In de constructor geef ik alleen parameters mee die absoluut nodig zijn voor het functioneren van het object. Een logobject is dat niet. Als er geen logobject gegeven is, kan de rest van de functionaliteit gewoon doorgaan.

Wat een betere optie is is om het via een setter te plaatsen. Je kan bijvoorbeeld na constructie van het object de factory of service container als je zoiets gebruikt het logobject laten setten. Een andere methode, die ik gebruik, is om een observer interface te bouwen die alle standaard classes implementeren. Via die interface kan een logobject zich dan zelf aanmelden bij het databaseobject. Het databaseobject broadcast vervolgens alle foutmeldigen en het logobject kan zelf bepalen bij ontvangst van een foutmelding wat ermee te doen. Het databaseobject weet in zo'n geval niet eens of er wel een logobject bestaat.
 
Write Down

Write Down

23/07/2012 15:32:39
Quote Anchor link
Ik ben van plan via een service container te werken. Maar dan zie ik niet zo goed in waar je gaat loggen.

Ik neem aan dan dat je in de container gaat instellen wat de logger is voor de database? Maar waar ga je dan loggen als het fout loopt met de database?
 
Wouter J

Wouter J

23/07/2012 15:32:55
Quote Anchor link
Het ben het bijna helemaal eens met Erwin, alleen niet met de laatste alinea.

Ik zou een logger via een setter meegeven, een constructor is inderdaad niet heel handig, en dan de klasse zelf de exceptions laten loggen. Je kan dan per log bepalen of het slechts alleen een Notice level wordt of dat het problematisch wordt en je het een Critic level moet meegeven.

Het observer pattern klinkt allemaal geweldig, maar die dan dat niet. Of wacht, nu zeg ik wat verkeerd. Je kan natuurlijk als 2e parameter bij het gooien van een exception een level meegeven die de observer dan kan uitlezen ($e->getCode()).
Alleen dat iedere class hetzelfde interface implementeert staat mij nog niet erg aan.
 
Write Down

Write Down

23/07/2012 15:40:39
Quote Anchor link
Wouter J op 23/07/2012 15:32:55:
Ik zou een logger via een setter meegeven, een constructor is inderdaad niet heel handig, en dan de klasse zelf de exceptions laten loggen. Je kan dan per log bepalen of het slechts alleen een Notice level wordt of dat het problematisch wordt en je het een Critic level moet meegeven.


Klinkt theoretisch geweldig. Ik zie alleen niet gelijk in hoe. Neem nu dat mijn wachtwoord voor de database verkeerd is. Dan wordt er in principe een exception gegooid. (door PDO in dit geval) Het kan volgens mij echter niet de bedoeling zijn deze exception in de database class te gaan vangen. Ik wil namelijk graag de mogelijkheid hebben om een boodschap naar de buitenwereld te sturen. Aan een gewone bezoeker bijvoorbeeld 'Er trad een fatale fout op. De pagina kan niet worden weergegeven'. Aan een admin zou ik dan iets meer informatie kunnen geven of dergelijke.

Ik zie alleen niet in, als je de exception opvangt binnen de class zelf, hoe ga je dan nog een boodschap naar buiten toe tonen?
 
Wouter J

Wouter J

23/07/2012 15:52:33
Quote Anchor link
Het wachtwoord is verkeerd, PDO geeft een exception terug. Die laat je borrelen naar de Controller klasse. Die vangt deze op, logt de exception en geeft een melding weer in de view.

We komen dan in Voorbeeld 3 van Erwin.
Gewijzigd op 23/07/2012 15:52:54 door Wouter J
 
Erwin H

Erwin H

23/07/2012 15:53:54
Quote Anchor link
Wouter J op 23/07/2012 15:32:55:
Alleen dat iedere class hetzelfde interface implementeert staat mij nog niet erg aan.

In mijn logger worden 3 dingen meegegeven: de class(naam) die de fout broadcast, de melding en de severity. Aan de logger om er iets mee te doen.
Verder is de functionaliteit hiervoor volkomen opgenomen in een basis class waar alle classes van afstammen. Geen enkele class hoeft er nog iets voor te doen, de methodes in de basis class handelen het af.

@write down,
Afhandelen en toch nog door laten bubbelen naar boven is ook nog een optie. Je kan de fout bijvoorbeeld in de class zelf eerst afvangen, loggen en dan opnieuw een exception gooien zodat het toch nog op een ander niveau terecht komt. Zo kan je en de fout juist loggen en een juiste foutpagina tonen.
 
Write Down

Write Down

24/07/2012 10:56:40
Quote Anchor link
Ik ben een beetje aan het twijfelen of ik het dan al dan niet naar de 'Application' class stuur in sommige gevallen.

Want wat Erwin zegt vind ik ook wel een 'leuk' idee. Alleen vind ik het dubbel werk. Langs de andere kant, ik vind het logischer dat de exceptions in class zelf worden gelogd. Maar is het in dit geval niet handiger allemaal 'eigen' exception class te maken? Bijvoorbeeld een exception voor de database, een exception voor config, ... En in die exception class de logging inwerken?
 
Erwin H

Erwin H

24/07/2012 11:11:33
Quote Anchor link
Het hoeft niet zozeer dubbel werk te zijn. Ten eerste wil je altijd een soort fail-safe mechanisme hebben dat onverwachte fouten kan afhandelen op het allerhoogste niveau. Normaal gesproken heb je dan dus in je application class een try...catch block staan om nagenoeg de hele code (niet alles, maar in elk geval dat deel dat de echte logica aanstuurt). Hiermee kan je wellicht niet hele mooie foutmeldingen geven, maar in elk geval een melding en niet de kale php fout, of helemaal niets.

Ten tweede wil je ook zorgen dat je in elke class een afhandeling hebt voor fouten die je verwacht en die je ook kan afhandelen.

Met andere woorden, wil je alles op een nette manier kunnen afhandelen dan heb je toch al op meerdere niveau's foutafhandeling zitten. Kleine moeite dus om een fout door te sturen op het moment dat je hem onderschept en hem eigenlijk niet kan afhandelen. Door middel van verschillende Exception classes kan je dan heel gericht de ene fout wel afhandelen en de andere niet.
 
Wouter J

Wouter J

24/07/2012 11:17:03
Quote Anchor link
Write Down, ja verschillende exceptionklassen is zeer goed. Ik maak zelf altijd 1 basis Exception klasse voor het project:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
<?php
class OOPbuilderException extends \Exception {}
class OOPbuilderLogicException extends \LogicException {}
?>

En dan kun je dat weer verder uitbouwen naar andere typen exceptions (zoals PHP er ook al wat heeft:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
<?php
class ParserException extends \OOPbuilderException {}
class InvalidInstanceOfArgumentException extends \OOPbuilderLogicException {}
// ...
?>
 
Write Down

Write Down

24/07/2012 11:22:50
Quote Anchor link
Bedankt voor de reacties!

@Wouter
En injecteer je in sommige van die classes dan een log-object?
 
Wouter J

Wouter J

24/07/2012 11:51:49
Quote Anchor link
Nee, want dan heeft de exception opeens 2 verantwoordelijkheden...

Meneer Exception gooi je uit het vliegtuig, er is iets mis gegaan. Dan moet Meneer Exception zichzelf niet gaan opvangen, dat kan hij niet. Daarom moet je onderaan, op de aarde, een groot kussen leggen waarin je hem kan opvangen. Of je vangt Meneer Exception al eerder op door in een vliegtuigje onder hem te gaan vliegen en hem zo in de bijrijdersstoel te laten vallen.

Je moet proberen dat laatste te doen en mocht het echt zo kritiek zijn dat je hele applicatie niet meer verder kan dan moet je hem gewoon naar beneden laten vallen en de stootkussen gebruiken om de foutmelding weer te geven.
 
Write Down

Write Down

24/07/2012 11:57:36
Quote Anchor link
Okay, bedankt!

Het voordeel hiervan is dan dat je gemakkelijker verschillende catch-blokken kan gebruiken. Of zie ik dit verkeerd?

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
<?php
try {
    //hier boeiende code
}catch(\ParserException  $e) {
    echo $e->getMessage();
    //doe iets specifiek
}catch(\DatabaseException $e) {
    echo $e->getMessage();
    //doe iets specifiek
}
?>
Gewijzigd op 24/07/2012 11:58:28 door Write Down
 
Jasper DS

Jasper DS

06/05/2013 14:27:27
Quote Anchor link
Kan er iemand meer uitleg geven over de vorige vraag van Write Down? Wouter, Erwin of Write down zelf?
 
Wouter J

Wouter J

06/05/2013 14:39:46
Quote Anchor link
Ja, het voordeel is dat je dan de fouten anders kunt afvangen en ook op een ander moment. Je kan sommige exception al opvangen in een klasse, terwijl de andere helemaal opborrelt.
 
Jasper DS

Jasper DS

07/05/2013 01:18:34
Quote Anchor link
Dus je catcht'm en dan gooi je hem weer weg?

Toevoeging op 07/05/2013 01:18:37:

Dus je catcht'm en dan gooi je hem weer weg?
 
Ozzie PHP

Ozzie PHP

07/05/2013 01:25:09
Quote Anchor link
Ik ben dan ook wel benieuwd... heb je eigenlijk per se Exceptions nodig, of kun je ook alles afvangen met bijv. een trigger_error()? Is het wezenlijke verschil waarom je eventueel Exceptions zou gebruiken dat je die op een hoger niveau kunt afvangen?
 
Jasper DS

Jasper DS

07/05/2013 13:29:29
Quote Anchor link
Een controller method kan er zo uitzien:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public function index($args)
    {
        $view = new View('Jds::WelcomeView');
        $view->setParam('pageTitle', 'Hello World');        

        try
        {
            $articlemapper = new \Jds\Models\Article\ArticleMapper();
            $orderLineMapper = new \Jds\Models\Order\OrderLineMapper();
            $view->setParam('android', $articlemapper->findAvaibleByCat(1));
            $view->setParam('ios',  $articlemapper->findAvaibleByCat(2));
            $view->setParam('win', $articlemapper->findAvaibleByCat(3));
            $view->setParam('new', $articlemapper->getLast(4));
            $view->setParam('mostPopular', $orderLineMapper->getMostPopular());
        }
        catch(\Jframe\Storage\Adaptors\Exceptions\CouldNotConnectException $e)
        {
            $view = new View('Jds::Errors/ErrorMessageView');
            $view->setParam('pageTitle', 'Er liep iets mis');
            $view->setParam('message', $e->getMessage());
        }

        $view->render();
    }


Maar eigenlijk wil ik dat standaard elke methode dat try - catch block omvat. Is er een slimme manier om dat te doen? EN zou ik het implementeren in de bestanden van mijn framweork of zou ik daar geen exceptions gaan afhandelen?
 

Pagina: 1 2 volgende »



Overzicht Reageren

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.