On Sat, Mar 9, 2024, at 11:42 AM, Rasmus Schultz wrote:
> Hey Larry,
>
> Per chance, have you run into discussions elsewhere about this 
> alternative interface?
>
> service-provider/src/ServiceProviderInterface.php at 0.5.0 · 
> mindplay-dk/service-provider · GitHub 
> <https://github.com/mindplay-dk/service-provider/blob/0.5.0/src/ServiceProviderInterface.php>
>
> It's a refactored version of the previously proposed interface, 
> designed to work better for compiled containers.
>
> It avoids the use of callables, and the need to registration at run-time.
>
> A compiled container can retrieve the list of services/extensions at 
> compile-time, resolve service overrides among providers, etc. avoiding 
> this overhead at run-time.
>
> In practice, this would reduce the run-time overhead for compiled 
> containers (versus their own internal providers) to a single 
> function-call per service/extension.
>
> So it's not zero overhead, but it is reasonably close - perhaps as 
> close as you can get, and considerably less overhead compared with the 
> previous proposal.
>
> A hand-written or compiled service provider can use the match-based 
> approach, as you illustrated above.
>
> In addition, a compiled container/builder can make a generated provider 
> available, retaining the full performance of the compiled factory, if 
> used to provide services to other (possibly non compiled) containers.
>
> I mean, short of creating a standard that is fully declarative (e.g. an 
> XML schema) I believe this is the best we can expect in terms of 
> performance, isn't it? If it involves an interface, you can't really 
> reduce the overhead to less than a function call.
>
> Whether a PSR in this space ever happens, is essentially up to you, 
> Larry. No other core members have shown any interest in this, and 
> realistically, it's not going to go any further (and no one is going to 
> take this seriously or bother participating) without a formal working 
> group and a PSR number.
>
> I would love to see this come to fruition - but unless a core member 
> steps up to support this, I should probably just give up and move on 
> myself.
>
> What do you think, is there any chance this will ever happen, or am I 
> just spinning my wheels here? :-)
>
> Regards,
>   Rasmus Schultz

Well, we'd need more than just a CC member to sponsor it.  We'd also need at 
least 3 other people for a working group, preferably from projects that publish 
popular DICs (Symfony, PHP-DI, etc.)  

Korvin and I talked a little in Discord.  To summarize my position, and the 
challenge I see: There's a couple of standard features of DI Containers these 
days, which if we're going to standardize registration at all need to be fully 
considered.  However, some of them are at odds with each other.

1. Compiled containers.

These should be as fast as possible.  The gold standard right now (not all do 
this, but the good ones do) is to go all the way down to something like this:

class CompiledContainer {
  private array $services = [];

  public function get(string $id): mixed {
    return $this->services[$id] ??= return match($id) {
        ClassA::class => new ClassA(),
        ClassB::class => new ClassB($this->get(ClassA::class),
        InterfaceC::class => $this->get(ClassB::class),
        // ...
    }
  }
}

Yes there's cases where you may also need additional method calls for setup, 
but those can be handled. This is just an example, but a design that allows 
this to be produced is, IMO, mandatory.

2. Runtime configured containers.

These are also common (even in cases where they shouldn't be, IMO, like 
Laravel).  For many lower-traffic use cases they're much easier to work with, 
and fast enough.  The quintessential example here is Pimple, which shows just 
how trivially easy this can be if you don't need much functionality.

3. Complex/contextual creation.

This is usually handled by a factory method, generally another service.  This 
is also an important use case, even if not the typical one.

4. Peer-manipulation.

(This is a bad name, please come up with a better one.)  In some cases, one 
dependency wants to manipulate the DI configuration of another dependency.  
This could be to add a method call to the setup routine, or to build up an 
array of other services to use as a constructor dependency, etc.  Symfony 
solves this with Compiler Passes, and in practice I don't think there's a 
better option, give or take implementation details.


Here's the problem: For compiled containers to work properly, you really have 
to use an AST of container definitions that can then be compiled down into the 
appropriate code, and you cannot have any closures or instantiated objects in 
the process.  Without that, you're adding at absolute minimum an extra function 
call to every lookup, which adds up when there's hundreds of services.  
Potentially much more if you're doing any kind of complex/contextual creation 
or peer-manipulation.

The AST approach is basically what Symfony does today, although I find their 
interface and data model for it rather clumsy.  (Due largely to it being 
defined in PHP 5.3, when the language was a lot weaker.)

However, an AST approach is necessarily more complex, and while it could be 
implemented by a runtime container it would likely be slower than just passing 
everything a factory closure and moving on.

An AST approach does not necessarily mean YAML or XML files.  The smart way to 
do file-based configuration would be to have the config files translate to the 
AST, so that it can be unified with any code-provided definitions.  A config 
file would be just another peer-manipulation.

(Which is, yes, exactly how Symfony works.  I keep coming back to it being the 
right architectural design with a lackluster API.)

I don't know how to resolve this tension between compiled and runtime 
containers.

But here's why it matters:

If a PSR standard puts forth a runtime-optimized registration design, one based 
on factories, then any libraries that use it to expose their dependencies 
necessarily are hamstrung in a compiled container situation.  That's not good.  
OTOH, if we put forward a compile-optimized design, it makes supporting runtime 
containers more difficult and slower.  And we should assume that the majority 
of services eventually get registered via whatever we come up with, so "it's 
just for a few services, it won't make a difference" doesn't really fly.  (If 
it's only used in a few edge cases, then we have failed with the PSR.)

Rasmus' latest, if I understand it, is somewhat better, as it essentially tells 
each provider to have its own match() block or equivalent.  That's not as bad 
as having the provider return an array of factories, but it still adds at least 
one method call to every lookup, and if extensions are used (I don't fully 
understand those), it's several more.

We don't need to resolve these issues right now.  We need to agree that they 
need to be solved, and find people interested in solving them.  As a reminder, 
the purpose of a Working Group is to have these discussions in a focused way 
with the experts in a given field, not to throw it out to a list of however 
many people, most of whom are idle. :-)  All we need for a WG is a statement of 
purpose and people willing to work on it.  The latter is the challenge for now.

Wearing my Core Committee hat, I would vote Yes on a service registration 
working group, if the interested parties can be assembled.  I would vote no on 
a final result that doesn't adequately address the tension I outline above.  
What that solution is, well, that's up for the working group to figure out.

So the task for now isn't figuring out the solution, it's assembling a working 
group.

--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 view this discussion on the web visit 
https://groups.google.com/d/msgid/php-fig/e43c1a77-f2b2-4be8-acdc-a9119b42f973%40app.fastmail.com.

Reply via email to