[PSR-11] Exceptions

403 views
Skip to first unread message

Matthieu Napoli

unread,
Nov 2, 2016, 5:53:13 PM11/2/16
to PHP Framework Interoperability Group
Splitting off the main review thread, quoting Larry:

> 1) What other exceptions might get($id) throw besides NotFoundException? 

\Exception (because any exception could be thrown when creating services) => are you suggesting we should document it in the standard?

> 2) I would much prefer to see a DependencyNotFoundException defined for 
that case than leaving that up to individual implementers. Standard 
error handling is just as important if not moreso than the happy path. 

Why/when would one want to catch that exception (use case)? This is not a need we have seen when using container-interop as of now, I'm of course not against adding new stuff but we should do it for a reason.

Matthieu

Daniel Plainview

unread,
Nov 2, 2016, 7:06:03 PM11/2/16
to PHP Framework Interoperability Group
If you need to know whether dependency of a service is missing and you have some special logic behind it... What about DependencyArgumentTypeMismatchException, MissingRequiredArgumentException and tons of others container-is-screwed exceptions? What makes them less important? 

(question to Larry)

David Négrier

unread,
Nov 3, 2016, 4:53:13 AM11/3/16
to PHP Framework Interoperability Group
@Larry: the DependencyNotFoundException was discussed quite recently.

Here is the relevant link explaining the story behind it and why there is no DependencyNotFoundException: https://github.com/php-fig/fig-standards/pull/810

Larry Garfield

unread,
Nov 3, 2016, 11:32:15 AM11/3/16
to php...@googlegroups.com
From moufmouf in that thread:

" The logic behind this is that if a user is calling the get method on 'foo' and 'foo' does not exist, it is very different from the user calling get on 'foo' and the 'foo' service has a missing dependency. In one case, maybe the user screwed up something (i.e. checked exception), in the other case, it is the container configuration that is screwed up (i.e. unchecked exception)."

They are different error cases.  Different error cases should be different exceptions.  If not, then 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"
}

Because a delegated container didn't have a particular dependency, but because it doesn't know that it's a child it just throws "I don't have it", which gets thrown all the way up to the initial caller, who goes "wait, wat?"  If the parent container is going to catch it and handle it, then it needs to wrap that inner Not-Found with... something.  That something should be standardized.

If I as the caller don't care which type of exception is thrown, well, that's where interfaces that cover both exceptions are helpful. :-)

--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+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/c341150d-2834-42ba-a7ac-058510f4cb7c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Daniel Plainview

unread,
Nov 3, 2016, 2:54:15 PM11/3/16
to PHP Framework Interoperability Group
> but because it doesn't know that it's a child it just throws "I don't have it", which gets thrown all the way up to the initial caller, who goes "wait, wat?"

It would be considered as bugged PSR-11 implementation. 

> then it needs to wrap that inner Not-Found with... something.  That something should be standardized.

Why?

David Négrier

unread,
Nov 4, 2016, 6:27:38 AM11/4/16
to PHP Framework Interoperability Group
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.

Larry Garfield

unread,
Nov 4, 2016, 8:38:06 PM11/4/16
to php...@googlegroups.com
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

Daniel Plainview

unread,
Nov 5, 2016, 12:59:46 PM11/5/16
to PHP Framework Interoperability Group
> 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.

Larry Garfield

unread,
Nov 7, 2016, 9:27:58 AM11/7/16
to php...@googlegroups.com
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
--
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+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.

Daniel Plainview

unread,
Nov 7, 2016, 1:20:04 PM11/7/16
to PHP Framework Interoperability Group
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)?

Larry Garfield

unread,
Nov 11, 2016, 1:19:11 PM11/11/16
to php...@googlegroups.com
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

Daniel Plainview

unread,
Nov 11, 2016, 1:42:11 PM11/11/16
to PHP Framework Interoperability Group
> 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?

Daniel Plainview

unread,
Nov 11, 2016, 1:52:38 PM11/11/16
to PHP Framework Interoperability Group
> Could you, please, provide us a real use-case when you need to catch DependencyNotFoundException?

