2

Is it possible to catch an exception in an generator and just yield the next value? I tried something similar like the code in the example below, but it stops on an exception and does not yield the next value as "expected".

function generator(){
     foreach($aLotOfWork as $task){
       try {
         $promise = doSomethingThatCanFailBadly($task);

         yield $promise;
       } catch (Exception $e) {
         echo "oh.. there is an error, but I don't care and continue";
       }
     }
  }

IMHO This is not a duplicate of (php: catch exception and continue execution, is it possible?), because this person just wanted to know how to catch an exception in php and go on. In my case I already catch all exceptions, but the generator stops and does not go on as intended.

Community
  • 1
  • 1
NaN
  • 3,501
  • 8
  • 44
  • 77
  • Possible duplicate of [php: catch exception and continue execution, is it possible?](http://stackoverflow.com/questions/2132759/php-catch-exception-and-continue-execution-is-it-possible) – Roberto Geuke Jan 10 '17 at 18:32
  • 1
    Just add a `continue;` statement and you should be fine – Paradoxis Jan 10 '17 at 18:34
  • @RobertoGeuke Hi, I also found the other post, but these are two distinct cases. I already use try/catch, but I expect another behaviour. The other person was just looking for the try/catch construct. – NaN Jan 10 '17 at 18:34
  • 1
    @RobertoGeuke I don't think this is a duplicate, that is what OP wants to do, but it is not working – SaggingRufus Jan 10 '17 at 18:34
  • The person of the other question wants to catch the exception AND continue execution, like you want. But what @SaggingRufus is saying, the "duplicated" question shows how to do what you want to do, but it doesn't work like that in your case. So it's not a duplicate indeed, my bad! – Roberto Geuke Jan 10 '17 at 18:42
  • @Paradoxis can you describe it exactly? I tried to insert a continue statement at several places (end of catch, try and after the try/catch structure), but nothing worked. – NaN Jan 10 '17 at 18:50
  • Yes, it is a duplicate of [php: catch exception and continue execution, is it possible?](http://stackoverflow.com/questions/2132759/php-catch-exception-and-continue-execution-is-it-possible). Read your question again and again until you find the solution. It is embedded in the question: *"catch an exception in an generator and just yield the next value"*. Your code doesn't work because it doesn't `yield` after it `catch`es the exception. The `yield` in your code never executes because the thrown exception transfers the control to the innermost `catch` statement that matches the exception type. – axiac Jan 10 '17 at 21:13
  • 1
    that's not correct axiac, it doesn't yield, but it doesn't return either, it catches the exception, prints the error message, and goes back to the next iteration of the loop. Check my answer below and see if it makes sense to you. – palako Jan 10 '17 at 21:16

3 Answers3

1

Your code is right and it will capture the exception and continue, check this out:

$ cat so.php 
<?php

function doSomethingThatCanFailBadly($task) {
  if ($task == 3) {
    throw new Exception();
  }
  return $task;
}

function generator(){
     $aLotOfWork = array(1,2,3,4,5);
     foreach($aLotOfWork as $task){
       try {
         $promise = doSomethingThatCanFailBadly($task);

         yield $promise;
       } catch (Exception $e) {
         echo "oh.. there is an error, but I don't care and continue\n";
       }
     }
  }


foreach (generator() as $number) {
    echo "$number\n";
}
?>

$ php so.php 
1
2
oh.. there is an error, but I don't care and continue
4
5

Have a look at your error stack trace. Maybe what's happening is that something inside your doSomethingThatCanFailBadly method is producing an exception but it is also catching it and forcing the quit with die() or exit() before it ever gets to your catch block. There's not much you can do in that case. You could maybe use register_shutdown_function and see if that helps, but that is starting too look messy.

palako
  • 3,342
  • 2
  • 23
  • 33
0

You would need to save to output of the generator to an array, and if during individual yields an exception is thrown, with right exception handling, it simply will not get into the array.

In your example the doSomethingThatCanFailBadly throws an exception, then the program flow gets to the catch branch. So actually doSomethingThatCanFailBadly has no return value that could be assigned to $promise - so there's nothing to yield at that point.

Rápli András
  • 3,869
  • 1
  • 35
  • 55
  • I would also prefer an array instead of an generator function, but I need to use an generator, because I want to add new "tasks" while the programm is already working. Although, it might be possible by passing it as reference, it would not be not so easy and more like an dirty hack. – NaN Jan 10 '17 at 18:46
  • 1
    I don't think you should mess with references even if it's possible. That's code suicide :) – Rápli András Jan 10 '17 at 18:49
0

@palako explained the problem very nicely, but he does not really provide a solution for my case. I use this generator to generate Promises and the consumer (Guzzle's EachPromise-method) seems to stop working on an Exception. So I replaced the throw new SomeException('error message') statement by return RejectedPromise('error message').

I have to admit that this is a very situation-specific solution, but you can do something similar in other settings too: just return an object instead of using exceptions.

NaN
  • 3,501
  • 8
  • 44
  • 77