Skip to content

Commit

Permalink
New autowiring approach, option autowired can contain list of classes (
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed May 20, 2016
1 parent 51e3d23 commit 83b6a27
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 8 deletions.
2 changes: 1 addition & 1 deletion src/DI/Compiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ public static function parseService(ServiceDefinition $definition, $config)
}

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

Expand Down
33 changes: 30 additions & 3 deletions src/DI/ContainerBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -349,11 +349,38 @@ public function prepareClassList()
}

// build auto-wiring list
$this->classList = [];
$this->classList = $preferred = [];
foreach ($this->definitions as $name => $def) {
if ($class = $def->getImplement() ?: $def->getClass()) {
$defAutowired = $def->getAutowired();
if (is_array($defAutowired)) {
foreach ($defAutowired as $k => $aclass) {
if ($aclass === self::THIS_SERVICE) {
$defAutowired[$k] = $class;
} elseif (!is_a($class, $aclass, TRUE)) {
throw new ServiceCreationException("Incompatible class $aclass in autowiring definition of service '$name'.");
}
}
}

foreach (class_parents($class) + class_implements($class) + [$class] as $parent) {
$this->classList[$parent][$def->isAutowired() && empty($this->excludedClasses[$parent])][] = (string) $name;
$autowired = $defAutowired && empty($this->excludedClasses[$parent]);
if ($autowired && is_array($defAutowired)) {
$autowired = FALSE;
foreach ($defAutowired as $aclass) {
if (is_a($parent, $aclass, TRUE)) {
if (empty($preferred[$parent]) && isset($this->classList[$parent][TRUE])) {
$this->classList[$parent][FALSE] = array_merge(...$this->classList[$parent]);
$this->classList[$parent][TRUE] = [];
}
$preferred[$parent] = $autowired = TRUE;
break;
}
}
} elseif (isset($preferred[$parent])) {
$autowired = FALSE;
}
$this->classList[$parent][$autowired][] = (string) $name;
}
}
}
Expand Down Expand Up @@ -455,7 +482,7 @@ private function resolveServiceClass($name, $recursive = [])
}
self::checkCase($class);

} elseif ($def->isAutowired()) {
} elseif ($def->getAutowired()) {
trigger_error("Type of service '$name' is unknown.", E_USER_NOTICE);
}
return $class;
Expand Down
17 changes: 13 additions & 4 deletions src/DI/ServiceDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class ServiceDefinition
/** @var array */
private $tags = [];

/** @var bool */
/** @var bool|string[] */
private $autowired = TRUE;

/** @var bool */
Expand Down Expand Up @@ -210,26 +210,35 @@ public function getTag($tag)


/**
* @param bool
* @param bool|string|string[]
* @return self
*/
public function setAutowired($state = TRUE)
{
call_user_func($this->notifier);
$this->autowired = (bool) $state;
$this->autowired = is_string($state) || is_array($state) ? (array) $state : (bool) $state;
return $this;
}


/**
* @return bool
* @return bool|string[]
*/
public function isAutowired()
{
return $this->autowired;
}


/**
* @return bool|string[]
*/
public function getAutowired()
{
return $this->autowired;
}


/**
* @param bool
* @return self
Expand Down
201 changes: 201 additions & 0 deletions tests/DI/ContainerBuilder.autowiring.types.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
<?php

/**
* Test: Nette\DI\ContainerBuilder and direct types
*/

use Nette\DI;
use Tester\Assert;


require __DIR__ . '/../bootstrap.php';


interface IFoo
{
}

interface IBar
{
}

class Foo implements IFoo
{
}

class Bar extends Foo implements IBar
{
}


test(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition('bar')
->setClass('Bar')
->setAutowired('Bar');

Assert::same('bar', $builder->getByType('Bar'));
Assert::same(NULL, $builder->getByType('IBar'));
Assert::same(NULL, $builder->getByType('Foo'));
Assert::same(NULL, $builder->getByType('IFoo'));
});


test(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition('bar')
->setClass('Bar')
->setAutowired('IBar');

Assert::same('bar', $builder->getByType('Bar'));
Assert::same('bar', $builder->getByType('IBar'));
Assert::same(NULL, $builder->getByType('Foo'));
Assert::same(NULL, $builder->getByType('IFoo'));
});


