Permalink
Browse files

Merge pull request #810 from moufmouf/optional_containerexception

[PSR-11] Refining exception definitions
  • Loading branch information...
2 parents 211063e + cba5ea3 commit 38ef9fead831961b0a890187d27c945648058d76 @moufmouf moufmouf committed on GitHub Sep 16, 2016
Showing with 65 additions and 4 deletions.
  1. +55 −0 proposed/container-meta.md
  2. +10 −4 proposed/container.md
View
55 proposed/container-meta.md
@@ -254,6 +254,61 @@ object that would describe how to create an instance.
The conclusion of the discussion was that this was beyond the scope of getting entries from a container without
knowing how the container provided them, and it was more fit for a factory.
+### 7.3. Exceptions thrown
+
+This PSR provides 2 interfaces meant to be implemented by container exceptions.
+
+#### 7.3.1 Base exception
+
+The `Psr\Container\Exception\ContainerExceptionInterface` is the base interface. It SHOULD be implemented by custom exceptions thrown directly by the container.
+
+It is expected that any exception that is part of the domain of the container implements the `ContainerExceptionInterface`. A few examples:
+
+- if a container relies on a configuration file and if that configuration file is flawed, the container might throw an `InvalidFileException` implementing the `ContainerExceptionInterface`.
+- if a cyclic dependency is detected between dependencies, the container might throw an `CyclicDependencyException` implementing the `ContainerExceptionInterface`.
+
+However, if the exception is thrown by some code out of the container's scope (for instance an exception thrown while instantiating an entry), the container is not required to wrap this exception in a custom exception implementing the `ContainerExceptionInterface`.
+
+A [discussion about the usefulness of the base exception](https://groups.google.com/forum/#!topic/php-fig/_vdn5nLuPBI) was held on the PHP-FIG's discussion group
+
+#### 7.3.2 Not found exception
+
+A call to the `get` method with a non-existing id must throw an exception implementing the `Psr\Container\Exception\NotFoundExceptionInterface`.
+
+There is a strong relationship with the behaviour of the `has` method.
+
+For a given identifier:
+
+- if the `has` method returns `false`, then the `get` method MUST throw a `Psr\Container\Exception\NotFoundExceptionInterface`.
+- if the `has` method returns `true`, then the `get` method MUST NOT throw a `Psr\Container\Exception\NotFoundExceptionInterface`. However, this does not mean that the `get` method will succeed and throw no exception.
+
+When discussing the `ǸotFoundException`, a question arose to know whether the `NotFoundExceptionInterface` should have a `getMissingIdentifier()` method allowing the user catching the exception to know which identifier was not found.
+Indeed, a `ǸotFoundExceptionInterface` may have been triggered by a call to `get` in one of the dependencies, which is different from a call to `get` on a non existing identifier.
+
+After some discussion, it was decided that the `getIdentifier` method was not needed. Instead, it is important to stress out that the `get` method of the container SHOULD NOT throw a `NotFoundExceptionInterface` in case of a missing dependency. Instead, the container is expected to wrap the `NotFoundExceptionInterface` into another exception simply implementing the `ContainerExceptionInterface`.
+
+In pseudo-code, a correct implementation of `get` should look like this:
+
+~~~php
+public function get($identifier) {
+ if (identifier not found) {
+ throw NotFoundException::entryNotFound($identifier);
+ }
+ try {
+ // Do instantiation work
+ // Note: this instantiation work triggers additional calls to `get` for dependencies
+ // Returns the entry
+ } catch (NotFoundExceptionInterface $e) {
+ // Wrap the NotFoundExceptionInterface into another exception that does not implement the `NotFoundExceptionInterface`.
+ throw new MissingDependencyException('some text', 0, $e);
+ }
+}
+~~~
+
+With this rule in place, a user of a container can safely know that a `NotFoundExceptionInterface` means the identifier he provided to the `get` method is missing, and not that some dependency is missing.
+
+Behaviour of the `NotFoundException` was discussed in [container-interop's issue #37](https://github.com/container-interop/container-interop/issues/37).
+
## 8. Delegate lookup feature
### 8.1. Purpose of the delegate lookup feature
View
14 proposed/container.md
@@ -24,7 +24,7 @@ Users of dependency injections containers (DIC) are referred to as `user`.
- The `Psr\Container\ContainerInterface` exposes two methods : `get` and `has`.
- `get` takes one mandatory parameter: an entry identifier. It MUST be a string.
- A call to `get` can return anything (a *mixed* value), or throws an exception if the identifier
+ A call to `get` can return anything (a *mixed* value), or throws a `NotFoundExceptionInterface` if the identifier
is not known to the container. Two successive calls to `get` with the same
identifier SHOULD return the same value. However, depending on the `implementor`
design and/or `user` configuration, different values might be returned, so
@@ -35,16 +35,22 @@ Users of dependency injections containers (DIC) are referred to as `user`.
- `has` takes one unique parameter: an entry identifier. It MUST return `true`
if an entry identifier is known to the container and `false` if it is not.
`has($id)` returning true does not mean that `get($id)` will not throw an exception.
- It does however mean that `get($id)` will not throw a `NotFoundException`.
+ It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
### 1.2 Exceptions
-Exceptions directly thrown by the container MUST implement the
+Exceptions directly thrown by the container SHOULD implement the
[`Psr\Container\Exception\ContainerExceptionInterface`](#container-exception).
A call to the `get` method with a non-existing id MUST throw a
[`Psr\Container\Exception\NotFoundExceptionInterface`](#not-found-exception).
+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`.
+
### 1.3 Recommended usage
Users SHOULD NOT pass a container into an object so that the object can retrieve *its own dependencies*.
@@ -118,7 +124,7 @@ interface ContainerInterface
*
* @param string $id Identifier of the entry to look for.
*
- * @throws NotFoundExceptionInterface No entry was found for this identifier.
+ * @throws NotFoundExceptionInterface No entry was found for **this** identifier.
* @throws ContainerExceptionInterface Error while retrieving the entry.
*
* @return mixed Entry.

0 comments on commit 38ef9fe

Please sign in to comment.