On 10/26/2016 08:28 AM, Matthew Weier O'Phinney wrote:
Hello, everyone!

PSR-11, Container Interface (née container-interop) has been in incubation for a
couple years now, and the editor and sponsors feel it is ready for review. As
coordinator, I hereby open the mandatory review period prior to a formal
acceptance vote; voting will begin no earlier than 13:30 UTC on Wednesday,
November 9th, 2016.

For the current version, please reference:

- 
https://github.com/php-fig/fig-standards/blob/a47c644f9d0f914bb0a9777eeaec157f2d51dbff/proposed/container.md
- 
https://github.com/php-fig/fig-standards/blob/a47c644f9d0f914bb0a9777eeaec157f2d51dbff/proposed/container-meta.md

The related library containing the interfaces is here:

- https://github.com/php-fig/container

Additionally, a large number of projects either implement or consume
container-interop already, and would be (relatively) immediately compatible:

- https://github.com/container-interop/container-interop#compatible-projects

Please familiarize yourself with the specification and its scope, and reply on
this thread with any concerns.

Thanks!
Other thoughts, in a separate reply as they're not directly related to the other discussion, not really in any special order:

* The spec should specify what legal characters are for an entry identifier, and what if any reserved characters there are. It should also specify minimum supported key length and character sets, for completeness. I recommend borrowing PSR-6's language here, which I believe addresses this area well. Whether it uses the same reserved character list or another one I don't feel strongly about. I would not consider this a Review-breaking change.

* The allowance for additional parameters on get() is worrisome. That implies ANY use of a second or further parameter sets me up for an incompatibility, but implementations are free to add those implementation-specific hooks that I can couple to. I would much prefer to omit that allowance entirely. (Technically optional parameters cannot be prevented, but we shouldn't encourage it.)

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

* 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.

* The text of the spec says that the psr/container package doesn't exist yet, but Matthew's post above links to it. :-)

* Section 4 of the metadoc: I like!  Thank you.

* I don't believe any other spec put exceptions into their own sub-namespace. That seems unnecessary to me, and I'd prefer to remove that sub-namespace and just leave the exceptions in Psr/Container. There's no reason to have this inconsistency.

* In metadoc 7.3.2, there's this line:

throw NotFoundException::entryNotFound($identifier);

Which seems to come out of nowhere. The interface doesn't define a static method factory. The hypothetical MissingDependencyException later in the method just uses the constructor. The example here should do the same and use the constructor.

Now for the big one:

Metadoc 8.2 seems redundant with section 1.4 of the spec, as in, nearly identical. It doesn't add anything so should just be removed. Assume someone reading the metadoc has already read the spec and has questions about it.

The section on delegate containers strikes me as very odd. It describes behavior that is... inherently possible with any interface of almost any kind. Composing other objects of the same interface is one of the core features of interfaces, so describing it here is almost entirely redundant, especially when no actual mechanism for such composition is defined. I understand that such composition is a key feature of the spec, but the spec doesn't actually define how to do it other than "ya know, it's possible because interfaces". That seems needlessly redundant.

What it does seem to define is that delegates should be looked up exclusively for indirect dependencies. Which... does not make any sense to me at all.

Reading both section 1.4 of the spec and 8 of the metadoc, I am still confused about how I would "properly" implement the delegation concept at all. The example (with the pictures) assumes the composite container is empty of its own services, and is just delegating in order to other containers. The spec, however, says that get() should operate on self, but dependencies on child containers. That implies the composite container has its own services. The metadoc discussion (8.7) talks as though that's temporary until the rest of the market catches on and gets around to just providing empty composite containers... but then the spec still doesn't tell me what I'm supposed to do in this case.

Moreover, the spec and metadoc are inconsistent. The spec says that a container must

- provide some way to specify child (delegate) containers (which could vary by implementation)
- call a direct-get() on self only
- call a dependency-get() on child only
- call has() on self only
- But maybe will dependency-get() on self, if they really mean it

This last point is highly troublesome as it makes behavior unpredictable.

However, the metadoc in section 8.3 says the expected usage is:

- A composite container (new term) forwards all get() calls to its child containers until one is found - A non-composite container forwards all dependency-get() calls to its... parent composite container
- Loop until done

That's not at all the same logic. As a potential implementer, I have no idea what I'm supposed to do if my composite container has its own services. Do I forward them or not? Also, can any container be a composite container or no? Can a composite container be nested inside another? If so, to where do I forward get() calls?

The spec refers only to a delegate container, but I'm using the terms parent and child. That's because when you have a reference from one container to another, that's a parent-child relationship (object with the reference to the object being referenced). What it appears needs to happen for what the metadoc describes (which is not what the spec describes) is for the composite container to have a reference to all of its sub-containers, and then the sub-containers to also have a reference back to it in order to delegate their dependency-get() calls. That creates a circular reference. While those are not the memory leak they once were in PHP, circular references should almost always be seen as a code smell. That I do not know how to implement this pattern without them is a sign that something is very wrong.

Moreover, it seems to suggest that I cannot nest containers indefinitely. If Container2 has multiple delegate containers defined, then... what happens? It must, or else CompositeContainer is fundamentally different than a Container, but it's not defined in the spec.

Now, consider the case where container2 delegates to composite container but the service it's looking for doesn't exist anywhere. CompositeContainer would have to call has() on each sub-container in turn, and then call get() if found. But has() specifically doesn't delegate, so they can't have nested containers. So, nested containers aren't really banned, just not actually possible to implement, but that's not stated.

Moreover, all of this logic seems to then exist in get() or sub-calls therein. That makes get() considerably more expensive with multiple internal calls and branch points. From Drupal I know that the container's lookup process is very much along the critical path of the application; adding even one method call to a get() lookup results in, for our case, hundreds of additional stack calls (not cheap in PHP) over the course of a request. That's a performance hit we would be unwilling to swallow.

At this point I'm completely lost, and write this portion of the spec off as unimplementable. Clearly there are some implementations that are doing something with it, as noted in the metadoc. However, from the spec and metadoc itself, which is all I am voting on (I did not look at the linked discussion threads either, as those are not what I'm voting on and the purpose of the metadoc is to summarize those in one place), I don't know how I'd even implement this correctly, or in a manner that would let me assemble different containers that didn't know about each other without intimately knowing about all of them, and even then it would be creating an object graph with multiple circular references, which is a code smell.

Quite simply, as written I cannot vote for this spec with the delegate logic as ill-defined as it is, and the only way I can see to even try to implement it resulting in such a slow and loopy object and call graph.

I will leave these comments here for response before suggesting any alternatives.

--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.
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/8a66ddd5-c4ae-282e-f02d-cbf539c3b714%40garfieldtech.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to