views:

75

answers:

2

Hello. I'm confused by the following code:

class MyException extends Exception {}
class AnotherException extends MyException {}

class Foo {
  public function something() {
    print "throwing AnotherException\n";
    throw new AnotherException();
  }
  public function somethingElse() {
    print "throwing MyException\n";
    throw new MyException();
  }
}

$a = new Foo();

try {
  try {
    $a->something();    

  } catch(AnotherException $e) {
    print "caught AnotherException\n";
    $a->somethingElse();    
  } catch(MyException $e) {
    print "caught MyException\n";
  }
} catch(Exception $e) {
  print "caught Exception\n";
}

I would expect this to output:

throwing AnotherException
caught AnotherException
throwing MyException
caught MyException

But instead it outputs:

throwing AnotherException
caught AnotherException
throwing MyException
caught Exception

Could anyone explain why it "skips over" catch(MyException $e) ?

Thanks.

+8  A: 

The exception handlers catch exception raised by code inside the scope of their try block.

The call to $a->somethingElse() does NOT occur within the try block associated with the skipped exception handler. It occurs within another catch clause.

Just because it appears physically below the line that raises the exception isn't enough to make it cover that code.

The style choice of indenting braces makes this less clear, IMHO. The close brace for the previous try block appears on the same line as the next catch, even though they are unrelated (well, sibling) scopes.

Oddthinking
I just want to be sure we're on the same page... Basically he should have added another `try` after the first `catch`, right? Then, because the entire thing is embedded in the larger try, he would get 3 sets of exceptions? Essentially it never catches the second exception because it never tries?
Anthony
If he had, alternatively, changed the outer `catch` to "MyException" instead of "Exception" would this have caught the "MyException" `throw`? Is the issue that his attempt triggers the `throw` of "MyException" but because the attempt wasn't initiated by a `try`, the `catch` never happens? In other words, is "MyException" still out there to be caught by that third `catch`?
Anthony
@Anthony, re: First comment: Yes, if he added a third level of try block that would be one solution. Bit messy though. I wouldn't choose to characterise the problem as you have in your last sentence, but it isn't *wrong*.
Oddthinking
@Anthony, re: 2nd comment, 1st question: Yes, if he changed the outer catch as your describe, it would catch the exception, but the outer catch is ALREADY catching the exception! It is catching it as a *Exception* rather than as a *MyException*, but it is the same exception!
Oddthinking
A: 

Only because there isn't enough room for this in a comment. Think of the try...catch as an if...else loop. You wouldn't expect the following:

$a = 10;
if($a == 9)
    print "\$a == 9";
elseif($a == 10) {
    $a = 11;
    echo "now \$a == 11";
} elseif($a == 11) {
    echo "\$a == 11";
}

to print out the last condition ("\$a == 11"), because the condition was already met by the first elseif. The same is true with the try...catch. If the condition is met, it doesn't continue searching for new conditions in the same scope.

thetaiko