Closed gmazzap closed 1 year ago
As discussed, for me this apporach is totally fine. 👍🏻
Naming: are ContainerCompiler for the interface and ReadOnlyContainerCompiler for the implementation good names? Better alternatives?
For me Compiler, Builder and Configurator is right here. We choosed when creating this library "Configurator", eventho we internally always said "Compile the PSR Container".
Do you agree in renaning current ContainerConfigurator?
I would rename it + move it out of the Container
-Domain into an own one ( Compiler
?). The Container\ContainerConfigurator
is only used internally and never accessed externally. Eventho renaming/moving is a breaking change...no one accesses that internal code.
What do you think of the "alternative" implementation (using ContainerDecorator)? + In a scale of 0 (not helpful at all) to 5 (fundamental) how much think this PR would be helpful in projects you're working on?
Could be also a feasible solution. TBH I've no real world use case right now where I would need that kind of "Decorator" or "custom Compiler". Everything works fine for me and the Package
itself provides enough information via Package::moduleStatus()
. Everything else like tracing, logging or debugging shouldn't be really done inside of the Container
when resolving/registering Services. This could have an even bigger performance impact.
Base: 96.24% // Head: 95.37% // Decreases project coverage by -0.87%
:warning:
Coverage data is based on head (
09d19e0
) compared to base (65bc3f8
). Patch coverage: 95.91% of modified lines in pull request are covered.
:umbrella: View full report at Codecov.
:loudspeaker: Do you have feedback about the report comment? Let us know in this issue.
@Chrico
I would rename it + move it out of the Container-Domain into an own one ( Compiler ?).
Why that? The "compiler" is not a "generic" compiler. It is a container compiler. So to me, it belongs semantically to the container.
It is only used internally and never accessed externally. Eventho renaming/moving is a breaking change...no one accesses that internal code.
Yes, but you don't know who outside of the library is using it and how. If you rename a class, a new major is required. So IMO, we should decide if to keep the current ContainerConfigurator
so we can avoid having a major release or go with a new major by deleting ContainerConfigurator
.
In that case, the problem is that we probably want to maintain only the latest major, so many packages using Modularity will be using an unmaintained version.
Could be also a feasible solution. TBH I've no real world use case right now where I would need that kind of "Decorator" or "custom Compiler".
Me neither, but after I've played a bit with possible usages of this PR, I think the compiler provides better flexibility. So if we decide to merge this, I think we should use the "compiler" approach and not the "decorator" approach.
After some more internal discussion, we decided to close this.
Please check if the PR fulfills these requirements
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
Feature.
What is the current behavior? (You can also link to an open issue here)
See details below.
What is the new behavior (if this is a feature change)?
See details below.
Does this PR introduce a breaking change? (What changes might users need to make in their application due to this PR?)
No.
The issue
Currently, Modularity uses an hardcoded depency to the
ContainerConfigurator
class to "collect" factories, services, extensions, and child containers, and then builds an instance of theReadOnlyContainer
PSR-11 implementation.While this works most of the time, it make it impossible to use different PSR-11 implementation, for example to "decorate" the given container for debugging, logging, or similar things.
The proposed solution
The idea is to have a
ContainerCompiler
interface of which the currentContainerConfigurator
would be one implementation (only implementation shipped with Modularity).With an interface, consumers could provide their implementation and build a PSR-11 container as they like.
While the compiler enables very flexible operation, we believe most of the time the returned container should extend or decorate the provided
ReadOnlyContainer
to avoid breaking the package's "contract" with modules.The implementation
To not break backward compatibility with the existing
Package::new()
method, we introduce a newPackage::newWithCompiler()
static constrcutor, that instead of accepting a number fo child container accepts instead a ready-made Compiler.With such approach
Package::new($properties, $container1, $container2)
becomes equivalent to:Package::newWithCompiler( $properties, new ReadOnlyContainerCompiler($container1, $container2))
, but consumers can usePackage::newWithCompiler()
to pass a different implementation of the newContainerCompiler
interface.The default implemenation has been renamed from
ContainerConfigurator
toReadOnlyContainerCompiler
, but a class with the old name (extending the one with new name) is kept for backward compatibility. It is deprecated and will be removed in next major.Use case examples
A filtered container
Do you like filters, don't you?
Automatic
LoggerAware
resolutionAs soon as a
LoggerInterface
object is available in the container, it injects it to everyLoggerAware
class that is resolved.Other notes
Currently, the
ContainerConfigurator
holds a container instance as property, and do not re-compile the container if it is already compiled. Now that we have an interface, we can't be sure the implementation will hold a property as well, and we don't want to risk having multiple container instances, or we defeat the benefits of having a container at all. So the property is moved to package.The compiler is inject to the contructor and can't be changed later because it is needed right in the constructor, at least if we want to keep the
Package::new()
signature that accepts "child" containers. Otherwise we would not know where to store the child container nor the "properties" object. Even if we changed thePackage::new()
signature making an "impactful" breaking change, the compiler would still be needed before anyaddModule()
call, hence the compiler "setter" would need to be called right after construction anyway. So we decided that it just make sense to accept it in a new static constructor an that's it.Thanks to the fact we were wise enough to use a private constructor for the
Package
class, we can implement this change in a 100% backward compatible way.Renaming
ContainerConfigurator
had been a breaking change. We can decide to have a new major and that's it, after all we don't expectContainerConfigurator
to be used in many places as independent object, or we can keep that class as deprecated and extend the new class. So far, this PR takes the latter approach, but that's not set in stone.This PR includes unit tests and documentaion, which might be worth a read: https://github.com/inpsyde/modularity/blob/feature/container-compiler-interface/docs/PSR-11-Container.md
Alternatives
Like the examples above shows, most of the usage of the compiler we can think of is about decorating the default
ReadOnlyContainer
. An alternative we considered si to have aContainerDecorator
object, configurable to package via a setter, which if provided could be used to decorate the default container, having a single method with signatureContainerDecorator::decorate(ContainerInterface $c): ContainerInterface
.That would be less impactful because we don't need a new constructor nor any deprecation and renaming of
ContainerConfigurator
which would stay just as is.On the other had, it would be less flexible, and won't solve the hardcoded
ContainerConfigurator
dependency which isn't exaclty an example of good software design.Feedback wanted
Of course, like any other PR, this is open to any kind of feedback.
However, here's below a few questions that could help the conversation:
ContainerCompiler
for the interface andReadOnlyContainerCompiler
for the implementation good names? Better alternatives?ContainerConfigurator
? If so, do you think we should keep the class file as deprecated for a while, or we should delete it and release a major?ContainerDecorator
)?