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

Commit b195a48

Browse files
committed
#83 - cyclic alias exception and tests
1 parent ca07a2d commit b195a48

File tree

2 files changed

+289
-0
lines changed

2 files changed

+289
-0
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<?php
2+
/**
3+
* Zend Framework (http://framework.zend.com/)
4+
*
5+
* @link http://github.com/zendframework/zf2 for the canonical source repository
6+
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7+
* @license http://framework.zend.com/license/new-bsd New BSD License
8+
*/
9+
10+
namespace Zend\ServiceManager\Exception;
11+
12+
class CyclicAliasException extends InvalidArgumentException
13+
{
14+
/**
15+
* @param string[] $aliases map of referenced services, indexed by alias name (string)
16+
*
17+
* @return self
18+
*/
19+
public static function fromAliasesMap(array $aliases)
20+
{
21+
$detectedCycles = array_filter(array_map(
22+
function ($alias) use ($aliases) {
23+
return self::getCycleFor($aliases, $alias);
24+
},
25+
array_keys($aliases)
26+
));
27+
28+
if (! $detectedCycles) {
29+
return new self(sprintf(
30+
"A cycle was detected within the following aliases map:\n\n%s",
31+
self::printReferencesMap($aliases)
32+
));
33+
}
34+
35+
return new self(sprintf(
36+
"A cycle was detected within the provided aliases:\n\n%s"
37+
. "\n\nThe cycle was detected in the following alias map:\n\n%s",
38+
self::printCycles($detectedCycles),
39+
self::printReferencesMap($aliases)
40+
));
41+
}
42+
43+
/**
44+
* Retrieves the cycle detected for the given $alias, or `null` if no cycle was detected
45+
*
46+
* @param string[] $aliases
47+
* @param string $alias
48+
*
49+
* @return array|null
50+
*/
51+
private static function getCycleFor(array $aliases, $alias)
52+
{
53+
$cycleCandidate = [];
54+
$targetName = $alias;
55+
56+
while (isset($aliases[$targetName])) {
57+
if (isset($cycleCandidate[$targetName])) {
58+
return $cycleCandidate;
59+
}
60+
61+
$cycleCandidate[$targetName] = true;
62+
63+
$targetName = $aliases[$targetName];
64+
}
65+
66+
return null;
67+
}
68+
69+
/**
70+
* @param string[] $aliases
71+
*
72+
* @return string
73+
*/
74+
private static function printReferencesMap(array $aliases)
75+
{
76+
$map = [];
77+
78+
foreach ($aliases as $alias => $reference) {
79+
$map[] = '"' . $alias . '" => "' . $reference . '"';
80+
}
81+
82+
return "[\n" . implode("\n", $map) . "\n]";
83+
}
84+
85+
/**
86+
* @param string[][] $detectedCycles
87+
*
88+
* @return string
89+
*/
90+
private static function printCycles(array $detectedCycles)
91+
{
92+
return "[\n" . implode("\n", array_map([__CLASS__, 'printCycle'], $detectedCycles)) . "\n]";
93+
}
94+
95+
/**
96+
* @param string[] $detectedCycle
97+
*
98+
* @return string
99+
*/
100+
private static function printCycle(array $detectedCycle)
101+
{
102+
$fullCycle = array_keys($detectedCycle);
103+
$fullCycle[] = reset($fullCycle);
104+
105+
return implode(
106+
' => ',
107+
array_map(
108+
function ($cycle) {
109+
return '"' . $cycle . '"';
110+
},
111+
$fullCycle
112+
)
113+
);
114+
}
115+
}
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
<?php
2+
/**
3+
* Zend Framework (http://framework.zend.com/)
4+
*
5+
* @link http://github.com/zendframework/zf2 for the canonical source repository
6+
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7+
* @license http://framework.zend.com/license/new-bsd New BSD License
8+
*/
9+
10+
namespace ZendTest\ServiceManager\Exception;
11+
12+
use PHPUnit_Framework_TestCase as TestCase;
13+
use ProxyManager\Autoloader\AutoloaderInterface;
14+
use RecursiveDirectoryIterator;
15+
use RecursiveIteratorIterator;
16+
use RecursiveRegexIterator;
17+
use RegexIterator;
18+
use stdClass;
19+
use Zend\ServiceManager\Exception\CyclicAliasException;
20+
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
21+
use Zend\ServiceManager\Exception\ServiceNotFoundException;
22+
use Zend\ServiceManager\Factory\InvokableFactory;
23+
use Zend\ServiceManager\Proxy\LazyServiceFactory;
24+
use Zend\ServiceManager\ServiceManager;
25+
use ZendTest\ServiceManager\TestAsset\InvokableObject;
26+
27+
/**
28+
* @covers \Zend\ServiceManager\Exception\CyclicAliasException
29+
*/
30+
class CyclicAliasExceptionTest extends TestCase
31+
{
32+
/**
33+
* @dataProvider aliasesProvider
34+
*
35+
* @param string[] $aliases
36+
* @param string $expectedMessage
37+
*
38+
* @return void
39+
*/
40+
public function testFromAliasesMap(array $aliases, $expectedMessage)
41+
{
42+
$exception = CyclicAliasException::fromAliasesMap($aliases);
43+
44+
self::assertInstanceOf(CyclicAliasException::class, $exception);
45+
self::assertSame($expectedMessage, $exception->getMessage());
46+
}
47+
48+
/**
49+
* @return string[][]|string[][][]
50+
*/
51+
public function aliasesProvider()
52+
{
53+
return [
54+
'empty set' => [
55+
[],
56+
'A cycle was detected within the following aliases map:
57+
58+
[
59+
60+
]'
61+
],
62+
'acyclic set' => [
63+
[
64+
'b' => 'a',
65+
'd' => 'c',
66+
],
67+
'A cycle was detected within the following aliases map:
68+
69+
[
70+
"b" => "a"
71+
"d" => "c"
72+
]'
73+
],
74+
'acyclic self-referencing set' => [
75+
[
76+
'b' => 'a',
77+
'c' => 'b',
78+
'd' => 'c',
79+
],
80+
'A cycle was detected within the following aliases map:
81+
82+
[
83+
"b" => "a"
84+
"c" => "b"
85+
"d" => "c"
86+
]'
87+
],
88+
'cyclic set' => [
89+
[
90+
'b' => 'a',
91+
'a' => 'b',
92+
],
93+
'A cycle was detected within the provided aliases:
94+
95+
[
96+
"b" => "a" => "b"
97+
"a" => "b" => "a"
98+
]
99+
100+
The cycle was detected in the following alias map:
101+
102+
[
103+
"b" => "a"
104+
"a" => "b"
105+
]'
106+
],
107+
'cyclic set (indirect)' => [
108+
[
109+
'b' => 'a',
110+
'c' => 'b',
111+
'a' => 'c',
112+
],
113+
'A cycle was detected within the provided aliases:
114+
115+
[
116+
"b" => "a" => "c" => "b"
117+
"c" => "b" => "a" => "c"
118+
"a" => "c" => "b" => "a"
119+
]
120+
121+
The cycle was detected in the following alias map:
122+
123+
[
124+
"b" => "a"
125+
"c" => "b"
126+
"a" => "c"
127+
]'
128+
],
129+
'cyclic set + acyclic set' => [
130+
[
131+
'b' => 'a',
132+
'a' => 'b',
133+
'd' => 'c',
134+
],
135+
'A cycle was detected within the provided aliases:
136+
137+
[
138+
"b" => "a" => "b"
139+
"a" => "b" => "a"
140+
]
141+
142+
The cycle was detected in the following alias map:
143+
144+
[
145+
"b" => "a"
146+
"a" => "b"
147+
"d" => "c"
148+
]'
149+
],
150+
'cyclic set + reference to cyclic set' => [
151+
[
152+
'b' => 'a',
153+
'a' => 'b',
154+
'c' => 'a',
155+
],
156+
'A cycle was detected within the provided aliases:
157+
158+
[
159+
"b" => "a" => "b"
160+
"a" => "b" => "a"
161+
"c" => "a" => "b" => "c"
162+
]
163+
164+
The cycle was detected in the following alias map:
165+
166+
[
167+
"b" => "a"
168+
"a" => "b"
169+
"c" => "a"
170+
]'
171+
],
172+
];
173+
}
174+
}

0 commit comments

Comments
 (0)