My PHP web application is divided into modules, and I use the data model and data mapper patterns. My mapper methods are static, and they exist specifically to interact with the database.
I have the following method:
ModuleMapper::getRecordCountByModuleIdAndSiteId($moduleId, $siteId)
This method is only meant for a set list of modules (I have a good reason). Would it be reasonable to throw an exception if this method is called for a module outside of that set list (eg. "Record count for this module cannot be retrieved.").
try {
$recordCount = ModuleMapper::getRecordCountByModuleIdAndSiteId($moduleId, $siteId);
}
catch (Exception $e) {
// handle exception
}
Another option would be to just return 0.
Depends on how you want to handle errors really. I use zend framework which automatically catches exceptions and forwards them to the error controller for pretty display and logging. Obviously that method will kill processing unless you catch it explicitly. But it simplifies error display and logging.
You could just as well return 0 and do an if statement to display an inline message, or catch the error and display an inline message as well.
Related
How to display notice or success message during the import by the Convert Adapter ?
I can only display error by
Mage::throwException($message)
.
Class responsible for this is Mage_Adminhtml_Block_System_Convert_Profile_Run.
Magento actually have some kind a session message stack. (Something quite similar to the message bags you can find, for example on Symphony).
Those are quite easy to use, just get the session associated to the area you are in and use on of the functions addError, addWarning, addNotice or addSuccess.
Since you are on the data flow module, the session you are looking to is dataflow/session. Take care to get this model via a singleton otherwise, you will end up with odd multiple sessions.
Mage::getSingleton('dataflow/session')->addSuccess($this->__('This will add a success message'));
The other are :
Mage::getSingleton('dataflow/session')->addNotice($this->__('This a notice'));
Mage::getSingleton('dataflow/session')->addWarning($this->__('That a warning'));
Mage::getSingleton('dataflow/session')->addError($this->__('And finally an error'));
And the reason you actually get an error message when you throw an exception, is because somewhere on Magento core, there is a
try {
// the code or class instantiation on which you throw your exception happens here
} catch (Mage_Core_Exception $e) {
Mage::getSingleton('dataflow/session')->addError($e->getMessage());
}
I'm trying to add my custom pages. I'm using Kohana 3.3. The official documentation states that I should ovveride the method hander of native Kohana_Exception class. This is pretty easy to do so I've done that. Now I'm expecting that Kohana would call that method every time an exception or an error occurs. But this is not the case. I've found 2 catch blocks where an exception is caught inside execute_request method of Kohana_Request_Client_Internal class.
First catch
catch (HTTP_Exception $e)
{
// Get the response via the Exception
$response = $e->get_response();
}
Second catch
catch (Exception $e)
{
// Generate an appropriate Response object
$response = Kohana_Exception::_handler($e);
}
As you can see, none of the catch blocks calls handler method overriden by me.
Setting your own exception handler set_exception_handler has no effect since it is applied only to uncaught exceptions and the exceptions like 404 are thrown and caught.
No problems with run-time errors though. This block catches them and explicitly calls overriden handler method.
if (Kohana::$errors AND $error = error_get_last() AND in_array($error['type'],
Kohana::$shutdown_errors))
{
// Clean the output buffer
ob_get_level() AND ob_clean();
// Fake an exception for nice debugging
Kohana_Exception::handler(new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line']));
// Shutdown now to avoid a "death loop"
exit(1);
}
So my question how do I set up everything to have custom error page for Exception and HTTP_Exception?
PS. I can ovveride HTTP_Exception_404 and HTTP_Exception_500 to have my custom error page displayed, but I don't think that's the best option since it could work for these two, but overriding all possible HTTP_Exceptions is not a good way to go.
PS2. Or I can set my custom view in bootstrap.php:
Kohana_Exception::$error_view = 'custom_error.tpl';
Also don't like that solution.
All links to Kohana's documentation in this post are for version 3.3
You won't get what you want by overwriting just one method. Below I explain the methods that you can overwrite in order to achieve what you want. Just make just sure you put the right code in the right method.
Don't try to do everything in one place. While it will be all in one place is will most likely become a mess.
Kohana_Exception::handler()
Kohana_Exception::handler() is for when exceptions reach the exception handler or like you showed, in the shutdown handler. The last chance you have to display a nice error page in production environments. It outputs the result of Kohana_exception::_handler(), which is a Response object, and is therefor not suited to be called inside Request_Client_Internal::execute_response().
For production: Log the original exception. Since this scenario matches the description of HTTP Status Code 500 (Internal Server Error) Kohana_Exception::handler() should display a 500 error page.
During development you probably want to call parent::handler().
Kohana_Exception::_handler()
Kohana_Exception::_handler() return a Response object, so it is suited to be called in Request_Client_External::execute_response(), Kohana_Exception::handler() and View::__toString().
HTTP_Exception::get_response()
HTTP_Exception::get_response() will get called for exceptions extending HTTP_Exception, with the exception of exceptions extending HTTP_Exception_Expected. A few examples of exceptions extending HTTP_Expected_Exception are the 3xx and 401 HTTP_Exceptions.
By default it returns Kohana_Exception::response().
Overwrite it in specific exceptions to return specific responses for said exception.
Overwrite it in HTTP_Exception when you want the replace the default response.
Kohana_Exception::response()
Kohana_Exception::response() is responsible for collecting the data needed to render the Kohana_Exception::$error_view template. An example of output can be seen on the kohana/errors page of the userguide.
Change Kohana_Exception::$error_view when you want a different layout for the same data.
Overwrite Kohana_Exception::response() to replace the entire thing.
PS. Kevin pointed you to the 3.2 documentation. How to do this is very different for 3.2 and 3.3.
Building upon a question I already asked regarding exceptions, I fear that I might be writing php functions wrong then, or abusing the use of exceptions. The reason I say this, is because if custom exceptions are to be caught using try/catch blocks then the following function:
public function get_specific_page($page) {
if (!is_array( $this->_page )){
throw new AisisCore_Template_TemplateException( "<div class='error'>Trying to get a property from a non array.</div>" );
}
return $this->_page[$page];
}
Would then be called such as:
try{
get_specific_page($page);
}
catch(Exception $e){
echo $e->getMessage();
}
The problem with this approach is that I have many functions that are written like this, either checking to see if a file exists, throwing an error. Checking to see if a value is set in an array, throwing an error and my issue is that the file which deals with these function calls may become over loaded with try catch.....
So my question is, how would I better write functions like this so that I don't have php files over loaded with try catch statements, yet still be able to have y own custom functions.
Is it as obvious as writing the try catch inside the function it's self?
The reason I ask, if because I am use to working with fameworks and in companies where we write our functions as you see above. How ver I have worked with code bases that have tons of these functions and I dont see half the files that are useing them doing a bunch of try catches...
Update:
I was looking through zend source to better understand exceptions and came across this:
public function setMessage($messageString, $messageKey = null)
{
if ($messageKey === null) {
$keys = array_keys($this->_messageTemplates);
foreach($keys as $key) {
$this->setMessage($messageString, $key);
}
return $this;
}
if (!isset($this->_messageTemplates[$messageKey])) {
require_once 'Zend/Validate/Exception.php';
throw new Zend_Validate_Exception("No message template exists for key '$messageKey'");
}
$this->_messageTemplates[$messageKey] = $messageString;
return $this;
}
You can see how they throw a new exception message near the bottom, this function is not called by doing:
try{}catch(){}
yet when it throws an exception, there is no issue with "uncaught exception with message"
In my opinion, your approach is correct in general. However, a few notes:
You should refrain from using HTML formatting in exception messages. Generally, you don't know how the exception that you throw will be handled. For example, an exception handler could just write the message to a log file (you don't want HTML formatting then), present it to the user in a special error view (in which case the view itself should contain the HTML formatting), or simply ignore it (no need for formatting then, anyway).
Catch only exceptions that you can handle. If you know that your function throws an AisisCore_Template_TemplateException, you should just catch that exception and let all other exceptions bubble up to an exception handler that can handle them. You can use set_exception_handler to define such an exception handler that catches all uncaught exceptions by default (this is probably the case in your example from Zend Framework). Plainly put: Only catch exceptions in places where you know how to handle them.
Only use exceptions as what the name implies: to handle (unexpected) exceptions in your control flow. Using exceptions to control the regular flow of your program is possible, but generally considered bad design (just as a side note, your code samples look alright).
For the sake of completeness, some alternatives to using exceptions:
Use return codes instead of exceptions. This is old-school C-style. The advantage is that you don't need to wrap statements with try/catch-statements. However, you have to check the return values of each procedure, which is easy to forget. When using exceptions on the other hand, you reduce the risk of unexpected errors, since uncaught exceptions produce a fatal error per default.
Use PHP errors. See the trigger_error function for this. Custom errors are however nearly impossible to catch in PHP (except by using set_error_handler, which only works at global level).
I'm trying to develop a personal MVC framework for learning purposes. But every time I'm stuck in this problem: errors.
I feel like I'm handling them very bad. Currently I have an exception system (everything is converted to exception, even PHP triggered errors) that is catch in a try{} block that contains every line of code of the framework and the user application.
I'm treating errors such as "controller not found" or "action not found" like any other, for example "unable to connect to the database". But I feel like the latter is somehow more an "exception" rather than a pretty common "controller not found (404)".
Also currently I'm using an error handling that pretty much copy the way MVC works in my framework, in the sense that when an error occurs I load a specific action and load a specific view file for each type of error. I'm not using the MVC (by MVC I mean all the mechanism that load a controller, run an action, load a model and views for the user application) of my framework because an error in the MVC could cause an error to be triggered, which would try to manage it with the MVC which would trigger the same error again and then the MVC to be loaded again and so on in an infinite loop.
How should I handle every error of my framework? What are the best practice right now?
The execution of controller IMHO can generate two exceptions:
not found: when controller or method is missing
permission denied: when ACL blocked access
To handle this, i would just go with something like following code. And you can use multiple catch block.
try
{
$controller->$command($request, $response);
}
catch(AccessDeniedException $e)
{
$controller = new ErrorController;
$controller->accessDenied($request, $response);
}
catch(NotFoundException $e)
{
$controller = new ErrorController;
$controller->notFound($request, $response);
}
You can let AccessDeniedException to bubble up from Model Layer too, but usually it is a bad practice. Exception should be handles within same level of abstraction, where it was thrown, or, in case of critical exceptions (when object itself is unable to deal with it), the exceptions might penetrate ONE abstraction boundary. And exceptions should NOT leave the Model Layer, instead they should create error state in the layer, and be processed in your current View instance.
The point is this: instead of magical handler for all errors, you should handle errors close to the place where it originated.
You can do something like a more proper message at the try catch. For example:
try
{
//Your code here
}
catch (Exception $e)
{
// Clean the output buffer if one exists
ob_get_level() and ob_clean();
// Display the exception text
echo sprintf('%s [ %s ]: %s ~ %s [ %d ]', get_class($e), $e->getCode(), strip_tags($e->getMessage()), $e->getFile(), $e->getLine())."\n";
// Exit with an error status
exit(1);
}
Few days ago I deal with errors like this...
exit( 'Error!' );
or exit( 'Error!' );
Doh, right? =] Now I'm trying to learn Exceptions. This is how far I got...
http://pastie.org/1555970
Is that correct use of them? It would be cool that I can have more info about 'em. Like file where exception is thrown and line of that file. I know that there are build-in methods (in Exception class), but I want to somehow extend it so I don't need to write...
throw new My_Exception( '...' );
catch( My_Exception $e ) {
echo $e->my_method();
}
...but use old syntax.
throw new Exception( '...' );
catch( Exception $e ) {
echo $e->getMessage();
}
...or maybe you have any greater thought of Exceptions? How to deal with them? Help me! =]
In general you only need to echo/log exceptions, that cannot be otherwise handled. This pretty much means, that if you put your entire application within try block, there's only one place where you need to put echoing/logging logic (i.e. the catch block associated with the outermost try block).
If on the other hand the exception can be handled without stopping the application (in your example this could be providing a default numeric value, instead of incorrect value), then there's usually no need to echo/log it.
If you do want to log such exceptions (for debugging for example), then your application should contain a logging framework, so that it's enough to do in your catch blocks something like
} catch (Exception $e) {
ExceptionLogger::log($e); //static method == ugly, but it's for simplicity in this example
// do whatever needs to be done
}
log() method above would check if the logging is enabled, and if it is savenecessary data to a file.
Exceptions should be typed based upon the error that you find. The Spl Exceptions are a good start, but you really should be creating your own exceptions as well. Some common ones that I use:
FileNotFoundException extends RuntimeException <- self explanatory
HTTPException extends RuntimeException <- Used for http classes when a non-200 result is encountered
DatabaseQueryException extends LogicException <- Used for database query errors
Now, by typing them specifically, it lets you handle the errors in your code. So let's say that you want to fetch a HTTP resource. If that fails with anything but a 404, you want to try a backup URL. You could do that with:
try {
return getHttp($url1):
} catch (HttpException $e) {
if ($e->getCode() != 404) {
try {
return getHttp($url2);
} catch (HttpException $e2) {
//It's ok to ignore this, since why know it's an HTTP error and not something worse
return false;
}
} else {
return false;
}
}
As far as your example code that you posted, I would change a few things:
Change the thrown exception to InvalidArgumentException since it has more semantic meaning (I almost never throw a raw exception).
You should try to avoid catch(Exception $e) at all costs. You have no idea what exception was thrown, so how can you possibly handle it?
Only catch exceptions that you are reasonably sure you know how to handle (and outputting an error/logging is not handling, it's removing the usefulness of the exception). You should never see something like catch($e) { logerror($e); } or catch($e) { print $e->getMessage(); } since netiher is actually handling the exception.
If you don't fix or workaround the cause of the exception in your catch block, you should re-throw it. Let the code above you in the stack try to handle it. This is especially true with libraries and classes that are reused all over the place.
Now, with user interfaces, it may be acceptable to catch the exception and show the user an error message. So your example where you print the exception's message might be ok, but you'd really need to think about the use-cases of it. Are you calling it from a model or controller? If so, it may be ok display an error message. Are you calling it from a library? If so, it's probably better to let the exception bubble up.
Also, don't use a global try{} catch() {} block. Instead, install an Exception Handler to handle it for you. It's cleaner, and more semantically correct (since any try{}catch{} implies that you know how to handle the exception that you caught, whereas the exception handler is precisely meant for exceptions that weren't handled because you didn't know how to handle them.
Exceptions are for exceptional circumstances. Do not use them for all error conditions. If a user submits a password that's too short, don't throw an exception, handle that in validation. But if your hash function is expecting sha256 to be available and it isn't, that's a time for an exception. Exceptions are useful for program errors (when a condition that is unexpected happens, such as invalid input to a function), state errors (when the application enters a state that is unknown or unstable, such as if the requested view does not exist) and runtime errors (when the application encounters an error that can only be detected at runtime, such as a file-not-found error).
There is an entire page of the PHP manual devoted to extending exceptions and that page also gives you a lot of information on the methods to identify file/line number, backtrace etc. where the exception was thrown. This is the type of information that is extremely useful for debugging.