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 |
|
|
|
|
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); |
|
|
|
|
59
|
58 |
|
return $factory($container, $name); |
60
|
110 |
|
}; |
61
|
110 |
View Code Duplication |
if (isset($dependencies['delegators'][$name])) { |
|
|
|
|
62
|
84 |
|
$delegate = $this->makeDelegator( |
63
|
84 |
|
$injector, |
64
|
84 |
|
$name, |
65
|
84 |
|
$delegate, |
66
|
84 |
|
$dependencies['delegators'][$name] |
67
|
|
|
); |
68
|
|
|
} |
69
|
110 |
|
$injector->share($name)->delegate($name, $delegate); |
70
|
|
|
} |
71
|
160 |
|
} |
72
|
|
|
|
73
|
160 |
|
private function injectInvokables(Injector $injector, array $dependencies): void |
74
|
|
|
{ |
75
|
160 |
|
$invokables = $dependencies['invokables'] ?? []; |
76
|
160 |
|
foreach ($invokables as $alias => $invokable) { |
77
|
42 |
|
if (is_string($alias) && $alias !== $invokable) { |
78
|
14 |
|
$injector->alias($alias, $invokable); |
79
|
|
|
} |
80
|
42 |
|
$delegate = function () use ($invokable) { |
81
|
29 |
|
return $this->makeFactory($invokable, $invokable); |
82
|
42 |
|
}; |
83
|
42 |
View Code Duplication |
if (isset($dependencies['delegators'][$invokable])) { |
|
|
|
|
84
|
26 |
|
$delegate = $this->makeDelegator( |
85
|
26 |
|
$injector, |
86
|
26 |
|
$invokable, |
87
|
26 |
|
$delegate, |
88
|
26 |
|
$dependencies['delegators'][$invokable] |
89
|
|
|
); |
90
|
|
|
} |
91
|
42 |
|
$injector->share($invokable)->delegate($invokable, $delegate); |
92
|
|
|
} |
93
|
160 |
|
} |
94
|
|
|
|
95
|
160 |
View Code Duplication |
private function injectServices(Injector $injector, array $dependencies): void |
|
|
|
|
96
|
|
|
{ |
97
|
160 |
|
$services = $dependencies['services'] ?? []; |
98
|
160 |
|
foreach ($services as $name => $service) { |
99
|
160 |
|
$injector->share($name)->delegate($name, $this->makeIdentity($service)); |
100
|
|
|
} |
101
|
160 |
|
} |
102
|
|
|
|
103
|
|
|
private function makeDelegator(Injector $injector, string $name, callable $callback, array $delegators): callable |
104
|
|
|
{ |
105
|
110 |
|
return function () use ($injector, $name, $callback, $delegators) { |
106
|
110 |
|
foreach ($delegators as $delegator) { |
107
|
95 |
|
$container = $injector->make(ContainerInterface::class); |
108
|
95 |
|
$delegator = $this->makeFactory($name, $delegator); |
109
|
50 |
|
$instance = $delegator($container, $name, $callback); |
110
|
40 |
|
$callback = $this->makeIdentity($instance); |
|
|
|
|
111
|
|
|
} |
112
|
55 |
|
return $instance ?? $callback(); |
113
|
110 |
|
}; |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* @param string|callable $factory |
118
|
|
|
*/ |
119
|
152 |
|
private function makeFactory(string $name, $factory): callable |
120
|
|
|
{ |
121
|
152 |
|
if (is_callable($factory)) { |
122
|
58 |
|
return $factory; |
123
|
|
|
} |
124
|
|
|
|
125
|
118 |
|
if (is_string($factory) && !class_exists($factory)) { |
126
|
23 |
|
throw ContainerException::expectedInvokable($name); |
127
|
|
|
} |
128
|
|
|
|
129
|
99 |
|
$factory = new $factory(); |
130
|
|
|
|
131
|
81 |
|
if (is_callable($factory)) { |
132
|
65 |
|
return $factory; |
133
|
|
|
} |
134
|
|
|
|
135
|
18 |
|
throw ContainerException::expectedInvokable($name); |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
private function makeIdentity($object): callable |
139
|
|
|
{ |
140
|
160 |
|
return static function () use ($object) { |
141
|
24 |
|
return $object; |
142
|
160 |
|
}; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
private function makeLazy(string $name): callable |
146
|
|
|
{ |
147
|
70 |
|
return static function (ContainerInterface $container) use ($name) { |
148
|
70 |
|
return $container->get($name); |
149
|
70 |
|
}; |
150
|
|
|
} |
151
|
|
|
} |
152
|
|
|
|
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.