About : libVSC [3]
Another of the things I want to talk about, which I consider to be just good coding practice, but I seldom see it used in PHP development, is the usage of meaningful Exception handling. So I'll try to talk a bit about how Exception handling is used throughout libVSC.
Exception handling
Since PHP's own error handling is as useless as is simple, I decided to build a custom error handler to convert all problems generated by libVSC to custom Exceptions and handle them at various levels. You can see the code at set_error_handler() function and ErrorException objects. The code that does this, is quite simple and takes care of everything except Fatal Errors, of which I'll talk a bit about how I try to deal with them in order to fail as gracefully as possible. Here's some code:
function exceptions_error_handler ($iSeverity, $sMessage, $sFilename, $iLineNo) {
if (error_reporting() == 0) {
return;
}
if (error_reporting() & $iSeverity) {
// the __autoload seems not to be working here
include_once(realpath(VSC_LIB_PATH . 'exceptions/vscexceptionerror.class.php'));
throw new vscExceptionError ($sMessage, 0, $iSeverity, $sFilename, $iLineNo);
}
}
In this case vscExceptionError extends ErrorException, but mostly because I use a naming convention for Exceptions which assumes vscException is at the beginning of any exception. The purpose of this is to allow easy access from the IDE's auto complete. At the end of the exception's class name usually there is the module name. (Eg. vscExceptionController)
The above being said, I have hinted a bit the hierarchy I'm using for structuring the Exception related code. Namely modules (application, domain, presentation) and their sub-modules in turn, each have (well not exactly always) it's own set of Exceptions, to allow the control, as fine-grained as needed at catching time.
Next I'll give a couple of examples about handling them. First there's from the index.php file of a libVSC based application. This one is a bit useless, since if your Exception bubbled up to here, there's not really much you can do, except maybe display an error page to the user, or a debug stack to the developer.
// in the application's index.php file
try {
import ('exceptions');
import ('application/controllers');
$oDispatcher = vsc::getDispatcher();
$oRequest = vsc::getHttpRequest();
// load the sitemap
$oDispatcher->loadSiteMap (LOCAL_RES_PATH . 'map.php');
/* @var $oProcessor vscProcessorA */
// get the controller
$oProcessor = $oDispatcher->getProcessController ($oRequest);
/* @var $oFrontController vscFrontControllerA */
// get the front controller
$oFrontController = $oDispatcher->getFrontController ();
// get the response
$oResponse = $oFrontController->getResponse ($oRequest, $oProcessor);
// output the response
$sContent = $oResponse->getOutput();
ob_end_clean();
} catch (vscExceptionPackageImport $e) {
// one of the two folders import()'ed couldn't be found in the include path
if (vsc::isTesting()) {
// show a stack trace
} else {
// show a 500 error page
}
} catch (vscExceptionAutoload $e) {
// one of the objects we tried to instantiate couldn't be found
if (vsc::isTesting()) {
// show a stack trace
} else {
// show a 500 error page
}
}
The next example is a better one, from the abstract front controller object. Which, as you can see above, generates a vscResponse object, based on vscProcessor and vscRequest. The front controller can handle Exceptions generated at the processor level and treat them accordingly. Mostly there will be problems with the model or the processor throes Redirect Exceptions (which is generated by the processor). For example a custom Controller can be created to also handle failed authentication attempts and redirect to a login page, or change the response type to an Access forbidden one.
try {
$oProcessor->init();
$oModel = $oProcessor->handleRequest($oRequest);
} catch (vscExceptionResponseRedirect $e) {
// the processor wants to redirect
$oResponse->setStatus($e->getRedirectCode());
$oResponse->setLocation ($e->getLocation());
return $oResponse;
} catch (vscExceptionResponseError $e) {
// we had error in the processor or model - trying to fallback gracefully
$oModel = new vscEmptyModel();
$oProcessor = new vscApplicationErrorProcessor(); // the error is application generated, output a 500 response
$aStatusList = $oResponse->getStatusList();
$oModel->setPageTitle($e->getErrorCode() . ' - ' . $aStatusList[$e->getErrorCode()]);
$oModel->setPageContent($e->getMessage());
$oMyMap->setMainTemplatePath(VSC_RES_PATH . 'templates');
$oMyMap->setMainTemplate('main.php');
if (!$oMyMap->getTemplate()) {
$oMyMap->setTemplatePath (VSC_RES_PATH . 'templates');
$oMyMap->setTemplate ('500.php');
}
} catch (Exception $e) {
// an unknown exception, bubble it up
throw $e;
}
- [ permalink ]