Or MisconfiguredServiceException, whatever :)

Matthieu Napoli

unread,
Nov 11, 2016, 1:53:38 PM11/11/16
to php...@googlegroups.com
> As someone building a framework that uses a container I would very much care about the distinction between group 2 and group 3.

Larry, this is very valuable feedback. However can you be more specific on when/why?

 I'm completely open to such an interface if it is actually needed by some code out there. Seeing said code would help, maybe it was mentioned already, I apologize if I missed it.
--
You received this message because you are subscribed to a topic in the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/php-fig/2pnhudRUpQg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to php-fig+u...@googlegroups.com.

To post to this group, send email to php...@googlegroups.com.

Larry Garfield

unread,
Nov 19, 2016, 1:46:46 PM11/19/16
to php...@googlegroups.com
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
--
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+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.

Matthieu Napoli

unread,
Nov 20, 2016, 7:03:00 AM11/20/16
to PHP Framework Interoperability Group
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.

I'm sorry but I still don't see a use case can you lay it out clearly?

An exception exists to be caught, when would you need to catch it and not catch \Exception?

Matthieu

Daniel Plainview

unread,
Nov 20, 2016, 2:32:38 PM11/20/16
to PHP Framework Interoperability Group
@Larry

> Those would imply different people are doing something wrong, so it's a different person's responsibility to fix.

It is already clear. If it is not NotFoundServiceException, then it is not user's fault. No need to have yet another exception.
So we return to the same question: what's the use case when you need to catch MisconfiguredServiceExceptionInterface?

GeeH

unread,
Nov 23, 2016, 6:08:40 AM11/23/16
to PHP Framework Interoperability Group
My opinion is this:

NotFoundException - we don't have a service for this key, you've messed up your configuration somewhere
CreationException - argh, something went wrong creating your service, this is probably a bug or coding problem

These are different errors and could/should be handled differently.

G

Daniel Plainview

unread,
Nov 23, 2016, 8:05:00 AM11/23/16
to PHP Framework Interoperability Group
> These are different errors and could/should be handled differently.

I think we all agree on this point. The question is do you need explicit CreationException and why?

GeeH

unread,
Nov 23, 2016, 9:07:48 AM11/23/16
to PHP Framework Interoperability Group
Yes we do need an explicit exception, because create errors should be handled differently from service not being found.

G

Daniel Plainview

unread,
Nov 23, 2016, 10:29:05 AM11/23/16
to PHP Framework Interoperability Group
> because create errors should be handled differently from service not being found

Sure. We already can do it:

try {
    $this
->container->get('foo');
} catch (NotFoundService $e) {
   
// ...
} catch (\Exception $e) {
   
// Configuration syntax error, circular dependencies, etc.
}

Why do you need CreationException, can you provide some use cases when you want to catch exactly this exception?

By the way, I don't know if you did it on purpose, but your name (CreationException) seems more vague than Larry's MisconfiguredServiceException. It sounds like more generic exception and means for me literally any error excepting NotFoundService exception. What's the difference with \Exception then?

GeeH

unread,
Nov 23, 2016, 11:32:54 AM11/23/16
to PHP Framework Interoperability Group
I deliberately picked a vague term, as I see it we have 3 exceptions:

ContainerException
NotFoundException extends ContainerException
CreationException extends ContainerException

Names are not finalised. 

This way I can catch all container exceptions using ContainerException, or the individual exception I am interested in. In your example, it would not be possible to catch only exceptions raised by the container, as other Exceptions could be raised (if for example pulling service config from database or memory, or using an HTTP request, these services may raise uncaught exceptions). 

Personally, I would handle missing services differently to a general container error and would be keen to see the spec reflect this, however, at this point I don't really feel strongly enough to spend any more time discussing.

G

Daniel Plainview

unread,
Nov 23, 2016, 12:03:07 PM11/23/16
to PHP Framework Interoperability Group
> it would not be possible to catch only exceptions raised by the container, as other Exceptions could be raised (if for example pulling service config from database or memory, or using an HTTP request, these services may raise uncaught exceptions). 

