Hi Dan,

On Wed, Jan 6, 2016 at 10:12 AM, Dan Ackroyd <dan...@basereality.com> wrote:
>
>
> // If FileException, NetworkException are thrown inside 'foo',
> // they are automatically
> // caught and execution continues to call bar()
> foo() suppress FileException, NetworkException;
> bar();
>
>

> That would allow people to ignore specific exceptions without having a
> huge amount of boilerplate code that makes the actual code illegible.
>

I like this suggestion a lot, it keeps the expected suppressions with the
call that will be suppressing.


> Possibly a more powerful technique would be needed, e.g.being able to
> specific an exception handler on a line by line basis.*
>
>
> // Execute function foo and if any exception is
> // raised, call the callable 'fooCallException'
> foo() raise 'fooRaiseException';
> bar();
>
> public function fooRaiseException(\Throwable $t)
> {
>     if ($t instanceof FileException ||
>         $t instanceof NetworkException) {
>         // This is not an error in this situation, execution can continue.
>         return;
>     }
>
>     // Other exceptions are not acceptable.
>     // Propagation of the exception should continue as normal.
>     throw $t
> }
>
>
I'm not sure I like this, because of a few things, it puts the suppressed
exceptions in a separate location from the call getting suppressed. Also,
having the function name as a string will make it harder to find in an IDE
since go to definition won't work (and since functions aren't first class
citizens as in javascript, we can't pass a raw name AFAIK) and admittedly
this reasoning doesn't count, IDE makers can update their IDE to have go to
definition work on strings after a raise keyword (however, this will get
complicated if ANY callable works, IE ['class','method'] and
'class::method'). Its basically moving the boilerplate you didn't like into
a function not connected to the call and allows accidental control flow
changes. Think if I do "foo() raises 'fooRaiseException'" everywhere I call
foo, but then in one place I need to suppress FileException and somewhere
else I don't want to. Someone comes along and changes the fooRaiseException
function and now the place where we need to know about FileException we no
longer do. This is caused by 2 things: the "handler" for the exceptions
isn't tied to the location where the exceptions happen, and "Find Usages"
in an IDE will not find strings referencing the function (especially if
built programmatically), again this second one is not valid because we
shouldn't make decisions based on what IDEs support but what's best for the
language. However I think its important to know the number of changes which
will be required in IDEs to make the feature usable for the average
developer (especially if someone from PHPStorm, NetBeans or ZendStudio came
along and said it would be impossible to support a specific new feature in
a usable way).

The benefits of doing it like this are:
>
>  * Any unexpected exceptions are not silenced. This avoids the problem
> of all errors being silenced when the programmer thought only a single
> one would be suppressed.
>
>  * It would allow users to debug their code. Even if an error is
> suppressed by the raise callable for a line of code, it would still be
> userland code that can be stepped through with a debugger, and so you
> can see the exception inside the 'raise' function.
>
>  * It leaves the code in a readable state, which having lots of
> try/catch blocks does not.
>
>  * It allows separate modules to have their own way of handling
> errors, whereas set_error_handler is a per application setting.
>

And what is the benefit of having either of these options being part of the
language (IIUC, this would mean changes to the parser, lexer, compiler and
run time) when it could be implemented in userland?

function tryAndSuppress(callable $func, array $args = [], array $suppress =
[])
{
   try {
       $ret = call_user_func_array($func, $args);
   } catch (Throwable $e) {
       foreach ($suppress as $type) {
          if ($e instanceof $type) {
             return;
          }
       }
       throw $e;
   }
   return $ret;
}

$fh = tryAndSuppress('fopen', ['/path/to/some/file.ext', 'r'],
['FileNotFoundException']);


>
> As I said at the start, I think we need to have more powerful error
> handling in place before deprecating the silence operator. If anyone
> wants to progress this RFC, what I think needs to happen is:
>
> * Work on something like the above 'raise' or 'suppress' exceptions so
> that there is an alternative to the internal warnings.
>
> * Work on a plan on how to migrate all internal errors/warnings to
> individual exceptions. The current way of using set_error_handler to
> convert warnings/errors into generic exceptions isn't good enough.
> People need file operations to raise a more specific 'FileException',
> to allow catching of individual types of error. I don't think it would
> be feasible to just have a massive BC break on this...so a cunning
> migration plan would be needed.
>

Agree with this 100%. While I think errors suck and should all be turned
into exceptions, I also think the exceptions used need to be meaningful.

~ Ryan

Reply via email to