We discussed the exception handling at the php[world] meeting, and the general consensus from those in the room was that semantically "missing", "broken", and "other" are distinct error conditions that should be treated separately. To that end I offer the following PR so there's something concrete to discuss:

https://github.com/php-fig/fig-standards/pull/844

As far as use case, beyond the semantic distinction "missing" implies the caller is doing something wrong. "Broken" means the configuration is wrong. (Even if the configuration in this case is a hard-coded class, it's still configuration so it's in-scope for PSR-11, not PSR-11-followup.) Those would imply different people are doing something wrong, so it's a different person's responsibility to fix.

--Larry Garfield

On 11/11/2016 01:42 PM, Daniel Plainview wrote:
> So why even mention it then?

I think it's because most implementations are not aware of this difference, but it's important. It doesn't automatically mean that the interface must reflect the DependencyNotFoundException.
@throws FooException means "it's your possible issue, take care of it".
@throws DependencyNotFoundException means "My arm can be broken, take care of it". @throws ConfigrationDoesNotExistException means "My leg is broken, take care of it".

> As someone building a framework that uses a container I would very much care about the distinction between group 2 and group 3

Could you, please, provide us a real use-case when you need to catch DependencyNotFoundException?

On Friday, November 11, 2016 at 9:19:11 PM UTC+3, Larry Garfield wrote:

    I'm not suggesting converting all exceptions that might possibly
    happen to MisconfiguredServiceException. I'm saying there's 3
    broad categories:

    1) What you asked for doesn't exist.
    2) What you asked for is broken.
    3) Something else happened, WTF?  (Eg, a database-backed container
    has a missing DB.)

    I agree that group 3 is out of scope.  Group 1 is already
    covered.  The question is group 2, where "there is something wrong
    with the container but it's not that what you asked for is
    missing" is, I assert, legitimately in scope.  As someone building
    a framework that uses a container I would very much care about the
    distinction between group 2 and group 3.  That doesn't mean fully
    exploring all possible details of group 2 and what might break,
    just indicating the separation between groups 2 and 3.

    The current text says, in essence, "for group 2, throw anything
    but the same as group 1".  So why even mention it then?  And no,
    that's not a suggestion to remove yet more things from the spec.
    :-)  (That way lies more incompatibility.)  I'm saying there
    should be a clearly defined exception for group 1, a clearly
    defined parent exception for group 2 (subclasses specific to a
    given implementation entirely welcome), and group 3 is
    not-our-problem.

    --Larry Garfield

    On 11/07/2016 12:20 PM, Daniel Plainview wrote:
    The major problem with unchecked exceptions is that it is not
    class consumer business.
    What do you say when someone throws SomethingIsBrokenInsideOfMe
    to you? I'd say "hey, take a vacation, fix yourself, you look
    unhealthy". Is it not my business what exactly is broken (leg or
    arm, whatever, it doesn't help to fix my issue). But it is fair
    enough if library throws YouAreWrong exception in my face when
    I'm screwed up.

    When you make a typo in your Container implementation, it could
    fail with something like \TypeError. It is "unhappy-path", isn't
    it? Is it MisconfiguredServiceException? I don't think so. What
    do you do then? You go and fix your implementation. What happens
    if configuration is broken? You go and fix it as well. I don't
    see much difference here.

    MisconfiguredServiceException for me is an illusory solution for
    "unhappy-path". You can't trust it. What does it exactly mean?
    What if you make a syntax typo in your PHP configuration (with
    arrays, let's say), should it raise the
    MisconfiguredServiceException? Or it should bubble up
    \ParseError? I mean, it is more like
    ConfigurationIsTotallyScrewedUp rather than simple
    MisconfiguredService. It's vague exception that means "something
    is wrong with *them*". I'd rather catch ALL other
    (non-NotFoundException) exceptions if I want to avoid program
    crash for some reason. I can trust \Exception (or even
    \Throwable). The meaning is simply same: "something is wrong with
    them" and I don't really care what exactly is.

    Can you please provide real-life example when you want to catch
    MisconfiguredServiceException (excepting "I want to log it
    differently", because it doesn't look real-life, honestly; and it
    is too universal answer for any kind of exceptions)?

    On Monday, November 7, 2016 at 5:27:58 PM UTC+3, Larry Garfield
    wrote:

        Container nesting is part of the spec, and a stated goal of
        the spec, so it's a valid use case to consider.

True, there are many different ways that things can break. Consistent and constructive handling of the not-happy path is
        a critical part of spec development.  See also: HTML5, the
        majority of which is not new stuff but standardizing the many
        different ways that browsers used to handle badly formed
        HTML.  It's a total mess because the unhappy path was never
        well-defined, so everyone did it differently, so code broke
        in a variety of inconsistent ways.  My issue is that "throw
        anything other than X" is not a consistent and constructive
        handling of the non-happy path.

        The biggest distinction I would draw would be between "you
        asked for something that's not there" and "you asked for
        something that's broken". Those are very different things;
        one implies I screwed up, the other implies the configurer
        screwed up.  I can see the argument for not specifying a
        separate exception for every possible way that the requested
        service is broken (there are many, that's true), but a clear
        distinction between those two broad categories ("missing" and
        "broken") seems like a much better baseline.

        In that case, I would revise my ask to defining two exceptions:

        * NotFoundException extends ContainerExceptionInterface
        (thrown if the requested service isn't defined anywhere)
        * MisconfiguredServiceException extends
        ContainerExceptionInterface (thrown if the requested service
        is defined, but for whatever reason can't be instantiated)

        And implementers are free to subclass the latter if they
        choose, but must still have that exception flag on them. (I'm
        flexible on the name, the one I have there is likely not the
        best name.)

        --Larry Garfield

        On 11/05/2016 12:59 PM, Daniel Plainview wrote:
        > A situation of "if your child container throws exception
        X, you're required to catch it and turn it into anything
        that's not X but is still Y" seems needlessly convoluted

        You did it by introducing "child container", Container
        contract doesn't have any child containers, this contract is
        very simple and straightforward. By saying about child
        containers, you mean that you know how internals of the
        Container work, you know about "child containers", but you
        shouldn't care about it when you want to use Container.

        > but doesn't provide me as a developer sufficient debug
        information.  I'd potentially want to log differently
        depending on which exception it is, but I can't do that if I
        have no idea what the second exception is going to be; I
        just know what it's *not* going to be, which means I'd need
        a Pokemon-catch if I wanted to log it.  That's what I am not
        comfortable with.

        I asked you above, what do you think about
        DependencyArgumentTypeMismatchException,
        MissingRequiredArgumentException and many other exceptions,
        what makes them less important than
        DependencyNotFoundException? They are all about wrong
        configuration (or similar issues that indicates of internal
        problems of Container, not user's failure).

        I mean, you can say that "I'd potentially want to log
        differenty DependencyArgumentTypeMismatchException and
        MissingRequiredArgumentException, but I don't know how to
        catch them". There are millions of reasons why container can
        be broken, after all, you can't predict them all.

        DependencyNotFoundException is unchecked, because it is not
        client's problem, but internal issue of Container itself.
        Service does exist, but configuration is wrong, client has
        nothing to do with it, there is no sense to reflect it in
        the interface.

        On Saturday, November 5, 2016 at 3:38:06 AM UTC+3, Larry
        Garfield wrote:

            On 11/04/2016 06:27 AM, David Négrier wrote:
            I'll try an analogy with Java.

            In Java, there is a difference between checked and
            unchecked exceptions. Checked exceptions are the
            exceptions that should be catched by the user.
            Unchecked exceptions are the exceptions for which it
            makes no sense to catch them. There is no reason to
            catch an "unchecked exception" because there is no way
            the user can provide an interesting alternative
            behaviour in the catch statement. So unchecked
            exception should essentially always bubble up all the
            way to the top of the application and trigger an
            exception middleware.

            For PSR-11, we deemed that the
            NotFoundExceptionInterface was a checked exception
            (because if the container does not contain the entry
            you are looking for, the user can maybe try an
            alternative behaviour like looking for an alias or
            creating a default entry).
            We also deemed that the
            DependencyNotFoundExceptionInterface was an unchecked
            exception (because it means the container is poorly
            configured and there is little to do about it except
            display an error message).

            Finally, we think that checked exceptions should be
            part of a PSR while unchecked exceptions should be out
            of any PSR (because there is no need to standardize an
            exception if you don't need to catch it).

            Of course, there is no absolute truth here. We could
            decide that the NotFoundExceptionInterface should be
            "unchecked" (because you can always call "has" before
            "get" so there is no reason this exception should be
            catched). Also, since it boils down to "what valid use
            case can I have to catch a
            DependencyNotFoundExceptionInterface?", we could also
            find a valid use case for catching
            DependencyNotFoundExceptionInterface and decide it
            should be part of the PSR. But so far, a quick survey
            of frameworks out there has shown that no-one ever
            catches the "dependency not found exceptions".

            Also, Larry, you say:

            /... based on the spec alone (no metadoc, no GitHub
            threads) the following would be legal:

            try {
              $c1->get('a');
            } catch (NotFoundExceptionInterface $e) {
              print $e->getMessage();
              // prints "Service 'b' not found"
            }
            /
            This is not completely true. The spec states that:

            /A call to |get| can trigger additional calls to |get|
            (to fetch the dependencies). If one of those
            dependencies is missing, the
            |NotFoundExceptionInterface| triggered by the inner
            |get| call SHOULD NOT bubble out. Instead, it should be
            wrapped in an exception implementing the
            |ContainerExceptionInterface| that does not implement
            the |NotFoundExceptionInterface|.
            /
            So your code example is only valid if the container
            decides not to follow the recommendation (we used
            "SHOULD NOT" instead of "MUST NOT" to cope with
            existing containers). Of course, we could also
            strengthen the wording and write: /If one of those
            dependencies is missing, the
            |NotFoundExceptionInterface| triggered by the inner
            |get| call MUST NOT bubble out./
            This way, your code sample would be illegal (but it
            would be harder for existing containers to implement
            PSR-11). I have no strong opinion about the "SHOULD
            NOT" vs "MUST NOT" idea so far. Your comments are welcome.

            ++
            David.

            See, I would disagree with dependency-not-found being an
            unchecked exception.  I think that's the fundamental
            difference.  A situation of "if your child container
            throws exception X, you're required to catch it and turn
            it into anything that's not X but is still Y" seems
            needlessly convoluted, but doesn't provide me as a
            developer sufficient debug information.  I'd potentially
            want to log differently depending on which exception it
            is, but I can't do that if I have no idea what the
            second exception is going to be; I just know what it's
            *not* going to be, which means I'd need a Pokemon-catch
            if I wanted to log it.  That's what I am not comfortable
            with.

            I am generally very skeptical about SHOULD, and favor
            MUST in nearly all cases by default. SHOULD should be
            read as "you're allowed to be incompatible here", which
            is a statement a spec should make as rarely as possible.

            --Larry Garfield


--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group. To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+unsubscr...@googlegroups.com <mailto:php-fig+unsubscr...@googlegroups.com>. To post to this group, send email to php-fig@googlegroups.com <mailto:php-fig@googlegroups.com>. To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/a3502dd2-6319-4072-9c09-0c42af509c71%40googlegroups.com <https://groups.google.com/d/msgid/php-fig/a3502dd2-6319-4072-9c09-0c42af509c71%40googlegroups.com?utm_medium=email&utm_source=footer>.
For more options, visit https://groups.google.com/d/optout.


--
You received this message because you are subscribed to the Google Groups "PHP 
Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to php-fig+unsubscr...@googlegroups.com.
To post to this group, send email to php-fig@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/php-fig/e664114f-f1a0-4fa4-26cf-91c4cbc30061%40garfieldtech.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to