Completed
Pull Request — master (#12)
by Woody
07:30
created

Config::makeFactory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 6
nc 1
nop 3
crap 1
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
    /** @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->makeContainerGet($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 = $this->makeFactory($injector, $name, $factory);
57 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...
58 84
                $delegate = $this->makeDelegator(
59 84
                    $injector,
60 84
                    $name,
61 84
                    $delegate,
62 84
                    $dependencies['delegators'][$name]
63
                );
64
            }
65 110
            $injector->share($name)->delegate($name, $delegate);
66
        }
67 160
    }
68
69 160
    private function injectInvokables(Injector $injector, array $dependencies): void
70
    {
71 160
        $invokables = $dependencies['invokables'] ?? [];
72 160
        foreach ($invokables as $alias => $invokable) {
73 42
            if (is_string($alias) && $alias !== $invokable) {
74 14
                $injector->alias($alias, $invokable);
75
            }
76 42
            $delegate = $this->makeLazyInvokable($invokable, $invokable);
77 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...
78 26
                $delegate = $this->makeDelegator(
79 26
                    $injector,
80 26
                    $invokable,
81 26
                    $delegate,
82 26
                    $dependencies['delegators'][$invokable]
83
                );
84
            }
85 42
            $injector->share($invokable)->delegate($invokable, $delegate);
86
        }
87 160
    }
88
89 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...
90
    {
91 160
        $services = $dependencies['services'] ?? [];
92 160
        foreach ($services as $name => $service) {
93 160
            $injector->share($name)->delegate($name, $this->makeIdentity($service));
94
        }
95 160
    }
96
97
    private function makeDelegator(Injector $injector, string $name, callable $callback, array $delegators): callable
98
    {
99
        return
100
        /** @return object */
101 110
        function () use ($injector, $name, $callback, $delegators) {
102 110
            foreach ($delegators as $delegator) {
103 95
                $container = $injector->make(ContainerInterface::class);
104 95
                $delegator = $this->makeInvokable($name, $delegator);
105 50
                $instance = $delegator($container, $name, $callback);
106 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...
107
            }
108 55
            return $instance ?? $callback();
109 110
        };
110
    }
111
112
    private function makeContainerGet(string $name): callable
113
    {
114
        return
115
        /** @return object */
116 70
        static function (ContainerInterface $container) use ($name) {
117 70
            return $container->get($name);
118 70
        };
119
    }
120
121
    /**
122
     * @param string|callable $factory
123
     */
124
    private function makeFactory(Injector $injector, string $name, $factory): callable
125
    {
126
        return
127
        /** @return object */
128 110
        function () use ($injector, $name, $factory) {
129 68
            $container = $injector->make(ContainerInterface::class);
130 68
            $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...
131 58
            return $factory($container, $name);
132 110
        };
133
    }
134
135
    /**
136
     * @param object $object
137
     */
138
    private function makeIdentity($object): callable
139
    {
140
        return
141
        /** @return object */
142 160
        static function () use ($object) {
143 24
            return $object;
144 160
        };
145
    }
146
147
    /**
148
     * @param string|callable $factory
149
     * @throws ContainerException if the factory cannot be made invokable
150
     */
151 152
    private function makeInvokable(string $name, $factory): callable
152
    {
153 152
        if (is_callable($factory)) {
154 58
            return $factory;
155
        }
156
157 118
        if (!class_exists($factory)) {
158 23
            throw ContainerException::expectedInvokable($name);
159
        }
160
161 99
        $factory = new $factory();
162
163 81
        if (is_callable($factory)) {
164 65
            return $factory;
165
        }
166
167 18
        throw ContainerException::expectedInvokable($name);
168
    }
169
170
    /**
171
     * @param string|callable $factory
172
     */
173
    private function makeLazyInvokable(string $name, $factory): callable
174
    {
175
        return
176
        /** @return callable */
177 42
        function () use ($name, $factory) {
178 29
            return $this->makeInvokable($name, $factory);
179 42
        };
180
    }
181
}
182