I recently have been asking question on exceptions and handling exceptions and such, which as recently best explained to me in this question. My question now is how would I use the
set_exception_handler();
in a class to set up a error handling class in php, that when ever an error is thrown is handled by this class. As the definition states:
Sets the default exception handler if an exception is not caught within a try/catch block. Execution will stop after the exception_handler is called.
I was thinking I could do something like:
class Test{
public function __construct(){
set_exception_handler('exception');
}
public function exception($exception){
echo $exception->getMessage();
}
}
But then the problem is that if the user is setting up the application or using any API from the application they have to do a whole: new Test();
So how could I write an exception handler class that is:
Automatically called when an exception is thrown to deal with the "uncaught" exception.
Done in an OOP way that is extendible.
The way I have shown is the only way I can think to do it.
For your class to work, the line inside your contructor should be:
// if you want a normal method
set_exception_handler(array($this, 'exception'));
// if you want a static method (add "static" to your handler method
set_exception_handler(array('Test', 'exception'));
Everybody thinks that using set_exception_handler would catch all the error in the PHP but it is not true as some errors are not handled by the set_exception_handler the proper way to handle all types of errors have to be done as:
//Setting for the PHP Error Handler
set_error_handler( call_back function or class );
//Setting for the PHP Exceptions Error Handler
set_exception_handler(call_back function or class);
//Setting for the PHP Fatal Error
register_shutdown_function(call_back function or class);
By setting these three setting you can catch all the errors of PHP.
Related
From my previous question I understand that isn't a good practice manage each exception with try/catch block, 'cause if I've hundred functions I should put hundred try/catch block.
Now I noticed the set_error_handler function, if I've understand correctly (never used it), allow me to swith in a file or function all the error generated in the whole scripts. So instead of put try catch block this function should automatically intercept the error and call a function, is right?
Now I already have a Log class that help me to write a stack trace in a file. This file is daily so I can see all system transaction in separated file.
My Log class is a SingleTon, so in each classes if I want write some trace in the log file I just need to do this:
Log::warning('some parameter here');
My goal is create an error.php file where all the error are switched in the Log::warning('...');. I think that this set_error_handler should be placed in the system core. As I said I never worked with it, someone could help me to achieve this with a bit example? I'll be glad.
set_error_handler is used to handle errors in a script not exceptions.
If you want to catch all exceptions from your application to apply the same process you have to call set_exception_handler PHPDoc.
This function takes a callable in argument, so your handler must be defined in another function.
The main difference between this function and a try catch block is that
Execution will stop after the exception_handler is called.
It's a also good practice to keep existing exception handlers possibly introduce by an included lib.
You can create a class to do this
class ErrorHandler
{
private $previousExceptionHandler;
public function registerExceptionHandler($callPrevious = true)
{
$prev = set_exception_handler(array($this, 'handleException'));
if ($callPrevious && $prev) {
$this->previousExceptionHandler = $prev;
}
}
public function handleException(\Exception $e)
{
// DO YOUR STUFF
if ($this->previousExceptionHandler) {
call_user_func($this->previousExceptionHandler, $e);
}
}
}
And to use it
$errorHandler = new ErrorHandler();
$errorHandler->registerExceptionHandler();
There are some good libs to do that and more, especially if you want to catch your exceptions for logging purpose. You can try the excellent Monolog lib wihich is widely used, and its ErrorHandler class
I want to create a class in my framework for exception handling.
How can write the class to be able doing exception handling without using try catch in my codes?
Here is a example:
I tried but couldn't write the code here
exception.php
class Soroush_Exception extends
//some code here
}
===============================
file1.php
$a = 2;
if ($a != 3)
{
throw new Soroush_Exception("Error");
}
//and here I want to show it without using catch with a static method
echo Soroush_Exception::show();
sorry for bad english
Use trigger_error function: php.net/trigger_error
Set a class' method as exception handler - it will catch any exception, that was not catched explicitly with a catch statement.
The problem is that exception stops execution of the script to the point where it is catched, and continue from the catch afterwards. If there is no catch, you still can execute code in the exception handler, but after the handler method the script will stop.
While attempting to implement some kind of error handling when my __autoload() function fails to load a file I stumbled upon this little 'oddity'.
According to http://nl.php.net/autoload Exceptions thrown from within the __autoload() function can be caught in a catch block since PHP version 5.3+.
Note:
Prior to 5.3.0, exceptions thrown in the __autoload function could not be caught in the catch block and would result in a fatal error. From 5.3.0+ exceptions thrown in the __autoload function can be caught in the catch block, with 1 provision. If throwing a custom exception, then the custom exception class must be available. The __autoload function may be used recursively to autoload the custom exception class.
This works perfectly for the type of error handling I had in mind. The example below works just like I want it to (It throws an exception and it's caught):
function __autoload($class) {
throw new Exception();
}
try {
new UndefinedClass();
}
catch (Exception $e) {
echo 'damnit, it no work!';
}
Output: damnit, it no work!
If however I try the same concept with a static method call from a undefined class, Exceptions are not thrown, instead I get fed a fatal error.
try {
$a = UndefinedClass::someRandomStaticMethod();
}
catch (Exception $e) {
echo 'meh, it no work!';
}
Output: Fatal error: Class 'UndefinedClass' not found in ***** on line 16
The code above does not work. No Exception is thrown. ( same __autoload() function is used ).
http://nl.php.net/autoload mentions nothing about this usecase and this leaves me wondering if I'm doing something terribly wrong here? How can I make my __autoload() function throw exceptions on static method calls of a non-existent class?
If this is simply not possible with the __autoload() function, does spl_autoload() allow this kind of Exception throwing?
#Galled
Based on the link you provided I changed the __autoload() function to this:
function __autoload($class) {
eval('
class ' . $class . ' {
};
');
throw new Exception('Im an Exception!');
}
Using this version the fatal error in question is no longer fed to my monitor. However, it will now feed me with a different fatal error: the fact that the someRandomStaticMethod() doesn't exist.
I could of course include the declaration of the method within the eval() call. But this is not workable solution, as I would have to redeclare every class my project contains within the __autoload() function just to be able to avoid said fatal error. It's also interesting to know that no Exception is caught, as it seems the fatal error occurs before the Exception is handled, if it is even thrown in the first place.
I somewhat managed to solve the problem by expanding on the suggestions from Galled. After some more reading, especially here: http://nl.php.net/manual/en/function.spl-autoload-register.php#84086 I wrote the following.
function __autoload($class) {
...
/* if something and/or everything fails */
eval('
class ' . $class . ' {
public function __construct() {
throw new Exception(\'Im an Exception!\');
}
public static function __callstatic($method, $arguments) {
throw new Exception(\'Im an Exception!\');
}
};
');
}
This variation will now throw Exceptions on both use-cases. regardless on the existence of methods within a class.
While the above works, I'm hoping someone can provide a solution that doesn't touch the eval() function. As, for some reason, I feel like I just molested PHP.
consider this code:
class TestClass {
function __construct() {
set_error_handler(array($this,'error_handler'));
}
function error_handler($errno,$errstr,$errfile,$errline,$errcontext) {
trigger_error("\ncode: [$errno]\nfile: [$errfile -> $errline]\nSTR: [$errstr]",E_USER_ERROR);
}
}
count($nonExistentVariable);
//notice was thrown above but script carry on
//but here error handler is registered and...
$anInstance = new TestClass();
// rest of the code is affected by it
count($nonExistentVariable); // user error is thrown
So is there a way to make the error_handler function to fire only when error is encountered inside an instance and do not mess globally with other code ?
You could check the call stack in your error handler (via debug_backtrace()) and determine if it's coming from within the class you're interested in. If it's not coming from the class be sure to return FALSE and PHP will fall back on it's default handler.
http://php.net/manual/en/function.set-error-handler.php
I witnessed some strange behaviour regarding PHP's exception handling in a recent project. Case goes as follows.
In my app, I use namespaces. All classes are in individual source code files. The code relevant to this particular case, is spread over 3 classes.
The "outermost" class is a dispatcher (or router), which wraps the dispatch call inside a try-catch block. The dispatched request, calls a method in a third class, which runs code (wrapped in a try-catch block), which causes an exception.
Because I had omitted a use Exception; statement in the class where the error happens, the thrown exception trickles all the way back to the outermost layer (the dispatcher), where it is caught - causing me to scratch my head why the catch around the code causing the error isn't working.
To me this seems strange. Logically, PHP should in this situation (IMO) throw a Class not found exception/error, leading me to the actual error in my code, instead of trying to "stay alive" as long as possible.
Should this be filed as a bug, or is this expected behaviour?
Edit: Code example
File: class-a.php
<?php
namespace hello\world;
class classA {
protected $b;
public function __construct() {
$this->b = new \hello\world\classB();
}
public function doSomething() {
try {
$this->b->throwException();
} catch (Exception $e) {
}
}
}
File: class-b.php
<?php
namespace hello\world;
class classB
{
public function throwException() {
throw new \Exception("bar closed");
}
}
File: run.php
<?php
include 'class-a.php';
include 'class-b.php';
$a = new \hello\world\classA();
$a->doSomething();
ClassB throws an \Exception in ClassB::doSomething(), for which ClassA has a catch-clause, but because ClassA doesn't declare use Exception or catch (\Exception), the catch doesn't match and execution ends with a Uncaught exception error1. But in my opinion, it should cause a Class not found error.
I might be expecting too much of the permissive PHP compiler, but it would help in tracking down silly errors that should be easy for the compiler to spot.
1 If the $a->doSomething() in run.php was surrounded by a try..catch clause, the Exception would (or at least could) be caught there, since it trickles down the stack.
PHP's exception catching mechanism does not validate that the class you catch actually exists.
It exhibits the same behavior when using typehinting in functions, so I suspect it merely converts the exception/function type hint into a string or something and compares that with the type of the relevant object.
Whether this is a bug or not is questionable. Personally I think it should be classified as a bug, but PHP has all sorts of wonky behaviors :D