annotated-container
annotated-container copied to clipboard
Allow profiles to be assigned a priority
Problem
There's currently a drawback when using profiles to resolve multiple aliases. You're required to specify a profile for all concrete services that would be used as an alias for an abstract service. There are scenarios where it would be preferable not to have to declare profiles on every concrete implementation. Take the following example:
<?php
use Cspray\AnnotatedContainer\Attribute\Service;
#[Service]
interface Widget {}
#[Service]
class StandardWidget implements Widget {}
#[Service(profiles: ['test'])
class TestWidget implements Widget {}
In this example the intent of the annotations is that if the list of active profiles includes test
we should use TestWidget
otherwise we should use the StandardWidget
. Right now our implementation doesn't allow this and would require you to specify on the StandardWidget
a profile that would be used in all non-test situations, depending on how your profiles are setup this could cause you to list every single profile other than test. This is not a good experience.
Conceptually this problem also exists when needing to disambiguate multiple #[Inject]
definitions.
Solution
Whenever we are required to disambiguate multiple aliases or inject definitions we would gather the set of ambiguous definitions. An implicit, per-set priority is assigned to all annotations that make use of profiles. An annotation that does not explicitly set a profile would have a priority of 0
. Each explicitly set, active profile would increase the priority for that annotation by 1
. If there is a single annotation with the highest priority it would be chosen. If there are multiple annotations with the highest priority we would continue to handle this however the app is currently handling it.
For example, given the code example above, if the active profiles were default, test
then the StandardWidget
would be given a priority of 0, because it does not explicitly set a profile, and the TestWidget
would be given a priority of 1, because it has 1 profile explicitly set. The TestWidget
would be used. If the active profiles were default, prod
then the StandardWidget
would continue to receive a 0 priority but the TestWidget
would no longer be a candidate, because the active profiles would preclude it.