Skip to content

Commit 83b6a27

Browse files
committed
New autowiring approach, option autowired can contain list of classes (#84)
1 parent 51e3d23 commit 83b6a27

File tree

4 files changed

+245
-8
lines changed

4 files changed

+245
-8
lines changed

src/DI/Compiler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ public static function parseService(ServiceDefinition $definition, $config)
396396
}
397397

398398
if (isset($config['autowired'])) {
399-
Validators::assertField($config, 'autowired', 'bool');
399+
Validators::assertField($config, 'autowired', 'bool|string|array');
400400
$definition->setAutowired($config['autowired']);
401401
}
402402

src/DI/ContainerBuilder.php

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -349,11 +349,38 @@ public function prepareClassList()
349349
}
350350

351351
// build auto-wiring list
352-
$this->classList = [];
352+
$this->classList = $preferred = [];
353353
foreach ($this->definitions as $name => $def) {
354354
if ($class = $def->getImplement() ?: $def->getClass()) {
355+
$defAutowired = $def->getAutowired();
356+
if (is_array($defAutowired)) {
357+
foreach ($defAutowired as $k => $aclass) {
358+
if ($aclass === self::THIS_SERVICE) {
359+
$defAutowired[$k] = $class;
360+
} elseif (!is_a($class, $aclass, TRUE)) {
361+
throw new ServiceCreationException("Incompatible class $aclass in autowiring definition of service '$name'.");
362+
}
363+
}
364+
}
365+
355366
foreach (class_parents($class) + class_implements($class) + [$class] as $parent) {
356-
$this->classList[$parent][$def->isAutowired() && empty($this->excludedClasses[$parent])][] = (string) $name;
367+
$autowired = $defAutowired && empty($this->excludedClasses[$parent]);
368+
if ($autowired && is_array($defAutowired)) {
369+
$autowired = FALSE;
370+
foreach ($defAutowired as $aclass) {
371+
if (is_a($parent, $aclass, TRUE)) {
372+
if (empty($preferred[$parent]) && isset($this->classList[$parent][TRUE])) {
373+
$this->classList[$parent][FALSE] = array_merge(...$this->classList[$parent]);
374+
$this->classList[$parent][TRUE] = [];
375+
}
376+
$preferred[$parent] = $autowired = TRUE;
377+
break;
378+
}
379+
}
380+
} elseif (isset($preferred[$parent])) {
381+
$autowired = FALSE;
382+
}
383+
$this->classList[$parent][$autowired][] = (string) $name;
357384
}
358385
}
359386
}
@@ -455,7 +482,7 @@ private function resolveServiceClass($name, $recursive = [])
455482
}
456483
self::checkCase($class);
457484

458-
} elseif ($def->isAutowired()) {
485+
} elseif ($def->getAutowired()) {
459486
trigger_error("Type of service '$name' is unknown.", E_USER_NOTICE);
460487
}
461488
return $class;

src/DI/ServiceDefinition.php

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class ServiceDefinition
3636
/** @var array */
3737
private $tags = [];
3838

39-
/** @var bool */
39+
/** @var bool|string[] */
4040
private $autowired = TRUE;
4141

4242
/** @var bool */
@@ -210,26 +210,35 @@ public function getTag($tag)
210210

211211

212212
/**
213-
* @param bool
213+
* @param bool|string|string[]
214214
* @return self
215215
*/
216216
public function setAutowired($state = TRUE)
217217
{
218218
call_user_func($this->notifier);
219-
$this->autowired = (bool) $state;
219+
$this->autowired = is_string($state) || is_array($state) ? (array) $state : (bool) $state;
220220
return $this;
221221
}
222222

223223

224224
/**
225-
* @return bool
225+
* @return bool|string[]
226226
*/
227227
public function isAutowired()
228228
{
229229
return $this->autowired;
230230
}
231231

232232

