Inmiddels al weer aardig wat topics gelezen en opgezocht van de beginner tot de expert. Het is alleen altijd lastig om de voor jou beste methode uit te kiezen. Ik zit momenteel namelijk met 2 problemen.
Een daarvan is de alom bekende foutafhandeling of liever gezegd het opvangen van uitzonderingen. Ik heb inmiddels begrepen dat ik in de meeste gevallen Exceptions kan gebruiken en dat ik er verstandig aan doe om meerdere catch blokken te gebruiken om zo gebruikersfouten en systeemfouten te kunnen onderscheiden.
Momenteel heb ik het zo gedaan dat mijn autoloader binnen een try blok staat. Dat houd dus in dat mocht er in een van de classes wat fout gaan, dan wordt het in ieder geval opgevangen.
Mijn punt, zodra er dus iets fout gaat stop hij al het andere wat er binnen het try blok gebeurt, zo dus ook de knoppen die ik bijvoorbeeld weer laat geven onder een error.
In een situatie als een CMS dat er geen gebruikers gevonden kunnen worden, maar ik wil er nog wel een kunnen toevoegen. Dan moet niet ineens die knop weg zijn. Zou dit inhouden dat ik dan gewoon mijn try en catch blokken anders moet doen? Of kan ik aan de hand van levels wellicht zeggen dat hij de rest gewoon uit moet voeren? Of geen exception gebruiken, maar iets totaal anders, iets van mijzelf? En de exceptions laten voor de pure fouten die alles moeten stoppen.
Mijn andere probleem ligt bij het zo min mogelijk herhalen van code. Het liefst schrijf ik het maar 1 keer. Althans dat is mijn doel.
Maar neem bijvoorbeeld mijn UserMapper, die is praktisch hetzelfde als mijn PageMapper behalve dat er een andere factory method inzit is hij gelijk. Is er geen methode om dat over te erven zonder dat mijn flexibiliteit naar de maan gaat? Denk bijvoorbeeld aan een abstracte classe?
>> Momenteel heb ik het zo gedaan dat mijn autoloader binnen een try blok staat. Dat houd dus in dat mocht er in een van de classes wat fout gaan, dan wordt het in ieder geval opgevangen.
De autoloader laad alleen bestanden, hij voert ze niet uit. Dit gaat het dus niet verhelpen. Beter is het om een exception handler in te stellen ([php]set_exception_handler[/php]), deze wordt aangeroepen wanneer een exception niet wordt opgevangen.
>> Zou dit inhouden dat ik dan gewoon mijn try en catch blokken anders moet doen? Of kan ik aan de hand van levels wellicht zeggen dat hij de rest gewoon uit moet voeren? Of geen exception gebruiken, maar iets totaal anders, iets van mijzelf? En de exceptions laten voor de pure fouten die alles moeten stoppen.
Ik zou gaan voor die eerste. Iets zelf bouwen moet je alleen in echt hele specifieke gevallen doen, als je echt hele goede redenen hebt. Redenen die zo goed zijn dat als ik je 's nachts om 3 uur wakker maak je ze mij moet kunnen vertellen. :)
>> Is er geen methode om dat over te erven zonder dat mijn flexibiliteit naar de maan gaat? Denk bijvoorbeeld aan een abstracte classe?
Allereerst, die abstracte classe. Wat ik ga doen; een abstracte classe, DataMapper. Deze DataMapper bepaald al een deel van de functionaliteiten, zoals bijvoorbeeld alle CRUD methods. De UserMapper, PageMapper etc. extend ik dan van mijn abstracte classe.
De Exceptions. Bij alle methodes die dus eigenlijk een Gelukt of Niet gelukt waarde moeten terugsturen ga ik nu dus niet meer de zin terugsturen. Ik stuur gewoon TRUE of FALSE. Zo maak ik die zinnen waar ik vroeger een notice blok voor gebruikte nu pas op de view pagina. Grote voordeel is dus dat ik geen exceptions hoef te gooien.
De exeptions die ik dan nog gooi zijn hoogstwaarschijnlijk fataal, denk dan bijvoorbeeld aan Query mislukt, Map niet gevonden dus code kapt ermee.
Bij formulieren kan ik dan zeggen dat ik de try catch blokken al in de class maak zodat hij al eerder opgevangen wordt en niet pas als alles uitgevoerd moet zijn.
>> In een situatie als een CMS dat er geen gebruikers gevonden kunnen worden, maar ik wil er nog wel een kunnen toevoegen. Dan moet niet ineens die knop weg zijn. Zou dit inhouden dat ik dan gewoon mijn try en catch blokken anders moet doen?
Je kunt je try blok ook "kleiner" maken en de knoppen onder je try/catch blok zetten:
<?php
try {
$user = new User();
} catch (Exception $e) {
// geen user aangemaakt
}
// toon hier je knoppen
?>
Ongeacht of er wel of niet een user wordt aangemaakt, zullen de knoppen altijd worden getoond.
Ja dat had ik begrepen Ozzie, ik probeerde dat alleen te voorkomen door eventueel iets van een Level of iets dergelijks. Ik wilde graag een centraal catch blok.
Maar ik heb het nu redelijk in mijn hoofd zitten wat het moet gaan worden, dus denk dat het wel goed komt.
Ik moet zeggen ik loop precies een aantal stappen achter jou aan geloof ik, want ik ontdek steeds redelijk recente topics van jou als ik een vraagstuk heb. Zo nu en dan verduidelijking of bevestiging vragen en ik kan weer verder. Heel prettig.
Wat ik zelf doe is op class niveau exceptions gooien, en dat vind ik zelf wel handig.
Als je een class foo hebt, dan gooi je vanuit die class een FooException. Dan weet je ook WAAR de exception vandaan komt. Je kunt ook nog een foutcode meegeven een een exception.
Als je het mij vraagt is dat dan weer wat overbodig? Dan maak je dus per class zo'n beetje een Exception classe aan. Dus UserException, PageException enz. Terwijl je ook een bestandsnaam mee kan laten komen?
Het gaat om het catch block. Je wilt dat je daar redelijk precies kunt bepalen welke exceptions je wilt catchen en welke niet, dit wil je niet later nog eens moeten doen in een if statement in de catch.
Per class vind ik wat overdreven, ik doe het vaak per onderwerp. Dat komt ook doordat ik de Doctrine stijl van exceptions gebruik: Exceptions aanmaken in een factory method.
Om maar in het verhaal van een database te blijven, wanneer er geen resultaten gevonden zijn is dat een uitzondering voor de find method, dus dan maken we een exception: NoResultsFoundException:
<?php
namespace Milo\Database\Exception;
class NoResultsFoundException extends \RuntimeException implements MiloDatabaseException
{
public static function create($query)
{
return new self(sprintf('No results found for query "%s".', $query));
}
}
?>
Je database heeft ook verschillende exceptions wanneer het geen connectie kan maken. Dit gaat over 1 onderdeel en is dus 1 exception (dit gaat een beetje op gevoel). Wel zorg ik altijd dat elke message zijn eigen exception code krijgt:
<?php
namespace Milo\Database\Exception;
class ConnectionException extends \RuntimeException implements MiloDatabaseException
{
const SERVER = 1;
const AUTH = 2;
public static function noServerConnection($server, $message = null)
{
return new self(sprintf(
'Failed to connect to server "%s"%s', $server, $message ? ': '.$message : ''
), self::SERVER);
}
public static function wrongCredits($server, $credits)
{
return new self(sprintf(
'Wrong credentials ($credits) to connect to server "%s".', $credits, $server
), self::AUTH);
}
}
>> Als je het mij vraagt is dat dan weer wat overbodig? Dan maak je dus per class zo'n beetje een Exception classe aan. Dus UserException, PageException enz. Terwijl je ook een bestandsnaam mee kan laten komen?
>> Of zie ik dat verkeerd?
Het is in ieder geval al goed dat je er over nadenkt. Nee, het heeft geen zin om de bestandsnaam mee te sturen. Je wil in je code kunnen zeggen... als vanuit class X een exception wordt gegooid dan wil ik die opvangen, maar als vanuit class Y een exception wordt gegooid dan laat ik 'm door gaan.
Stel jij wil iets doen met class Foo. Je zet het in een try block. Maar class Foo maakt gebruik van class Bar en class Bar maakt weer gebruik van class FooBar. Nu gooit class FooBar ineens een Exception. Als je met exceptions per class werkt, dan kun je gericht die exception opvangen, of misschien juist wel niet.
Ik kijk dus vooral WAAR de fout zich voordoet en Wouter kijkt vooral WAT er fout gaat. Voor beide methodes valt iets te zeggen. Het nadeel aan de WAT methode vind ik dat je niet weet waar ie vandaan komt. Persoonlijk vind ik dat niet fijn, want als ik niet weet waar de fout vandaan komt, hoe moet ik er dan actie op ondernemen.
Oké ik snap nu redelijk de verschillen, mijn doel is nu uiteraard het beste van de twee werelden. Nu ga ik even hardop denken over hoe ik dat ga bereiken. Het is nogal wat.
Persoonlijk voel ik denk ik wel meer voor wat Wouter doet, ik bedoel als ik weet waar iets vandaan komt vind ik dat minder zaligmakend dan dat ik weet wat er fout gaat. Een bestandsnaam voor waar het fout gaat, Exceptions eigen getfile methode, dan zal dat voor mij genoeg zijn.
Op de nodige plekken maak ik gewoon extra Try - Catch combinaties zodat niet alles omver gaat. Op mijn hoofd Try - Catch combinatie, welke om de include van de uitvoer pagina's heen staat, wil ik een extra catch blok om de verschillende fouten op te kunnen vangen. Zoals Gebruikersgeralteerd en Systeemgerelateerd. Al denk ik dat ik dat zelfs bij de meeste zou gebruiken. Anders is het misschien al te laat.
Verder bekijk ik de resultaten van Queries en dingen gewoon met TRUE of FALSE. Dus als er TRUE komt kan ik eventueel bericht tonen dat het goed is gegaan met verwijderen of de data laten zien met ophalen. Daar ga ik dus geen Exceptions voor gebruiken, ook al was dat wel het idee eerst.
Oké. Met zoveel dingen is het een persoonlijke keuze hoe je het aanpakt. Als je er vooral maar erg goed over nadenkt. Stel jij gooit ergens een WAT exception, bijv. een FileNotFoundException of iets dergelijks. Weliswaar weet je nu WAT er fout gaat, maar je weet niet WAAR de fout optreedt. Stel dat een belangrijk configuraitebestand niet kan worden ingeladen, dan heb je een dik probleem. Echter, is het simpelweg een cachebestand wat nog niet is aangemaakt, dan is er niks aan de hand. Als je dus niet weet WAAR de fout vandaan komt, kun je ook geen passende maatregelen treffen. En tuurlijk, dan kun je wel de file ophalen, maar daar heb je niks meer aan, want je hele applicatie is al gestopt. Bovendien, je wil toch niet op 1 centraal punt de filenamen binnenhalen en dan per filenaam gaan beslissen wat er moet gebeuren? Krijg je straks een switch met 100 opties :)