Jump to content

Handling exceptions by loading a page with a safe message.


Go to solution Solved by Jacques1,

Recommended Posts

Hi All,

 

I have touched upon exceptions earlier. However I am still not sure if I am handling them correctly.

try {
      ...
      ...
      
      ...
    }catch(Exception $e){
     
	if($prod === true)  // In production mode
	{	
	     header("Location: exceptions/errors.php")     
             exit();
	}
	if($dev === true) // In development mode
	{	
	    echo $e->getMessage();
            // & if needed log the errors / exceptions into a file.
            exit();
	} 										
    } 

I would like to ask if using the function header() to load the errors.php page is a good and safe practice. Or is there a better way to load the errors.php.
 
If I load the errors page as in the snippet, do I also have to log the errors myself in some files or is php going to do that in any case.
 
Any improvements or suggestions are welcome. 
 
Thanks all !        
 
P.S. Googling exceptions gives loads of information but seldom does it touch the issue of loading an errors page when an exception occurs.
Edited by ajoo

It doesn't make sense to concentrate on exceptions in particular, because they're just one way of how PHP handles errors. In fact, large parts of the PHP core still rely on classical errors. You should take care of all errors instead. This automatically includes exceptions, because PHP turns unhandled exceptions into fatal errors.

 

Catching an exception in the code only makes sense if you have a concrete solution for a specific problem. This is very rare. 99% of the time, the problem cannot be solved at runtime, and the exception should simply be left alone.

 

It's also unnecessary to implement basic features like logging, error pages etc. yourself, because PHP can already do that. To enable logging, simply turn log_errors on and point error_log to the logfile. Error pages can be displayed by the webserver: If PHP encounters a fatal error while display_errors is disabled and no output has been produced yet, it will emit a 500 status code. The webserver can detect this code and show a custom error page.

 

Showing custom errors is very easy with modern webservers like nginx:

fastcgi_intercept_errors on;

error_page 500 /error_pages/5xx.html;

location /error_pages {
    internal;    # the error pages should not be publicly accessible
}

I think it's slightly harder with Apache.

 

For more detailed info, see The mystery of errors and exceptions.

HI, 

 

Would you be kind enough to explain the usage of the following two functions functions :

 

1.  exception_handler($e)  

2. set_exception_handler('exception_handler') 

 

I think they are analogous to the two functions  register_shutdown_function('handle_fatal_errors') & handle_fatal_errors()

 

Do we need to use just one set ? 

 

In case we use the exceptions_handler then what modifications would be required as has been explained for the errors' functions in your article? ( i.e. to say how would we set the auto_prepend_file directive and the fatal_error_handler.php code)

 

Thanks loads 

 

You don't need any exception handler. Exceptions are just special fatal errors, and those are either handled by the webserver (which is preferrable) or the shutdown function.

 

Would you be kind enough to explain the usage of the following two functions functions :

 

1.  exception_handler($e)  

2. set_exception_handler('exception_handler') 

 

I think they are analogous to the two functions  register_shutdown_function('handle_fatal_errors') & handle_fatal_errors()

 

Yes. If you're using PHP 5.4 or above (which you definitely should), you can actually pass an anonymous function directly to register_shutdown_function():

<?php

register_shutdown_function(function () {
    // the error handling code etc.
});

But like I said, the best solution is to let the webserver handle errors. A shutdown function is only required if you have a bad webserver which cannot do this (Apache?) or very complex error handling code.

Edited by Jacques1

Hi Jacques, 

 

Thanks for the reply. Yes I am using Apache and the xampp stack. The php version that I have installed is 5.6.8.

 

I just tried your code example in the article titled  "The mystery of errors and exceptions". I deliberately inserted a parse error and got the error on the screen

Parse error: syntax error, unexpected ')' in D:\xampp\htdocs\xampp\magics\index.php on line 3 

If the production environment was set then it would have suppressed this message as well and would have shown a blank screen.  Instead, all I want is to be able to show a simple message on a nice html page saying that an error has occurred and will be resolved soon, without divulging any technical details to the user. 

 

Since I also want to display a similar message for any run-time exceptions that may occur,do I also need to set an exceptions handler? I have used try - catch blocks around code that access the database and I want to catch database related exceptions in the catch block and retry a few times as also explained in one of your replies. I have used mysqli for the database. For any other run-time errors I simply want the exception handler to display a user friendly message before terminating the problem. 

 

