I am writing a PDO wrapper and having this issue with catching exceptions.
I am trying to maintain exception safety practice,so I wanted to know how to catch an exception,writing it to a log file,and using exception safety to to do something, probably tell the user to retry action again or navigate to an error page or whatever (anything you can suggest).
So how is it done if possible?
Look at this code snippet, it shows how it is done:
class MyDb extends PDO
{
protected $error;
function __construct( $logger )
{
try {
parent::__construct( 'mysql:host=localhost;dbname=xxxx', 'xxx', 'xxx' );
$this->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
}
catch ( Exception $e ) {
$this->error = $e->getMessage();
// do your log writing stuff here
$logger->Add( $this->error );
}
}
}
update:
usage of the class:
$logger = new MyLogger();
$db = new MyDb( $logger );
of course, you need a logger class with a add method:
class MyLogger
{
const FILENAME = '/tmp/mylog.txt' ;
function Add( $error )
{
file_put_contents( self::FILENAME, $error, FILE_APPEND);
}
}
Related
I know this is a weird on, but in my code, i have development mode errors, and production mode errors. This is the function i have:
private function error($message, $mysql_error = null){
if( DEVELOPMENT_MODE ){
$exp = new Exception();
$trace = $exp -> getTrace();
array_shift( $trace ); // removes this functions from trace
$data["Error Mg"] = $message;
$data["MySQL Er"] = ( is_null ( $mysql_error ) ) ? "" : $mysql_error;
array_unshift($trace, $data );
fkill( $trace ); // formats array and then dies
}
else{
throw new Exception ( $data );
}
}
I wrote this function in my database class, so that if an error happens, I don't have to provide the check if we're in development mode or not!
So I thought I could externalise the re-usable code. However, because I'm throwing an exception from this function, I'm basically just using a function, that will return a thrown error. Pretty useless in production mode.
I would have to do this every time i want to use it:
try{
$this -> error( "Invalid Link After Connect.", mysql_error () );
} catch ( Exception $exp ){
throw $exp;
}
RATHER THAN JUST
$this -> error( "Invalid Link After Connect.", mysql_error () );
so to avoid writing a try ... catch block for every error function I want to call... is there any way to throw the exception 2 levels up?
An exception will automatically travel up the call chain until it reaches the highest level. If it's not caught there, program execution terminates due to an uncaught exception. The whole point of exceptions is to be able to have errors bubble up. You don't need to throw harder or do anything special to "throw it up 2 levels", that's what it does by definition.
Just omit the try/catch block. Exceptions automatically propagate up as far as they can until something catches them; you don't need to explicitly re-throw them at every level of the call stack.
This...
try{
$this -> error( "Invalid Link After Connect.", mysql_error () );
} catch ( Exception $exp ){
throw $exp;
}
is exactly equivalent to this:
$this -> error( "Invalid Link After Connect.", mysql_error () );
Use Multiple catch Blocks
use admin table which has field
Mode Value
0 Production
1 Debug
the first catch which matches the exception is executed
Example
try {
if (!$bDBConnection && $row['mode'] ==0 ) {
throw new Produciton_DBException("Problem with Database");
}
else
{
throw new Debug_DBException("Problem with Database");
}
}
catch(Produciton_DBException $e)
{
// display suitable error messages
}
catch(Debug_DBException $ex)
{
// Exception falls through until a match is found
}
I have to develop an exception handler that should handle like 5 different type of exceptions. Let's call them simply Ex1, Ex2, Ex3...
I though of doing a single class called ExHandler which will be instantiated like this:
...
} catch (Ex1 $e) { $h = new ExHandler($e); $h->render(); }
catch (Ex2 $e) { $h = new ExHandler($e); $h->render(); }
catch (Ex3 $e) { $h = new ExHandler($e); $h->render(); }
...
And inside ExHandler manage each different Exception differently using $e instance of Ex1, $e instance of Ex2, $e instance of Ex3...
But It doesn't seems a very good practice to me. Is it good? Is there any other way of doing this?
Should I create an Ex1Handler, Ex2Handler, Ex3Handler...? My S.O.L.I.D spirit tells me something is just wrong here. What is it?
I need to note before I answer this, that procedural programmers will look at this and think it's dumb :) but I can live with that, this is assuming an OOP application with HTML templating that outputs after the output_buffer is cleaned.
I always create a try/catch block encompassing the majority of my code in one call usually at the point where I start requiring other files as well as starting an output_buffer whilst in development.
ob_start();
try {
switch($appPage) {
case('work'):
require_once('im_bored_at_work.php');
break;
case('home'):
require_once('im_a_little_less_bored_at_home.php');
break;
default:
require_once('on_the_fence.php');
}
} catch (Exception $e) {
// Handle exception caught and apply formatting
}
$devOut = ob_get_contents();
ob_end_flush();
To give an example how I would handle the multiple exceptions you need to catch with a custom class
class CustomExceptionHandler extends Exception {
private $msg;
private $code;
private $otherVars;
public function __construct($msg,$code=0,$otherDebugVar=null){
$this->msg = $msg != null ? $msg : "An unknown exception was thrown";
$this->code = $code;
$this->otherVars = $otherDebugVar;
parent::__construct($msg,$code);
}
public function getOtherVars() {
return $this->otherVars;
}
}
The idea is to just keep the custom information within the exception object, and when you rethrow the exception at the end of a try/catch block as a standard exception you include the formatted custom message, it shouldn't really matter now which Exception handler picked up the original exception as all the info you will need will come downstream and be caught in the original try / catch block.
class BasicTemplate {
private $template;
private $path;
private $contents;
public function __construct($template, $path) {
$this->template = $template;
$this->path = $path;
$this->buildTemplate();
}
private function buildTemplate() {
if ($contents = #file_get_contents($this->path . $this->template)) {
$this->contents = $contents;
} else {
$e = new CustomExceptionHandler("Message",2,$this->path . $this->template);
// Do whatever else you want to do with custom exception handling class
throw $e;
}
}
}
Now you need to catch your exception and rethrow it:
try {
$html = new BasicTemplate($temp,$path);
} catch {CustomExceptionHandler $e) {
throw new Exception("Message: {$e->getMessage()} Other Info: {$e->getOtherVars()}",$e->getCode());
}
That's the rough idea anyhow, hope it helps.
So I catch an exception (instance of Exception class) and what I want to do is change its exception message.
I can get the exception message like this:
$e->getMessage();
But how to set an exception message? This won't work:
$e->setMessage('hello');
For almost every single case under the sun, you should throw a new Exception with the old Exception attached.
try {
dodgyCode();
}
catch(\Exception $oldException) {
throw new MyException('My extra information', 0, $oldException);
}
Every once in a while though, you do actually need to manipulate an Exception in place, because throwing another Exception isn't actually what you want to do.
A good example of this is in Behat FeatureContext when you want to append additional information in an #AfterStep method. After a step has failed, you may wish to take a screenshot, and then add a message to the output as to where that screenshot can be seen.
So in order to change the message of an Exception where you can just replace it, and you can't throw a new Exception, you can use reflection to brute force the parameters value:
$message = " - My appended message";
$reflectionObject = new \ReflectionObject($exception);
$reflectionObjectProp = $reflectionObject->getProperty('message');
$reflectionObjectProp->setAccessible(true);
$reflectionObjectProp->setValue($exception, $exception->getMessage() . $message);
Here's that example the Behat in context:
/**
* Save screen shot on failure
* #AfterStep
* #param AfterStepScope $scope
*/
public function saveScreenShot(AfterStepScope $scope) {
if (!$scope->getTestResult()->isPassed()) {
try {
$screenshot = $this->getSession()->getScreenshot();
if($screenshot) {
$filename = $this->makeFilenameSafe(
date('YmdHis')."_{$scope->getStep()->getText()}"
);
$filename = "{$filename}.png";
$this->saveReport(
$filename,
$screenshot
);
$result = $scope->getTestResult();
if($result instanceof ExceptionResult && $result->hasException()) {
$exception = $result->getException();
$message = "\nScreenshot saved to {$this->getReportLocation($filename)}";
$reflectionObject = new \ReflectionObject($exception);
$reflectionObjectProp = $reflectionObject->getProperty('message');
$reflectionObjectProp->setAccessible(true);
$reflectionObjectProp->setValue($exception, $exception->getMessage() . $message);
}
}
}
catch(UnsupportedDriverActionException $e) {
// Overly specific catch
// Do nothing
}
}
}
Again, you should never do this if you can avoid it.
Source: My old boss
Just do this, it works I tested it.
<?php
class Exception2 extends Exception{
public function setMessage($message){
$this->message = $message;
}
}
$error = new Exception2('blah');
$error->setMessage('changed');
throw $error;
You can't change Exception message.
You can however determine it's class name and code, and throw a new one, of the same class, with same code, but with different message.
You can extend Exception and use the parent::__construct to set your message. This gets around the fact that you cannot override getMessage().
class MyException extends Exception {
function __construct() {
parent::__construct("something failed or malfunctioned.");
}
}
here a generified snippet i'm using.
foreach ($loop as $key => $value)
{
// foo($value);
thow new Special_Exception('error found')
}
catch (Exception $e)
{
$exception_type = get_class($e);
throw new $exception_type("error in $key :: " . $e->getMessage());
}
An ugly hack if you don't know which kind of exception you're handling (that can have its own properties) is to use reflection.
try {
// business code
} catch (\Exception $exception) {
$reflectedObject = new \ReflectionClass(get_class($exception));
$property = $reflectedObject->getProperty('message');
$property->setAccessible(true);
$property->setValue($exception, "new message");
$property->setAccessible(false);
throw $exception;
}
You should use this crap wisely in very specific case when you don't have any other choice.
You can't change the message given by the Exception class. If you wanted a custom message, you would need to check the error code using $e->getCode() and create your own message.
If you really wanted to do this (in the only situation I can think that you might want to do it), you could re-throw the exception:
function throwException() {
throw new Exception( 'Original' );
}
function rethrowException() {
try {
throwException();
} catch( Exception $e ) {
throw new Exception( 'Rethrow - ' . $e->getMessage() );
}
}
try {
rethrowException();
} catch( Exception $e ) {
echo $e->getMessage();
}
The php Exception class has a __toString() method which is the only method within the Exception class that is not final, meaning it can be customised.
class HelloMessage extends Exception {
function __toString() {
return $this->getMessage()." you have an error with code: ".$this->getCode();
}
}
You use it as follows within try-catch block:
try {
if (2 > 0) {
throw new HelloMessage("Hello", 10);
}
} catch (HelloMessage $e) {
echo $e;
}
Output would be:
Hello you have an error with code: 10
You can extend Exception with your own, and put a setter in it
class MyException extends Exception
{
private $myMessage = '';
public function getMessage()
{
if ($this->myMessage === '') {
return parent::getMessage();
} else {
return $this->myMessage;
}
public function setMessage($msg)
{
$this->myMessage = $msg;
}
}
This is an improved version of David Chan's answer. It's a re-throw solution which uses get_class to rethrow the same exception type, and it passes all parameters to the constructor, even in the case of ErrorException, which has six rather than three constructor parameters.
foreach ($loopvar as $key => $value)
{
doSomethingThatMightThrow($value);
}
catch (\Exception $e)
{
$exception_type = get_class($e);
$new_message = "[key '" . $key . "'] " . $e->getMessage();
if ($e instanceof \ErrorException) {
throw new $exception_type($new_message, $e->getCode(), $e->getSeverity(), $e->getFile(), $e->getLine(), $e);
}
throw new $exception_type($new_message, $e->getCode(), $e);
}
What are best way of error handling? This is what I came up with:
class test {
public static function Payment($orderid, $total) {
if (empty($orderid) && empty($total)) {
return array('status' => 'fail', 'error' => 'Missing Data');
}
}
}
I heard about Try/Exceptions but how to fit that into my code? If you could provide example that would be great!
If you use PHP 5, you can handle error with exception :
http://fr2.php.net/manual/en/class.exception.php
This way is cleaner than manual set exception message, because you have access to a try catch system and you can isolate exception handling
As mentioned, use Exceptions. Specific to your example, you throw an exception if some condition fails. Then when you envoke the method that can throw an exception, you wrap it with a try/catch handling block.
class test {
public static function Payment( $orderid, $total ) {
if (empty( $orderid ) && empty( $total )) {
throw new Exception('Missing Data');
}
}
}
try {
test::Payment("1", "2"); //should be fine
test::Payment(); //should throw exception
} catch (Exception $e){
echo $e;
//do other things if you need
}
You could use exceptions.
However, in the use case you've posted, simply doing the checks at the controller level should suffice.
I also think that explicitly checking the return type for array (on fail) is counter intuitive.
Here is how you might modify your code to use an exception. It also helps to document the circumstances under which the exception is thrown.
class test {
/**
* [method description]
* #throws Exception if the order ID or total is empty
*/
public static function Payment($orderid, $total) {
if (empty($orderid) && empty($total)) {
throw new Exception("fail: Missing Data");
}
}
}
You can also create your own exception class if you want to include extra data in the exception.
class MyException extends Exception{
public $status, $error;
public function __construct($status, $error){
parent::__construct("$status: $error");
$this->status = $status;
$this->error = $error;
}
}
I tend to lean towards throwing exceptions, and then using the try/catch mechanism to deal with the aftermath. The man page is here: http://php.net/manual/en/language.exceptions.php
The best practice is to use Exceptions.
http://php.net/manual/en/language.exceptions.php
Most functions in PHP returns true/false:
var_dump (is_int ("1")); // false
Can I configure PHP to return exceptions instead of boolean ?
try {is_int ("1")} catch (Exception $e) {exit ($e->getMessage ());}
Thanks.
Couldn't you just use a throw?
<?php
function myFunction($var)
{
if(!(is_int($var))
{
throw new Exception('Custom message about the error');
}
}
?>
And the just have a try/catch block to catch your issue?
<?php
try
{
myFunction(1);
myFunction("1");
}
catch
{
echo 'Caught exception: ', $e->getMessage(), "\n";
}
?>
I agree is_int would be a terrible thing to throw an exception, but you could change warnings and errors into exceptions by settint an error handler that'll throw an exception with the warning or error message:
class ErrorOrWarningException extends Exception
{
protected $_Context = null;
public function getContext()
{
return $this->_Context;
}
public function setContext( $value )
{
$this->_Context = $value;
}
public function __construct( $code, $message, $file, $line, $context )
{
parent::__construct( $message, $code );
$this->file = $file;
$this->line = $line;
$this->setContext( $context );
}
}
function error_to_exception( $code, $message, $file, $line, $context )
{
throw new ErrorOrWarningException( $code, $message, $file, $line, $context );
}
set_error_handler( 'error_to_exception' );
Not that this will not magically change non errors to throw exceptions the way you explained it, but I believe it may be just what you're looking for.
no, you will either have to
use if/else statements with error handling or
write a wrapper which throws exceptions or
use class library/framework for that
i would go for #1
i guess you mean functions that return false and generate a warning on failure, like fopen(). Yes, you can (and actually should) convert these warnings to exceptions, using the technique outlined here.