Skip to content
This repository was archived by the owner on Feb 6, 2020. It is now read-only.

Commit aea63ce

Browse files
committed
Merge branch 'feature/230' into develop
Close #230
2 parents fdc7a27 + 9db343d commit aea63ce

File tree

3 files changed

+54
-4
lines changed

3 files changed

+54
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ All notable changes to this project will be documented in this file, in reverse
2929

3030
### Fixed
3131

32-
- Nothing.
32+
- [#230](https://github.com/zendframework/zend-servicemanager/pull/230) fixes a
33+
problem in detecting cyclic aliases, ensuring they are detected correctly.
3334

3435
## 3.3.1 - 2017-11-27
3536

src/ServiceManager.php

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,12 @@ private function mapAliasToTarget($alias, $target)
868868
*
869869
* This function maps $this->aliases in place.
870870
*
871+
* This algorithm is an adaptated version of Tarjans Strongly
872+
* Connected Components. Instead of returning the strongly
873+
* connected components (i.e. cycles in our case), we throw.
874+
* If nodes are not strongly connected (i.e. resolvable in
875+
* our case), they get resolved.
876+
*
871877
* This algorithm is fast for mass updates through configure().
872878
* It is not appropriate if just a single alias is added.
873879
*
@@ -881,16 +887,35 @@ private function mapAliasesToTargets()
881887
if (isset($tagged[$alias])) {
882888
continue;
883889
}
890+
884891
$tCursor = $this->aliases[$alias];
885892
$aCursor = $alias;
893+
if ($aCursor === $tCursor) {
894+
throw CyclicAliasException::fromCyclicAlias($alias, $this->aliases);
895+
}
896+
if (! isset($this->aliases[$tCursor])) {
897+
continue;
898+
}
899+
900+
$stack = [];
901+
886902
while (isset($this->aliases[$tCursor])) {
887-
$tagged[$aCursor] = true;
888-
$this->aliases[$aCursor] = $this->aliases[$tCursor];
903+
$stack[] = $aCursor;
904+
if ($aCursor === $this->aliases[$tCursor]) {
905+
throw CyclicAliasException::fromCyclicAlias($alias, $this->aliases);
906+
}
889907
$aCursor = $tCursor;
890908
$tCursor = $this->aliases[$tCursor];
891-
if ($aCursor === $tCursor) {
909+
}
910+
911+
$tagged[$aCursor] = true;
912+
913+
foreach ($stack as $alias) {
914+
if ($alias === $tCursor) {
892915
throw CyclicAliasException::fromCyclicAlias($alias, $this->aliases);
893916
}
917+
$this->aliases[$alias] = $tCursor;
918+
$tagged[$alias] = true;
894919
}
895920
}
896921
}

test/CommonServiceLocatorBehaviorsTrait.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,4 +855,28 @@ public function testCrashesOnCyclicAliases()
855855
],
856856
]);
857857
}
858+
859+
public function testMinimalCyclicAliasDefinitionShouldThrow()
860+
{
861+
$sm = $this->createContainer([]);
862+
863+
$this->expectException(CyclicAliasException::class);
864+
$sm->setAlias('alias', 'alias');
865+
}
866+
867+
public function testCoverageDepthFirstTaggingOnRecursiveAliasDefinitions()
868+
{
869+
$sm = $this->createContainer([
870+
'factories' => [
871+
stdClass::class => InvokableFactory::class,
872+
],
873+
'aliases' => [
874+
'alias1' => 'alias2',
875+
'alias2' => 'alias3',
876+
'alias3' => stdClass::class,
877+
],
878+
]);
879+
$this->assertSame($sm->get('alias1'), $sm->get('alias2'));
880+
$this->assertSame($sm->get(stdClass::class), $sm->get('alias1'));
881+
}
858882
}

0 commit comments

Comments
 (0)