1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
<?php
declare(strict_types=1);
namespace Laminas\Di;
use ArrayAccess;
use function array_keys;
use function class_exists;
use function interface_exists;
use function is_array;
/**
* Provides a DI configuration from an array.
*
* This configures the instantiation process of the dependency injector.
*
* **Example:**
*
* <code>
* return [
* // This section provides global type preferences.
* // Those are visited if a specific instance has no preference definitions.
* 'preferences' => [
* // The key is the requested class or interface name, the values are
* // the types the dependency injector should prefer.
* Some\Interface::class => Some\Preference::class
* ],
* // This configures the instantiation of specific types.
* // Types may also be purely virtual by defining the aliasOf key.
* 'types' => [
* My\Class::class => [
* 'preferences' => [
* // this supercedes the global type preferences
* // when My\Class is instantiated
* Some\Interface::class => 'My.SpecificAlias'
* ],
*
* // instantiation parameters. These will only be used for
* // the instantiator (i.e. the constructor)
* 'parameters' => [
* 'foo' => My\FooImpl::class, // Use the given type to provide the injection (depends on definition)
* 'bar' => '*' // Use the type preferences
* ],
* ],
*
* 'My.Alias' => [
* // typeOf defines virtual classes which can be used as type
* // preferences or for newInstance calls. They allow providing
* // custom configs for a class
* 'typeOf' => Some\Class::class,
* 'preferences' => [
* Foo::class => Bar::class
* ]
* ]
* ]
* ];
* </code>
*
* ## Notes on Injections
*
* Named arguments and Automatic type lookups will only work for Methods that
* are known to the dependency injector through its definitions. Injections for
* unknown methods do not perform type lookups on its own.
*
* A value injection without any lookups can be forced by providing a
* Resolver\ValueInjection instance.
*
* To force a service/class instance provide a Resolver\TypeInjection instance.
* For classes known from the definitions, a type preference might be the
* better approach
*
* @see Laminas\Di\Resolver\ValueInjection A container to force injection of a value
* @see Laminas\Di\Resolver\TypeInjection A container to force looking up a specific type instance for injection
*/
class Config implements ConfigInterface
{
/** @var array */
protected $preferences = [];
/** @var array */
protected $types = [];
/**
* Construct from options array
*
* Utilizes the given options array or traversable.
*
* @param array|ArrayAccess $options The options array. Traversables will
* be converted to an array internally.
* @throws Exception\InvalidArgumentException
*/
public function __construct($options = [])
{
$this->ensureArrayOrArrayAccess($options);
$this->preferences = $this->getDataFromArray($options, 'preferences');
$this->types = $this->getDataFromArray($options, 'types');
}
/**
* @param array|ArrayAccess $data
*/
private function getDataFromArray($data, string $key): array
{
$result = $data[$key] ?? [];
return is_array($result) ? $result : [];
}
/**
* {@inheritDoc}
*
* @see Laminas\Di\ConfigInterface::getClassForAlias()
*/
public function getClassForAlias(string $name): ?string
{
if (isset($this->types[$name]['typeOf'])) {
return $this->types[$name]['typeOf'];
}
return null;
}
/**
* Returns the instantiation parameters for the given type
*
* @param string $type The alias or class name
* @return array The configured parameters
*/
public function getParameters(string $type): array
{
if (! isset($this->types[$type]['parameters']) || ! is_array($this->types[$type]['parameters'])) {
return [];
}
return $this->types[$type]['parameters'];
}
/**
* {@inheritDoc}
*
* @see Laminas\Di\ConfigInterface::setParameters()
*
* @return $this
*/
public function setParameters(string $type, array $params)
{
$this->types[$type]['parameters'] = $params;
return $this;
}
public function getTypePreference(string $type, ?string $context = null): ?string
{
if ($context) {
return $this->getTypePreferenceForClass($type, $context);
}
if (! isset($this->preferences[$type])) {
return null;
}
$preference = $this->preferences[$type];
return $preference !== '' ? (string) $preference : null;
}
/**
* {@inheritDoc}
*
* @see Laminas\Di\ConfigInterface::getTypePreferencesForClass()
*/
private function getTypePreferenceForClass(string $type, ?string $context): ?string
{
if (! isset($this->types[$context]['preferences'][$type])) {
return null;
}
$preference = $this->types[$context]['preferences'][$type];
return $preference !== '' ? (string) $preference : null;
}
/**
* {@inheritDoc}
*
* @see ConfigInterface::isAlias()
*/
public function isAlias(string $name): bool
{
return isset($this->types[$name]['typeOf']);
}
/**
* {@inheritDoc}
*
* @see ConfigInterface::getConfiguredTypeNames()
*/
public function getConfiguredTypeNames(): array
{
return array_keys($this->types);
}
public function setTypePreference(string $type, string $preference, ?string $context = null): self
{
if ($context) {
$this->types[$context]['preferences'][$type] = $preference;
return $this;
}
$this->preferences[$type] = $preference;
return $this;
}
/**
* @param string $name The name of the alias
* @param string $class The class name this alias points to
* @throws Exception\ClassNotFoundException When `$class` does not exist.
*/
public function setAlias(string $name, string $class): self
{
if (! class_exists($class) && ! interface_exists($class)) {
throw new Exception\ClassNotFoundException($class);
}
$this->types[$name]['typeOf'] = $class;
return $this;
}
/** @param array|ArrayAccess $options */
private function ensureArrayOrArrayAccess($options): void
{
if (! is_array($options) && ! $options instanceof ArrayAccess) {
throw new Exception\InvalidArgumentException(
'Config data must be of type array or ArrayAccess'
);
}
}
}