Completed
Pull Request — master (#12)
by Woody
01:27
created

Config::injectFactories()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 11

Duplication

Lines 3
Ratio 18.75 %

Code Coverage

Tests 12
CRAP Score 3

Importance

Changes 0
Metric Value
dl 3
loc 16
ccs 12
cts 12
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 11
nc 3
nop 2
crap 3
1
<?php
2
declare(strict_types=1);
3
4
namespace Northwoods\Container\Zend;
5
6
use ArrayObject;
7
use Auryn\Injector;
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 235
    public function __construct(array $config)
18
    {
19 235
        $this->config = $config;
20 235
    }
21
22
    /**
23
     * Configure the injector using Zend Service Manager format
24
     */
25 235
    public function apply(Injector $injector): void
26
    {
27 235
        $dependencies = $this->config['dependencies'] ?? [];
28
29
        // Define the "config" service, accounting for the fact that Auryn
30
        // requires all returns are objects.
31 235
        $dependencies['services']['config'] = new ArrayObject($this->config, ArrayObject::ARRAY_AS_PROPS);
32
33 235
        $this->injectServices($injector, $dependencies);
34 235
        $this->injectFactories($injector, $dependencies);
35 235
        $this->injectInvokables($injector, $dependencies);
36 235
        $this->injectAliases($injector, $dependencies);
37 235
    }
38
39 235 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...
40
    {
41 235
        $aliases = $dependencies['aliases'] ?? [];
42 235
        foreach ($aliases as $alias => $target) {
43
            // Standard Auryn aliases do not work when chained. Work around by
44
            // lazily fetching the shared target from the container.
45 132
            $injector->share($alias)->share($target)->delegate($alias, $this->makeContainerGet($target));
46
        }
47 235
    }
48
49 235
    private function injectFactories(Injector $injector, array $dependencies): void
50
    {
51 235
        $factories = $dependencies['factories'] ?? [];
52 235
        foreach ($factories as $name => $factory) {
53 150
            $delegate = $this->makeFactory($injector, $name, $factory);
54 150 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...
55 96
                $delegate = $this->makeDelegator(
56 96
                    $injector,
57 96
                    $name,
58 96
                    $delegate,
59 96
                    $dependencies['delegators'][$name]
60
                );
61
            }
62 150
            $injector->share($name)->delegate($name, $delegate);
63
        }
64 235
    }
65
66 235
    private function injectInvokables(Injector $injector, array $dependencies): void
67
    {
68 235
        $invokables = $dependencies['invokables'] ?? [];
69 235
        foreach ($invokables as $alias => $invokable) {
70 77
            if (is_string($alias) && $alias !== $invokable) {
71 29
                $injector->alias($alias, $invokable);
72
            }
73 77
            $delegate = $this->makeLazyInvokable($invokable, $invokable);
74 77 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...
75 49
                $delegate = $this->makeDelegator(
76 49
                    $injector,
77 49
                    $invokable,
78 49
                    $delegate,
79 49
                    $dependencies['delegators'][$invokable]
80
                );
81
            }
82 77
            $injector->share($invokable)->delegate($invokable, $delegate);
83
        }
84 235
    }
85
86 235 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...
87
    {
88 235
        $services = $dependencies['services'] ?? [];
89 235
        foreach ($services as $name => $service) {
90 235
            $injector->share($name)->delegate($name, $this->makeIdentity($service));
91
        }
92 235
    }
93
94
    private function makeDelegator(Injector $injector, string $name, callable $callback, array $delegators): callable
95
    {
96 145
        return /** @return object */ function () use ($injector, $name, $callback, $delegators) {
97 145
            foreach ($delegators as $delegator) {
98 127
                $container = $injector->make(ContainerInterface::class);
99 127
                $delegator = $this->makeInvokable($name, $delegator);
100 73
                $instance = $delegator($container, $name, $callback);
101 53
                $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...
102
            }
103 71
            return $instance ?? $callback();
104 145
        };
105
    }
106
107
    private function makeContainerGet(string $name): callable
108
    {
109 132
        return /** @return object */ static function (ContainerInterface $container) use ($name) {
110 132
            return $container->get($name);
111 132
        };
112
    }
113
114
    /**
115
     * @param string|callable $factory
116
     */
117
    private function makeFactory(Injector $injector, string $name, $factory): callable
118
    {
119 150
        return /** @return object */ function () use ($injector, $name, $factory) {
120 102
            $container = $injector->make(ContainerInterface::class);
121 102
            $factory = $this->makeInvokable($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...
122 78
            return $factory($container, $name);
123 150
        };
124
    }
125
126
    /**
127
     * @param object $object
128
     */
129
    private function makeIdentity($object): callable
130
    {
131 235
        return /** @return object */ static function () use ($object) {
132 27
            return $object;
133 235
        };
134
    }
135
136
    /**
137
     * @param string|callable $factory
138
     * @throws ContainerException if the factory cannot be made invokable
139
     */
140 223
    private function makeInvokable(string $name, $factory): callable
141
    {
142 223
        if ($this->isValidClass($factory)) {
143 159
            $factory = new $factory();
144
        }
145
146 201
        if (is_callable($factory)) {
147 153
            return $factory;
148
        }
149
150 62
        throw ContainerException::expectedInvokable($name);
151
    }
152
153
    /**
154
     * @param string|callable $factory
155
     */
156
    private function makeLazyInvokable(string $name, $factory): callable
157
    {
158 77
        return /** @return callable */ function () use ($name, $factory) {
159 47
            return $this->makeInvokable($name, $factory);
160 77
        };
161
    }
162
163
    /**
164
     * @param mixed $factory
165
     */
166 223
    private function isValidClass($factory): bool
167
    {
168 223
        return is_string($factory) && class_exists($factory);
169
    }
170
}
171