Yes, because there is no need in it.
Both ContainerException and CreationException are very artificial exceptions.
You can't do much at this point, container is screwed, that's it.
People say "I want to handle it differently", but they find difficult to say what's the difference with simple catch (\Exception $e).
That's weird.

Client doesn't care if service is misconfigured, or database is down, or configuration has syntax error, etc. Container just doesn't provide the service, that's all.

Matthieu Napoli

unread,
Nov 23, 2016, 12:05:20 PM11/23/16
to php...@googlegroups.com
This way I can catch all container exceptions using ContainerException, or the individual exception I am interested in. In your example, it would not be possible to catch only exceptions raised by the container, as other Exceptions could be raised (if for example pulling service config from database or memory, or using an HTTP request, these services may raise uncaught exceptions). 

Again, can we get an actual use case where one would need to catch MisconfiguredException (or whatever the name) separately from \Exception? We still have had no answer to that.

Personally, I would handle missing services differently to a general container error and would be keen to see the spec reflect this

NotFoundException is already a thing in the spec, do you have something else in mind?

David Négrier

unread,
Dec 8, 2016, 9:19:34 AM12/8/16
to PHP Framework Interoperability Group
Hi people!

This issue has been standing for the last 2 weeks with no activity.

Let's revive this and try to close it.

