I'm interested in opinions on how to handle exceptions in the following situation.

Class A throws an exception from its constructor if something fails.

Class B's constructor creates an instance of class A. If anything fails in class B's constructor, I want it to throw an exception.

So, is there any benefit/reason for B to catch an exception from A if it fails, or does it make more sense to just let it slide through to the code that is trying to instantiate B. My feeling is that B should catch it, even if I will essentially just throw it again, but I can't make up my mind for sure (probably because I should be in bed and asleep 😉 ). Just wondered if there's a standard rule-of-thumb that would apply, or any best practice that would dictate which is better.

Example:

class A {
   public function __construct() {
      if($fullMoon && $tuesday)
      {
         throw new Exception(__METHOD__."(): Bad karma!");
      }
   }
}

// catch and throw again:
class B {
   private $obj;
   public function __construct() {
      try {
         $this->obj = new A();
      }
      catch(Exception $e) {
         throw(__METHOD__."(): $e");
      }
   }
}

// let the caller worry about it:
class C {
   private $obj;
   public function __construct() {
      $this->obj = new A();
   }
}

try {
   $test_b = new B();
   $test_c = new C();
}
catch(Exception $e)
{
   // do something with $e
}

    One of the touted advantages of exceptions over returning error codes is that exceptions can be handled at the appropriate level by propagation. However, "appropriate level" depends on context. If A's constructor throws an exception specific to A, I would be inclined to translate it to an exception specific to B, therefore I would use a try/catch in B's constructor. On the other hand, if A's constructor throws a more general exception, I would be inclined to just let the exception propagate to the scope that attempted to construct the B object.

      If B is to rethrow an exception it has just caught (this is generally speaking, not just in constructors), I tend to give the new exception that it throws an additional property (innerException) that contains the original exception that it caught, so that it is still available should it be wanted by some higher-up exception handler after all.

      As a (very) rough sketch:

      class MyException extends Exception
      {
      	public $innerException;
      	public function __construct($message=null, $code=0)
      	{
      		parent::__construct($message, $code);
      	}
      }
      
      
      class A
      {
      	public function __construct()
      	{
      		throw new Exception("Thrown from A");
      	}
      }
      
      class B extends A
      {
      	public function __construct()
      	{
      		try
      		{
      			parent::__construct();
      		}
      		catch(Exception $e)
      		{
      			$newe = new MyException("Thrown from B");
      			$newe->innerException = $e;
      			throw $newe;
      		}
      	}
      }
      
      try
      {
      	$t = new B;
      }
      catch(Exception $e)
      {
      	echo $e->getMessage(),"\n",$e->innerException->getMessage();
      }
      

        Thanks for those inputs. I think that at least for the particular application I was working on where this came up, I'll catch the exception within the calling constructor, and probably do something along the lines of what Weedpacket suggested so that I can easily(?) see the sequence of events that led to the exception in the error log.

          NogDog wrote:

          see the sequence of events that led to the exception in the error log.

          Don't forget that Exception objects come with a stack trace, so it's really only useful if the inner exception message is likely to contain information that a stack trace would lack.

            Hmmm...good point. Now I have to think about it some more. :rolleyes: Maybe what I want to think about first is an exception-handler class/function and determine exactly how I want to log such things, and then that will answer the question for me. 🙂

              Write a Reply...