233+
/**
234+
* @return bool|string[]
235+
*/
236+
public function getAutowired()
237+
{
238+
return $this->autowired;
239+
}
240+
241+
233242
/**
234243
* @param bool
235244
* @return self
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
<?php
2+
3+
/**
4+
* Test: Nette\DI\ContainerBuilder and direct types
5+
*/
6+
7+
use Nette\DI;
8+
use Tester\Assert;
9+
10+
11+
require __DIR__ . '/../bootstrap.php';
12+
13+
14+
interface IFoo
15+
{
16+
}
17+
18+
interface IBar
19+
{
20+
}
21+
22+
class Foo implements IFoo
23+
{
24+
}
25+
26+
class Bar extends Foo implements IBar
27+
{
28+
}
29+
30+
31+
test(function () {
32+
$builder = new DI\ContainerBuilder;
33+
$builder->addDefinition('bar')
34+
->setClass('Bar')
35+
->setAutowired('Bar');
36+
37+
Assert::same('bar', $builder->getByType('Bar'));
38+
Assert::same(NULL, $builder->getByType('IBar'));
39+
Assert::same(NULL, $builder->getByType('Foo'));
40+
Assert::same(NULL, $builder->getByType('IFoo'));
41+
});
42+
43+
44+
test(function () {
45+
$builder = new DI\ContainerBuilder;
46+
$builder->addDefinition('bar')
47+
->setClass('Bar')
48+
->setAutowired('IBar');
49+
50+
Assert::same('bar', $builder->getByType('Bar'));
51+
Assert::same('bar', $builder->getByType('IBar'));
52+
Assert::same(NULL, $builder->getByType('Foo'));
53+
Assert::same(NULL, $builder->getByType('IFoo'));
54+
});
55+
56+
57+
test(function () {
58+
$builder = new DI\ContainerBuilder;
59+
$builder->addDefinition('bar')
60+
->setClass('Bar')
61+
->setAutowired('Foo');
62+
63+
Assert::same('bar', $builder->getByType('Bar'));
64+
Assert::same(NULL, $builder->getByType('IBar'));
65+
Assert::same('bar', $builder->getByType('Foo'));
66+
Assert::same(NULL, $builder->getByType('IFoo'));
67+
});
68+
69+
70+
test(function () {
71+
$builder = new DI\ContainerBuilder;
72+
$builder->addDefinition('bar')
73+
->setClass('Bar')
74+
->setAutowired('IFoo');
75+
76+
Assert::same('bar', $builder->getByType('Bar'));
77+
Assert::same(NULL, $builder->getByType('IBar'));
78+
Assert::same('bar', $builder->getByType('Foo'));
79+
Assert::same('bar', $builder->getByType('IFoo'));
80+
});
81+
82+
83+
test(function () {
84+
$builder = new DI\ContainerBuilder;
85+
$builder->addDefinition('bar')
86+
->setClass('Bar')
87+
->setAutowired(['IFoo', 'IBar']);
88+
89+
Assert::same('bar', $builder->getByType('Bar'));
90+
Assert::same('bar', $builder->getByType('IBar'));
91+
Assert::same('bar', $builder->getByType('Foo'));
92+
Assert::same('bar', $builder->getByType('IFoo'));
93+
});
94+
95+
96+
test(function () {
97+
$builder = new DI\ContainerBuilder;
98+
$builder->addDefinition('bar')
99+
->setClass('Bar')
100+
->setAutowired(['Foo', 'Bar']);
101+
102+
Assert::same('bar', $builder->getByType('Bar'));
103+
Assert::same(NULL, $builder->getByType('IBar'));
104+
Assert::same('bar', $builder->getByType('Foo'));
105+
Assert::same(NULL, $builder->getByType('IFoo'));
106+
});
107+
108+
109+
test(function () {
110+
$builder = new DI\ContainerBuilder;
111+
$builder->addDefinition('bar')
112+
->setClass('Bar')
113+
->setAutowired(['Foo', 'IBar']);
114+
115+
Assert::same('bar', $builder->getByType('Bar'));
116+
Assert::same('bar', $builder->getByType('IBar'));
117+
Assert::same('bar', $builder->getByType('Foo'));
118+
Assert::same(NULL, $builder->getByType('IFoo'));
119+
});
120+
121+
122+
test(function () {
123+
$builder = new DI\ContainerBuilder;
124+
$builder->addDefinition('bar')
125+
->setClass('Bar')
126+
->setAutowired(['IFoo', 'Bar']);
127+
128+
Assert::same('bar', $builder->getByType('Bar'));
129+
Assert::same(NULL, $builder->getByType('IBar'));
130+
Assert::same('bar', $builder->getByType('Foo'));
131+
Assert::same('bar', $builder->getByType('IFoo'));
132+
});
133+
134+
135+
test(function () {
136+
$builder = new DI\ContainerBuilder;
137+
$builder->addDefinition('bar')
138+
->setClass('Bar')
139+
->setAutowired('Bar');
140+
141+
$builder->addDefinition('foo')
142+
->setClass('Foo')
143+
->setAutowired();
144+
145+
Assert::same('bar', $builder->getByType('Bar'));
146+
Assert::null($builder->getByType('IBar'));
147+
Assert::same('foo', $builder->getByType('Foo'));
148+
Assert::same('foo', $builder->getByType('IFoo'));
149+
});
150+
151+
152+
test(function () {
153+
$builder = new DI\ContainerBuilder;
154+
$builder->addDefinition('bar')
155+
->setClass('Bar')
156+
->setAutowired(['Bar', 'IFoo']);
157+
158+
$builder->addDefinition('foo')
159+
->setClass('Foo')
160+
->setAutowired();
161+
162+
Assert::same('bar', $builder->getByType('Bar'));
163+
Assert::null($builder->getByType('IBar'));
164+
Assert::same('bar', $builder->getByType('Foo'));
165+
Assert::same('bar', $builder->getByType('IFoo'));
166+
});
167+
168+
169+
test(function () {
170+
$builder = new DI\ContainerBuilder;
171+
$bar = $builder->addDefinition('bar')
172+
->setClass('Bar')
173+
->setAutowired(['Bar', 'IFoo']);
174+
175+
$foo = $builder->addDefinition('foo')
176+
->setClass('Foo')
177+
->setAutowired('IFoo');
178+
179+
Assert::same('bar', $builder->getByType('Bar'));
180+
Assert::null($builder->getByType('IBar'));
181+
182+
Assert::exception(function () use ($builder) {
183+
$builder->getByType('Foo');
184+
}, DI\ServiceCreationException::class, 'Multiple services of type Foo found: bar, foo');
185+
186+
Assert::exception(function () use ($builder) {
187+
$builder->getByType('IFoo');
188+
}, DI\ServiceCreationException::class, 'Multiple services of type IFoo found: bar, foo');
189+
});
190+
191+
192+
test(function () {
193+
$builder = new DI\ContainerBuilder;
194+
$bar = $builder->addDefinition('bar')
195+
->setClass('Foo')
196+
->setAutowired(['Bar']);
197+
198+
Assert::exception(function () use ($builder) {
199+
$builder->getByType('Foo');
200+
}, DI\ServiceCreationException::class, "Incompatible class Bar in autowiring definition of service 'bar'.");
201+
});

0 commit comments

Comments
 (0)