Can errors and exceptions be handled using a common handler or should a simple one be written for exceptions too using a set_exception_handler function?  

 

I re-iterate that I do not wish to handle errors or exceptions any more than displaying a user friendly message to user, nor do i wish to generate any logs which the server does for us as you have already explained. I want these to be as simple as possible. Sorry if I am taking a long time understanding these. Just want to be doubly sure. 

 

Thanks again very much.

Edited by ajoo
  • Solution

Ideally, you shouldn't need any handler.

 

Disable both display_errors and display_startup_errors to simulate a production environment. Then create a script with a fatal error and watch the developer tools of your browser. You should see a blank page and a 500 response code. If this works, you have to make Apache intercept this error and display a custom error page. How do Apache and PHP communicate on your production server (not the XAMPP testing stuff)? Via FastCGI? mod_php? CGI?

 

If you don't want to (or cannot) intercept the error with the webserver, you should register a shutdown function as explained in the Dev Shed thread. This function can then render a user-friendly error message.

Edited by Jacques1
  • Like 1

Hi Jacques, Thanks for being so patient with me. Disabling display_errors and display_startup_errors worked exactly as you suggested. It generates an error 500 message which is caught by the error_handler and displays the custom message. It is also handling exceptions as you suggested by you. Disabling the two error messages and experimenting with errors and exceptions has given me a better insight into them. I don't profess that I know it all but still I feel that quite a few of my doubts have been cleared. 

 

WIll revert back with any more questions or queries if I have them. 

 

Thanks a lot. 

Hi Guru Jacques, 

 

I have another and related question on this & that is that is on extending the Exceptions class. 

 

So suppose it is wished to capture only the database related exceptions and we are using mysqli prepared statements in code which is predominantly procedural then if we subclass like

class DBExceptions extends Exceptions 

then how can this (extending of the Exceptions class as DBExceptions) be used to catch the mysqli related exceptions in the following code where a function 

LC($link)

 is called in a try catch block and any DBExceptions are caught in the catch block.

try
{
       $verify = LC($link);
       $vu = true;
}catch(DBException $e){    

           // do whatever on catching the DBException
}

function LC($con)
{
	if(isset($_SESSION['id'],$_SESSION['user_id'],$_SESSION['usr'],$_SESSION['login_string']))
	{
		$id = $_SESSION['id'];
		$user_id = $_SESSION['user_id'];
		$username = $_SESSION['usr'];
		$login_string = $_SESSION['login_string'];
		$user_browser = $_SERVER['HTTP_USER_AGENT'];
		$ip = $_SERVER['REMOTE_ADDR'];
		$query = "SELECT salt FROM loginstatus WHERE id = ? && status = 'ABA' LIMIT 1";
			$stmt = $con->prepare($query);
			$stmt->bind_param('i',$id);
			$stmt->execute()
			$stmt->bind_result($salt);
			$stmt->fetch()
			$LC = crypt($user_browser.$id.$ip, $salt);
			if(isset($_SESSION['login_string']) && $_SESSION['login_string'] == $LC) 
			{
				$stat = "Logged in !!";
				return true;
			}
	
	}else return false;	
}

Once again what I am trying to ask is that assuming the function 

LC()

can throw any kind of exception including DBException how will the catch block know which exception is the one that is in the category of DBExceptions and catch that only?

 

If I extend the Exceptions class as I have done above, then would I also be obliged to write some error / exception log function or would php continue to do that by itself?

 

I wish to do the bare minimum, as advised by you and leave all the error / exception handling to php. I just want to make sure that if the exception / error is database related then the program is terminated with an appropriate friendly message on a nice page to the end user. 

 

P.S.  I know the function is using a few things like HTTP_USER_AGENT & REMOTE_ADDR which you advise against but kindly overlook them since I am using this code as an example in support of my question. 

 

Thanks loads.

PHP takes care of all unhandled exceptions, including custom exceptions which you defined yourself. So, no, you don't need any error handling code for them.

 

MySQLi can actually throw its own exceptions, but you have to enable this feature:

<?php

const DB_HOST = 'localhost';
const DB_USER = '...';
const DB_PASSWORD = '...';
const DB_NAME = '...';
const DB_CHARSET = 'UTF8';



// enable exceptions in the MySQLi driver
$mysqliDriver = new mysqli_driver();
$mysqliDriver->report_mode = MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT;

