1

I have regular use cases of PHP \Exception sub classes where I want to collect up data and then bundle it into a final error message. For example:

  1. checking some data has contiguous days

     $missing = new MissingAdjustmentDataException('');        
    
     $testDate = $period->getPreviousPeriod()->getEnd();
    
     $z = 0;
     while ($testDate <= $period->getEnd() && $z < 500){
         if (!in_array($testDate, array_column($activationRedemptionAdjustmentDays, 'effective') )){
             $missing->addMissingRedemptionAdjustment($testDate);
         }
         if (!in_array($testDate, array_column($platformAdjustmentDays, 'effective') )){
             $missing->addMissingPlatformAdjustment($testDate);
         }
         $testDate->add(new \DateInterval('P1D'));
         $z++;
     }
    
  1. Then in my exception, I'm collecting the data in arrays:

     class MissingAdjustmentDataException extends \Exception
     {
         private $missingRedemptionAdjustment = [];
    
         private $missingPlatformAdjustment = [];
    
         public function updateMessage()
         {
             $message = 'Missing Adjustment data: ';
             if ($this->missingRedemptionAdjustment){
                 $ra = [];
                 foreach ($this->missingRedemptionAdjustment as $item){
                     $ra[] = $item->format('Y-m-d');
                 }
                 $message .= 'RedemptionAdjustment: '.implode(',',$ra);
             }
             if ($this->missingPlatformAdjustment){
                 $pl = [];
                 foreach ($this->missingRedemptionAdjustment as $item){
                     $pl[] = $item->format('Y-m-d');
                 }
                 $message .= 'PlatformAdjustment: '.implode(',',$pl);
             }
             $this->message = $message;
         }
    
         public function inError() : bool
         {
             if ($this->missingRedemptionAdjustment || $this->missingPlatformAdjustment){
                 return true;
             }else{
                 return false;
             }
         }
    
         public function addMissingRedemptionAdjustment(\DateTime $dateTime){
             $this->missingRedemptionAdjustment[] = clone $dateTime;
             $this->updateMessage();
         }
    
         public function addMissingPlatformAdjustment(\DateTime $dateTime){
             $this->missingPlatformAdjustment[] = clone $dateTime;
             $this->updateMessage();
         }
     }
    

My main problem is that I cannot find a way to do the formatting of the message in a "lazy" way when $missing->getMessage() is called. It seems to have update $this->message inside the Exception every time I add a data point to the exception.

Is there a better way to do this?

2 Answers 2

1
+100

The issue is that you are mixing two different things: the object that keeps track of the errors, and the exception. You should properly seperate them. For example:

class MissingDataCollector
{
    private $missingRedemptionAdjustment = [];
    private $missingPlatformAdjustment = [];

    public function addMissingRedemptionAdjustment(\DateTime $dateTime)
    {
        $this->missingRedemptionAdjustment[] = clone $dateTime;
    }

    public function addMissingPlatformAdjustment(\DateTime $dateTime)
    {
        $this->missingPlatformAdjustment[] = clone $dateTime;
    }

    public function check()
    {
        if ($this->missingRedemptionAdjustment || $this->missingPlatformAdjustment)
            throw new \Exception($this->getMessage());
    }

    private function getMessage()
    {
        $message = 'Missing Adjustment data:';
        if ($this->missingRedemptionAdjustment){
            $ra = [];
            foreach ($this->missingRedemptionAdjustment as $item){
                $ra[] = $item->format('Y-m-d');
            }
            $message .= ' RedemptionAdjustment: '.implode(',', $ra);
        }
        if ($this->missingPlatformAdjustment){
            $pl = [];
            foreach ($this->missingRedemptionAdjustment as $item){
                 $pl[] = $item->format('Y-m-d');
            }
            $message .= ' PlatformAdjustment: '.implode(',', $pl);
        }
        return $message;
    }
}

And the way to use it:

$missing = new MissingDataCollector();

// Some processing that may call addMissingRedemptionAdjustment() or addMissingPlatformAdjustment()
...

// Throw an exception in case of missing data
$missing->check();
Sign up to request clarification or add additional context in comments.

5 Comments

this may go into codereview.stackexchange.com territory, but should an Exception be thrown at the first sign of trouble or collect up all issues so they can be resolved more efficiently. Am I wrong to expect this from a programming language
@jdog To me, exceptions should be created and thrown immediately. They are not meant for anything else. Your code always creates an exception, even when there are no errors, which is weird.
it doesn't always create an exception, if the condition in the loop are not met, it just runs through. But clearly there can be different expectations by people.
@jdog The first line of your code creates $missing, which is an exception.
I see how you mean that now.
-1

You can execute updateMessage() while catching the exception

catch(MissingAdjustmentDataException $e) {
    $e->updateMessage();
    echo $e->getMessage();
}

You will find some advices and hacks in How to change exception message of Exception object?

2 Comments

I came up with this method, but it breaks encapsulation of the exception which I find even worse than repeatedly updating the message
Using __toString also breaks encapsulation, as I then need to remember to echo($e), instead of being able to use $e->getMessage() in some way

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.