Completed
Pull Request — master (#12)
by Woody
08:34
created

Config::injectFactories()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 10

Duplication

Lines 3
Ratio 20 %

Code Coverage

Tests 12
CRAP Score 3

Importance

Changes 0
Metric Value
dl 3
loc 15
ccs 12
cts 12
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 10
nc 3
nop 2
crap 3
1
<?php
2
declare(strict_types=1);
3
4
namespace Northwoods\Container\Zend;
5
6
use Auryn\Injector;
7
use ArrayObject;
8
use Northwoods\Container\ContainerException;
9
use Northwoods\Container\InjectorConfig;
10
use Psr\Container\ContainerInterface;
11
12
class Config implements InjectorConfig
13
{
14
    /** @var array */
15
    private $config;
16
17
    /** @var bool */
18
    private $sharedByDefault = true;
19
20 160
    public function __construct(array $config)
21
    {
22 160
        $this->config = $config;
23 160
    }
24
25
    /**
26
     * Configure the injector using Zend Service Manager format
27
     */
28 160
    public function apply(Injector $injector): void
29
    {
30 160
        $dependencies = $this->config['dependencies'] ?? [];
31
32
        // Define the "config" service, accounting for the fact that Auryn
33
        // requires all returns are objects.
34 160
        $dependencies['services']['config'] = new ArrayObject($this->config, ArrayObject::ARRAY_AS_PROPS);
35
36 160
        $this->injectServices($injector, $dependencies);
37 160
        $this->injectFactories($injector, $dependencies);
38 160
        $this->injectInvokables($injector, $dependencies);
39 160
        $this->injectAliases($injector, $dependencies);
40 160
    }
41
42 160 View Code Duplication
    private function injectAliases(Injector $injector, array $dependencies): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
43
    {
44 160
        $aliases = $dependencies['aliases'] ?? [];
45 160
        foreach ($aliases as $alias => $target) {
46
            // Standard Auryn aliases do not work when chained. Work around by
47
            // lazily fetching the shared target from the container.
48 70
            $injector->share($alias)->share($target)->delegate($alias, $this->makeLazy($target));
49
        }
50 160
    }
51
52 160
    private function injectFactories(Injector $injector, array $dependencies): void
53
    {
54 160
        $factories = $dependencies['factories'] ?? [];
55 160
        foreach ($factories as $name => $factory) {
56 110
            $delegate = function () use ($injector, $name, $factory) {
57 68
                $container = $injector->make(ContainerInterface::class);
58 68
                $factory = $this->makeFactory($name, $factory);
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $factory, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
59 58
                return $factory($container, $name);
60 110
            };
61 110 View Code Duplication
            if (isset($dependencies['delegators'][$name])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
62 84
                $delegate = $this->makeDelegator($injector, $name, $delegate, $dependencies['delegators'][$name]);
63
            }
64 110
            $injector->share($name)->delegate($name, $delegate);
65
        }
66 160
    }
67
68 160
    private function injectInvokables(Injector $injector, array $dependencies): void
69
    {
70 160
        $invokables = $dependencies['invokables'] ?? [];
71 160
        foreach ($invokables as $alias => $invokable) {
72 42
            if (is_string($alias) && $alias !== $invokable) {
73 14
                $injector->alias($alias, $invokable);
74
            }
75 42
            $delegate = static function () use ($invokable) {
76 29
                return new $invokable();
77 42
            };
78 42 View Code Duplication
            if (isset($dependencies['delegators'][$invokable])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
79 26
                $delegate = $this->makeDelegator($injector, $invokable, $delegate, $dependencies['delegators'][$invokable]);
80
            }
81 42
            $injector->share($invokable)->delegate($invokable, $delegate);
82
        }
83 160
    }
84
85 160 View Code Duplication
    private function injectServices(Injector $injector, array $dependencies): void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
86
    {
87 160
        $services = $dependencies['services'] ?? [];
88 160
        foreach ($services as $name => $service) {
89 160
            $injector->share($name)->delegate($name, $this->makeIdentity($service));
90
        }
91 160
    }
92
93
    private function makeDelegator(Injector $injector, string $name, callable $callback, array $delegators): callable
94
    {
95 110
        return function () use ($injector, $name, $callback, $delegators) {
96 110
            foreach ($delegators as $delegator) {
97 95
                $container = $injector->make(ContainerInterface::class);
98 95
                $delegator = $this->makeFactory($name, $delegator);
99 50
                $instance = $delegator($container, $name, $callback);
100 40
                $callback = $this->makeIdentity($instance);
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $callback, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
101
            }
102 55
            return $instance ?? $callback();
103 110
        };
104
    }
105
106
    /**
107
     * @param string|callable $factory
108
     */
109 133
    private function makeFactory(string $name, $factory): callable
110
    {
111 133
        if (is_callable($factory)) {
112 58
            return $factory;
113
        }
114
115 99
        if (is_string($factory) && !class_exists($factory)) {
116 19
            throw ContainerException::expectedInvokable($name);
117
        }
118
119 82
        $factory = new $factory();
120
121 66
        if (is_callable($factory)) {
122 50
            return $factory;
123
        }
124
125 18
        throw ContainerException::expectedInvokable($name);
126
    }
127
128
    private function makeIdentity($object): callable
129
    {
130 160
        return static function () use ($object) {
131 24
            return $object;
132 160
        };
133
    }
134
135
    private function makeLazy(string $name): callable
136
    {
137 70
        return static function (ContainerInterface $container) use ($name) {
138 70
            return $container->get($name);
139 70
        };
140
    }
141
}
142