$databaseConnection = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);

// try an invalid query
$databaseConnection->query('THIS IS NOT SQL');

In development mode, you should now see a fatal error caused by a mysqli_sql_exception.

 

So you don't need your own exception class for database-related errors. When you do create custom exceptions, note that you have to throw them yourself (since PHP obviously won't do it). For example:

if (file_put_contents($templatePath, $template) === false)
{
    throw new TemplateException('Failed to create template file '.$templatePath.'.');
}

Hi Jacques,

 

Thanks for the last reply and I have been trying out that code snippet to understand exceptions. 

 

From This :

try	{
		$con = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
		$query = "Select ID, UserLogin from test where SNo = ?";
		$stmt = $con->prepare($query);
		$stmt->bind_param('i',$sno);
		
			if($stmt->execute())
			{
				// throw new exception("HAHAH");
				$stmt->bind_result($ID,$user);
				$stmt->store_result();
				$stmt->fetch();
				echo " WOW ";
			}	
	}catch(mysqli_sql_exception $e){
		echo $e->myMessage." NO GO";
		
	}		

I observed that only 

$con = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
		$query = "Select ID, UserLogin from test where SNo = ?";
		$stmt = $con->prepare($query);
		$stmt->bind_param('i',$sno);
		
			if($stmt->execute()) 

this part of the code produced mysqli_sql_exceptions type exceptions. Even there the bind_param produced the error if I created a mismatch in the number of parameters. 

 

The rest of the lines 

        $stmt->bind_result($ID,$user);
	$stmt->store_result();
	$stmt->fetch();

did not produce any mysqli_sql_exceptions type exceptions even though it gave warnings, with display_errors directive turned on, when i deliberately added an extra parameter in bind_result or added a parameter to store_result().

 

I gather from this that the last 3 lines of code above would not generate an exception of type mysqli_sql_exceptions which are generated only by the other set of initial lines of code ( till and including $STMT->execute() ) and I guess those are the lines that would actually throw the true kind of mysqli_sql_exceptions that are caused by query failure due to whatever reason. Others commands simply manipulate the retrieved data.  Is that correct ? 

 

Thank you very much.

Edited by ajoo

It's a design decision of the mysqli authors. They appearently think that passing the wrong number of variables to bind_result() isn't a severe problem (I disagree), so they merely trigger a warning instead of throwing an exception. It doesn't have to be like that. An exception would actually make sense.

 

So there are no definite rules for when to throw an exception in PHP. It depends on the person writing the code.

Hi, 

 

Just one last thing, 

 

 

 

If you’re using Apache, you need to do some handiwork. Since Apache doesn’t intercept 500 errors issued by PHP, you’ll get a blank page by default. Any custom error page must be sent by PHP itself.

 

1. What's the mechanics of sending this page on a local server running apache. Should the page be created in the fata_error_handler.php  itself or should it be created separately and redirected to it.  OR  is there a directive in one of the config files on the server that points to some error page by default so that there is no need to make any error page as well.

 

By the way I am using the Amazon aws servers and the server uses Amazon Linux. I am not aware 

 

 

 

How do Apache and PHP communicate on your production server (not the XAMPP testing stuff)? Via FastCGI? mod_php? CGI?

 

Hmmm I am not aware of this? Where can I find out about it?

 

Kindly take some time out to revert to a query that I had sent on your personal messenger on phpfreaks. 

 

Thanks again very much.

Hmmm I am not aware of this? Where can I find out about it?

Create a page which calls the phpinfo() function. It will tell you how PHP is configured with apache. Look for the row beginning with Server API and see what it's value is. My server for example is setup to use FPM with FastCGI and show FPM/FastCGI as the server API. I'm not sure exactly what other setups would show, as it's been a long time since I used any other configuration.

Hi Kicken and Guru Jacques, 

 

Thanks for the inputs. My SERVER API shows as Apache 2.0 Handler on my production server. 

 

I tried but could not find the file that holds the ProxyErrorOverride directive. Please enlighten. 

 

Thanks loads. 

“Apache 2.0 Handler” means that you're not using FastCGI, so ProxyErrorOverride is not available. That leaves you with only two options: Either you change the webserver setup and switch to FastCGI, or you give up and use the shutdown function hack to render the error pages. The webserver itself cannot do that in your current setup (as far as I'm aware).

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.