test(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition('bar')
->setClass('Bar')
->setAutowired('Foo');

Assert::same('bar', $builder->getByType('Bar'));
Assert::same(NULL, $builder->getByType('IBar'));
Assert::same('bar', $builder->getByType('Foo'));
Assert::same(NULL, $builder->getByType('IFoo'));
});


test(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition('bar')
->setClass('Bar')
->setAutowired('IFoo');

Assert::same('bar', $builder->getByType('Bar'));
Assert::same(NULL, $builder->getByType('IBar'));
Assert::same('bar', $builder->getByType('Foo'));
Assert::same('bar', $builder->getByType('IFoo'));
});


test(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition('bar')
->setClass('Bar')
->setAutowired(['IFoo', 'IBar']);

Assert::same('bar', $builder->getByType('Bar'));
Assert::same('bar', $builder->getByType('IBar'));
Assert::same('bar', $builder->getByType('Foo'));
Assert::same('bar', $builder->getByType('IFoo'));
});


test(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition('bar')
->setClass('Bar')
->setAutowired(['Foo', 'Bar']);

Assert::same('bar', $builder->getByType('Bar'));
Assert::same(NULL, $builder->getByType('IBar'));
Assert::same('bar', $builder->getByType('Foo'));
Assert::same(NULL, $builder->getByType('IFoo'));
});


test(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition('bar')
->setClass('Bar')
->setAutowired(['Foo', 'IBar']);

Assert::same('bar', $builder->getByType('Bar'));
Assert::same('bar', $builder->getByType('IBar'));
Assert::same('bar', $builder->getByType('Foo'));
Assert::same(NULL, $builder->getByType('IFoo'));
});


test(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition('bar')
->setClass('Bar')
->setAutowired(['IFoo', 'Bar']);

Assert::same('bar', $builder->getByType('Bar'));
Assert::same(NULL, $builder->getByType('IBar'));
Assert::same('bar', $builder->getByType('Foo'));
Assert::same('bar', $builder->getByType('IFoo'));
});


test(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition('bar')
->setClass('Bar')
->setAutowired('Bar');

$builder->addDefinition('foo')
->setClass('Foo')
->setAutowired();

Assert::same('bar', $builder->getByType('Bar'));
Assert::null($builder->getByType('IBar'));
Assert::same('foo', $builder->getByType('Foo'));
Assert::same('foo', $builder->getByType('IFoo'));
});


test(function () {
$builder = new DI\ContainerBuilder;
$builder->addDefinition('bar')
->setClass('Bar')
->setAutowired(['Bar', 'IFoo']);

$builder->addDefinition('foo')
->setClass('Foo')
->setAutowired();

Assert::same('bar', $builder->getByType('Bar'));
Assert::null($builder->getByType('IBar'));
Assert::same('bar', $builder->getByType('Foo'));
Assert::same('bar', $builder->getByType('IFoo'));
});


test(function () {
$builder = new DI\ContainerBuilder;
$bar = $builder->addDefinition('bar')
->setClass('Bar')
->setAutowired(['Bar', 'IFoo']);

$foo = $builder->addDefinition('foo')
->setClass('Foo')
->setAutowired('IFoo');

Assert::same('bar', $builder->getByType('Bar'));
Assert::null($builder->getByType('IBar'));

Assert::exception(function () use ($builder) {
$builder->getByType('Foo');
}, DI\ServiceCreationException::class, 'Multiple services of type Foo found: bar, foo');

Assert::exception(function () use ($builder) {
$builder->getByType('IFoo');
}, DI\ServiceCreationException::class, 'Multiple services of type IFoo found: bar, foo');
});


test(function () {
$builder = new DI\ContainerBuilder;
$bar = $builder->addDefinition('bar')
->setClass('Foo')
->setAutowired(['Bar']);

Assert::exception(function () use ($builder) {
$builder->getByType('Foo');
}, DI\ServiceCreationException::class, "Incompatible class Bar in autowiring definition of service 'bar'.");
});

0 comments on commit 83b6a27

Please sign in to comment.