There is currently one PR made by Larry (https://github.com/php-fig/fig-standards/pull/844) proposing to add a "MisconfiguredServiceExceptionInterface" to PSR-11. There is support for this PR from @GeeH and from the people attending PHPWorld (although we don't know who was at this meeting). Larry, could you let us know who attended the FIG meeting at PHPWorld?

Also, Matthieu, Daniel and myself are reluctant to add this exception because we don't see why you would catch this exception? The question has been asked by Matthieu and so far, we haven't had any proposition of a use case where catching "MisconfiguredServiceExceptionInterface" would make sense. If you can highlight such a use case, please let us know.

Meanwhile, I've been thinking a bit more about what would be the impact of adding a "MisconfiguredServiceExceptionInterface" to PSR-11.

Impact of a "MisconfiguredServiceExceptionInterface"

I realized a few days ago that adding this exception could actually be detrimental to users and hinder adoption.

Let me give you a sample based on Simplex (the Pimple 3 PSR-11 fork):

The PR reads:

> A call to the get method for a a service that is defined but cannot be instantiated due to misconfiguration, missing dependency, or other issue related to the container MUST throw a Psr\Container\Exception\MisconfiguredServiceExceptionInterface.

Now, let's assume we have a Simplex container declared like this:

$container->set('boom', function() {
    // $container variable is not defined, this will fail.
    return $container->get('foo');
});

$container->set('cache.apcu', function() {
    // Let's say the constructor below will throw an exception because APCU is not available on my server.
    return new ApcuCache();
});



Here, I have 2 entries that will cause errors / throw exceptions.

When a call:

- $container->get('boom')
- $container->get('cache.apcu')

should I get a 'Psr\Container\Exception\MisconfiguredServiceExceptionInterface' ?

This question is hard to answer! Do we consider that what happens in a factory is part of the container or not?

I would certainly not wrap `$container->get('boom')` in an exception (so it means the container should not be catching Throwable). It makes absolutely no sense as this is most typically an "unchecked" exception. And yet, if you have a look at the wording of the PR, to me, it qualifies as a "misconfiguration" of the container.

So what about the exception thrown by `$container->get('cache.apcu')`? If the container wraps it into a 'MisconfiguredServiceExceptionInterface' exception, this makes catching the initial exception a lot harder.

Have a look at this other service:

// This service returns an APCU cache when available, a file cache service otherwise
$container->set('best.possible.cache', function($container) {
    try {
        return $container->get(cache.apcu);
    } catch (NoApcuExtensionException $e) {
        return $container->get(cache.file);
    }
});


The code above will only work if the container does not catch the exceptions. Otherwise, we would need to write something like:

$container->set('best.possible.cache', function($container) {
    try {
        return $container->get(cache.apcu);
    } catch (MisconfiguredServiceExceptionInterface $e) {
        if ($e->getPrevious() instanceof NoApcuExtensionException) {
            return $container->get(cache.file);
        } else {
            throw $e;
        }
    }
});


I'm sure we all can agree that this is a code-smell :)

So we have a problem because the wording surrounding 'MisconfiguredServiceExceptionInterface' will be extremely hard to get right. It should catch enough, but not too much while not saying exactly what to catch because this is container specific...

Another issue: depending on the container used, the exceptions are not thrown at the same moment. Let's consider a "CyclicDependencyException". It is fair to think it should implement the 'MisconfiguredServiceExceptionInterface'. But in compiled containers, this exception will be thrown at build time (so not when calling `get`). Actually, I expect most exceptions to be thrown at build time for compiled/cached containers, and I expect runtime containers to throw very few exceptions apart the NotFoundException / DependencyMissingException.

So my feeling is that the 'MisconfiguredServiceExceptionInterface' is opening a can of worms. It will be very hard to define precisely what goes in and what goes out of this exception.

And we haven't even heard of a practical use case yet.

On the other end, there is one real impact to adding this exception:

Existing containers implementaing container-interop might need a major version bump to be compatible with PSR-11 (because some exceptions that would previously have bubbled up could be wrapped inside a 'MisconfiguredServiceExceptionInterface')


So there is no real benefit yet demonstrated, it is hard to define and it causes challenge regarding adoption by existing containers. You understood my position, I'm very much against the idea of a 'MisconfiguredServiceExceptionInterface'.


Another solution: a simple `MissingDependencyExceptionInterface`


Because the scope of the 'MisconfiguredServiceExceptionInterface' is really hard to define, we could simply restrict the scope of this exception to "missing dependencies" (that was the idea at the beginning of this thread).

This is a better solution in my opinion because:

- This is directly related to PSR-11 scope: "getting things out of the container". We are dealing with "dependency injection container", this means an entry can have "dependencies" (it is in the name), and if the dependency is missing, it is not illogical to have an exception for this.
- It is really easy to define without ambiguity (no "can of worm" opening here)
- It is easy to migrate existing container-interop containers. All those containers have such an exception. Indeed, when a dependency is missing, the container MUST NOT throw a `NotFoundException`. Therefore, all containers are catching the `NotFoundException` and wrapping it in another `MissingDependencyException`. So implementation is as easy as implementing the `MissingDependencyExceptionInterface` in the existing `MissingDependencyException` of each container.

Yet, there is a drawback here (compared to the current proposal):

We cannot do an "instant migration" of container-interop towards PSR-11 anymore.

So far, when PSR-11 is adopted, we were considering to release of container-interop 1.2 version where Interop\Container\ContainerInterface simply extends Psr\Container\ContainerInterface. Shazam! Instantly at the release of PSR-11, all container-interop containers would have become PSR-11 containers (fastest adoption rate for a PSR ever).

If we add a `MissingDependencyExceptionInterface`, this is no longer possible and every container must add support manually.

Finally

We have 3 possible solutions here:

- Adding a `MisconfiguredServiceExceptionInterface` (I explained above why I find this idea potentially harmful)
- Adding a `MissingDependencyExceptionInterface` (I believe it does not hurt but is mostly useless and preventing us to do "instant migration" of container-interop)
- Keep things as they are

You probably understood I prefer the last solution. We haven't even heard of a practical use case yet for those exception interfaces (in the 3+ years of container-interop, no-one ever complained that this exception interface was missing, see: https://github.com/container-interop/container-interop/issues/3 ). And if we don't add this exception interface, we can do an "instant" migration of all container-interop containers to PSR-11 (which is insanely cool even if this is not an objective of PSR-11 per-se).

But finally, this boils down to one question: can any one provide a good real-world use case to catching one of these 2 exception interfaces?

++
David.
Reply all
Reply to author
Forward
0 new messages