How to change exception message of Exception object?

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;

Tags:

Php