I have following piece of code:
function doSomething()
{
try {
doSomeNastyStuff() // throws Exception
} catch(\Exception $e) {
if ($this->errorHandler) {
call_user_func($e);
} else {
throw($e);
}
}
}
However, the catch block does not work. The stack trace shows me the error happened at the line doSomeNastyStuff(). Where is the problem?
The problem is, you are rethrowing your Exception. The stack trace is part of the Exception instance and is recorded at the moment, exception is created. You can get the stack trace by
$e->getTrace(); // Exception $e
When you rethrow exception in your code, it still has the old stack trace recorded and this tricks your framework to show you, the exception actually happened at the line doSomeNastyStuff() and it seems like the catch does not work.
Therefore, it is better idea to rethrow exceptions the following way:
/** instead of throw($e) do */
throw new \Exception("Unhandled exception", 1, $e);
Beginining with php5.3, Exception constructor has optional third parameter $previous exactly for this purpose. You can then get the previous Exception using $e->getPrevious();
Related
I want to know what errors can be thrown that I am not catching, for dynamic code - not static code. For example, my code may run without throwing any Exceptions for 10 years and then throw UncaughtException
I want to specifically (non-generically) catch every type of Exception that can be thrown by the methods I am using. How can I know what Exceptions MAY be thrown by these methods?
I want to non-generically catch every type of error that can possibly be thrown for a section of PHP code.
Examples of exceptions that may be thrown:
PDOException
ExpiredException
Now I have this around everything:
try{
...
}catch(Exception $e){
...
}
I'd like to replace it with something like this:
try{
...
}catch(PDOException $e){
...
}catch(ExpiredException $e){
...
}catch(Exception $e){
...
}
I'd like to be confident that I am catching all different kinds of Exception that can be thrown by the methods in the section of code
And if I catch all Exceptions individually, will it be safe to remove this part?:
catch(Exception $e){
...
}
Or are there methods which will simply throw Exception?
My solution preference list (1 is the most-preferred solution):
1: A flag I can turn on that will cause php.exe to warn me about each and every possible type of Exception that is not specifically being caught
2: A way to individually check each method and see what errors can be thrown. Is the documentation the only way to check? or is there some IDE or PHP block that will tell me which Exceptions may be thrown by individual methods?
You can set callback function by using register_shutdown_function() which will call on every end of your php code execution. In this callback function you can check whether any error occurs or not using error_get_last().
For Example:
// Register shutdown function
register_shutdown_function("shutdownTracker");
// Define all error types you want to catch and handle
define('E_FATAL', E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR);
function shutdownTracker() {
$error = error_get_last(); // This will return empty if no error occurs while executing php code.
if(!empty($error) && ($error['type'] & E_FATAL)) {
// Write your code here to handle you error
}
}
Note: You should include this code on top of your code.
Well if you are talking exceptions, you already have the answer
try{
...
}catch(PDOException $e){
...
}catch(ExpiredException $e){
...
}catch(Exception $e){
echo get_class($e); // get's the class of unrecorded exceptions.
//catches any exceptions missed by the above
}
Now if you are talking about "errors" you can do is use a custom error handler
if(!function_exists('myErrorHandler')){
function myErrorHandler($severity, $message, $file = 'UNKNOWN', $line = 'UNKNOWN')
{
throw new ErrorException(
$message,
1,
$severity,
$file,
$line
);
}
set_error_handler('myErrorHandler');
}
What it does is convert all PHP errors to exceptions or rather to ErrorExceptoins.
Now you can go a step further and use these other two functions.
register_shutdown_function
AND
set_exception_handler
I'm actually working on porting what I use to it's own stand alone github project. Right after I finish my eJinn project, which you may be interested in. eJinn is designed to build exception classes based off a config file, so you can have one error per exception file and unique error codes in a project.
You can catch all exceptions without even call try.
function hello($e)
{
if ($e instanceof PDOException){
echo "something".
}else echo $e->getMessage();
}
set_exception_handler('hello');
this should catch all exceptions.
UPDATE 2
I've edited the code so you know what exceptions will be thrown using get_class();
class mycustomException extends Exception{} // making new exception
function hello($e)
{
if ($e instanceof PDOException){ // exception already known
echo "something";
}else{
echo get_class($e); // get exception name.
}
}
set_exception_handler('hello');
throw new mycustomException(); // throw exception that we made.
In C#, doing the following would destroy the stack trace of an exception:
try{
throw new RuntimeException();
}
catch(Exception e){
//Log error
//Re-throw
throw e;
}
Because of this, using throw rather than throw e is preferred. This will let the same exception propagate upwards, instead of wrapping it in a new one.
However, using throw; without specifying the exception object is invalid syntax in PHP. Does this problem simply not exist in PHP? Will using throw $e as follows not destroy the stack trace?
<?php
try{
throw new RuntimeException();
}
catch(Exception $e){
//Log error
//Re-throw
throw $e;
}
When you throw $e in PHP like you did you rethrow the exisiting exception object without changing anything of its contents and send all given information including the stacktrace of the catched exception - so your second example is the correct way to rethrow an exception in PHP.
If (for whatever reason) you want to throw the new position with the last message, you have to rethrow a newly created exception object:
throw new RuntimeException( $e->getMessage() );
Note that this will not only lose the stack trace, but also all other information which may be contained in the exception object except for the message (e.g. Code, File and Line for RuntimeException). So this is generally not recommended!
Re-throwing the same exception will not destroy the stack trace.
But depending on what you need, you might want to just throw the same exception or build an Exception Chaining ( see PHP Documentation > Exception::__construct )
A very good explanation of when and why one would choose one approach over another is given in this answer
Yes.This is a best way to catch the exception and re-throw the same exception object which carries the stack-trace data. Once you reaches the point of method that handles requests, just catch it there and send response back to user accordingly.
Its a bad idea to throw a new exception object which looses the stack-trace and create an additional object causing memory load.
Hope this helps.
I suppose that the exception system of PHP will catch all. but it doesn't.
try{
$obj = new Asdfasdfasdf()
} catch(Exception $e){
trace(...something...)
}
But it doesn't catch this kind of error, and I have searched php document, which didn't say which kind of exception/error is catch-able in try,catch clause.
So, how can I know that which kind of exception/error will be catched by my catch clause ?
P.S.
I finnally understand the 'error' from php engine is not the 'exception' from use land code. If you want use exception to handle engine 'error', you should manually wrap all 'error' in exception.
If you want to throw an Exception in the event that a class does not exist it, you could use class_exists().
A naive example might look something like:
function createClass($class)
{
if (!class_exists($class)) {
throw new Exception(
sprintf('Class %s does not exist', $class)
);
}
return new $class;
}
try {
$asdfasdfasdf = createClass('Asdfasdfasdf');
} catch (Exception $e) {
echo $e->getMessage();
}
From my experience, most PHP frameworks throw some sort of exception when a class is not found - for example, Symfony2 throws a ClassNotFoundException. That said, I don't know if you can 'catch' that, it's probably really just a development aid.
PHP 7 has just been released and from what I understand from the spec, you will be able to catch a fatal error as an EngineException. I don't know if it would work for your example; I haven't tested it because I have not installed PHP 7 stable yet. I tried your example with an alpha release of PHP 7 on an online REPL, and it appears that it does not work.
However for completeness, here's an example from the RFC:
function call_method($obj) {
$obj->method();
}
try {
call_method(null); // oops!
} catch (EngineException $e) {
echo "Exception: {$e->getMessage()}\n";
}
// Exception: Call to a member function method() on a non-object
In any case, as noted by #MarkBaker and #MarcB in the question's comments, you cannot "catch" a fatal error in previous versions of PHP.
Hope this helps :)
I got a try-catch block in my php application like this:
try {
if ($userForgotToEnterField) {
throw new Exception('You need to fill in your name!');
}
...
doDifferentThingsThatCanThrowExceptions();
...
} catch (ExpectedException $e) {
$template->setError('A database error occured.');
} catch (Exception $e) {
$template->setError($e->getMessage());
}
I would like to only output $e->getMessage() for the exceptions I have manually thrown with a custom error text and not the ones that have been thrown by the other code as these might contain sensitive information or very technical info that the user should not see.
Is it possible to differentiate from a manually thrown exception and a random exception thrown by some method without using a custom exception class?
I agree that it might be best to just write your own exceptions. If for whatever reason you don't want to, you could set a custom error message and a custom error code (the second parameter for the Exception constructor.) Check each thrown Exception if the error code is yours, and display only those:
public Exception::__construct() ([ string $message = "" [,int $code = 0[, Exception $previous = NULL ]]] )
and then use getCode
I've thought about this a bit and I'd say that what you are doing DOES call for a custom exception class. If you want to get around it (which in the end is going to be more confusing), you would basically create a global (or same-scope) variable that all exceptions can modify, and in your throw block flag it.
$threwCustomException = false;
try {
if ($userForgotToEnterField) {
throw new Exception('You need to fill in your name!');
$threwCustomException = true;
}
...
doDifferentThingsThatCanThrowExceptions();
...
} catch (ExpectedException $e) {
$template->setError('A database error occured.');
} catch (Exception $e) {
if($threwCustomException){
//Whatever custom exception handling you wanted here....
}
$template->setError($e->getMessage());
}
That's the best I can think of. However, this is a bad idea, and it's the whole reason you are allowed to create your own exception classes. I know you're not looking for this answer, but since you look like you're trying not to create a TON of extra code, I would just extend Exception to "CustomException" or some other name specific to your project, and throw that for all cases, and handle it that way.
Hope that helps.
catch is not working because there is installed an exception handler using set_exception_handler()
I need "catch" to work, so I guess I need to unset the exception handler somehow. Things such as set_exception_handler(NULL) isn't working.
Any ideas how to unset the exception handler?
function my_exception_handler($exception) {
error_log("caught exception: " . $exception->getMessage() );
}
set_exception_handler("my_exception_handler");
// QUESTION: how does on unset it ?
//set_exception_handler(NULL);
try {
throw new Exception('hello world');
error_log("should not happen");
} catch(Exception $e) {
error_log("should happen: " . $e->getMessage());
}
Actual output:
caught exception: hello world
Desired output:
should happen: hello world
restore_exception_handler, which is linked from the manual entry for set_exception_handler.
BTW, these exception handlers should only come into play when an exception is uncaught. A catch block should always have precedence.
Reading a little bit in the comments on the Exceptions page brings you to this bug and this bug. They describe exactly what you experience, Exceptions can't be caught when a custom error handler is defined.
Solution:
Fixed in 5.3 and HEAD, won't be backported to 5.2.
The function is restore_exception_handler. However, the handler should only be called when an exception is unhandled. It does not disable catches.