rdlowrey / auryn

IoC Dependency Injector
MIT License
722 stars 65 forks source link

Proposal: Configurations #128

Closed elazar closed 8 years ago

elazar commented 9 years ago

I've implemented this in a separate project and found that it works well. The code involved isn't much, but I think it encourages separation of concerns and reusability in injector configuration. If it seems like it would be better suited for a separate repo/package, fair enough, but I thought I'd introduce it here to see if there's any interest in bringing it into the Auryn core.

interface ConfigurationSet
{
    public function apply(\Auryn\Injector $injector);
}

This is somewhat similar in concept to Silex service providers: it uses classes to encapsulate sets of injector configuration specific to a particular concern, such as integrating a third-party template engine.

Here's an example specific to the Diactoros library:

class DiactorosConfiguration implements ConfigurationInterface
{
    public function apply(Injector $injector)
    {
        $injector->alias(
            'Psr\Http\Message\ResponseInterface',
            'Zend\Diactoros\Response'
        );
        $injector->delegate(
            'Psr\Http\Message\ServerRequestInterface',
            'Zend\Diactoros\ServerRequestFactory::fromGlobals'
        );
    }
}

Another cool thing this allows for is groupings of configurations using something like this:

class ConfigurationSet implements ConfigurationInterface
{
    protected $classes;
    public function __construct(array $classes)
    {
        $this->validateClasses($classes);
        $this->classes = $classes;
    }
    public function apply(\Auryn\Injector $injector)
    {
        foreach ($this->classes as $class) {
            $configuration = $injector->make($class);
            $configuration->apply($injector);
        }
    }
    protected function validateClasses(array $classes)
    {
        $invalid = array_filter(
            $classes,
            function ($class) {
                return !is_subclass_of($class, ConfigurationInterface::class);
            }
        );
        if ($invalid) {
            $message = 'Classes cannot be loaded or do not implement ConfigurationInterface: ' . implode(', ', $invalid);
            throw new \DomainException($message);
        }
    }
}

So you can then do something like this:

$injector = new \Auryn\Injector;
$configuration = new ConfigurationSet([
    'My\\Configuration\\DiactorosConfiguration',
    'My\\Configuration\\RelayConfiguration',
    // ...
]);
$configuration->apply($injector);

Or even this:

namespace My;
class DefaultConfigurationSet extends ConfigurationSet
{
    public function __construct()
    {
        parent::__construct([
            'My\\Configuration\\DiactorosConfiguration',
            'My\\Configuration\\RelayConfiguration',
            // ...
        ]);
    }
}
$injector = new \Auryn\Injector;
$configuration = new \My\DefaultConfigurationSet;
$configuration->apply($injector);

Thoughts?

elazar commented 8 years ago

It's been 8 months with no reply to this issue, so I'm going to assume that there's no interest in supporting this proposal in Auryn core. As such, I've created a supplemental library for this purpose and am closing this issue.