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

Commit

Permalink
#83 - cyclic alias exception and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Ocramius committed Jan 26, 2016
1 parent ca07a2d commit b195a48
Show file tree
Hide file tree
Showing 2 changed files with 289 additions and 0 deletions.
115 changes: 115 additions & 0 deletions src/Exception/CyclicAliasException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace Zend\ServiceManager\Exception;

class CyclicAliasException extends InvalidArgumentException
{
/**
* @param string[] $aliases map of referenced services, indexed by alias name (string)
*
* @return self
*/
public static function fromAliasesMap(array $aliases)
{
$detectedCycles = array_filter(array_map(
function ($alias) use ($aliases) {
return self::getCycleFor($aliases, $alias);
},
array_keys($aliases)
));

if (! $detectedCycles) {
return new self(sprintf(
"A cycle was detected within the following aliases map:\n\n%s",
self::printReferencesMap($aliases)
));
}

return new self(sprintf(
"A cycle was detected within the provided aliases:\n\n%s"
. "\n\nThe cycle was detected in the following alias map:\n\n%s",
self::printCycles($detectedCycles),
self::printReferencesMap($aliases)
));
}

/**
* Retrieves the cycle detected for the given $alias, or `null` if no cycle was detected
*
* @param string[] $aliases
* @param string $alias
*
* @return array|null
*/
private static function getCycleFor(array $aliases, $alias)
{
$cycleCandidate = [];
$targetName = $alias;

while (isset($aliases[$targetName])) {
if (isset($cycleCandidate[$targetName])) {
return $cycleCandidate;
}

$cycleCandidate[$targetName] = true;

$targetName = $aliases[$targetName];
}

return null;
}

/**
* @param string[] $aliases
*
* @return string
*/
private static function printReferencesMap(array $aliases)
{
$map = [];

foreach ($aliases as $alias => $reference) {
$map[] = '"' . $alias . '" => "' . $reference . '"';
}

return "[\n" . implode("\n", $map) . "\n]";
}

/**
* @param string[][] $detectedCycles
*
* @return string
*/
private static function printCycles(array $detectedCycles)
{
return "[\n" . implode("\n", array_map([__CLASS__, 'printCycle'], $detectedCycles)) . "\n]";
}

/**
* @param string[] $detectedCycle
*
* @return string
*/
private static function printCycle(array $detectedCycle)
{
$fullCycle = array_keys($detectedCycle);
$fullCycle[] = reset($fullCycle);

return implode(
' => ',
array_map(
function ($cycle) {
return '"' . $cycle . '"';
},
$fullCycle
)
);
}
}
174 changes: 174 additions & 0 deletions test/Exception/CyclicAliasExceptionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

namespace ZendTest\ServiceManager\Exception;

use PHPUnit_Framework_TestCase as TestCase;
use ProxyManager\Autoloader\AutoloaderInterface;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RecursiveRegexIterator;
use RegexIterator;
use stdClass;
use Zend\ServiceManager\Exception\CyclicAliasException;
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
use Zend\ServiceManager\Exception\ServiceNotFoundException;
use Zend\ServiceManager\Factory\InvokableFactory;
use Zend\ServiceManager\Proxy\LazyServiceFactory;
use Zend\ServiceManager\ServiceManager;
use ZendTest\ServiceManager\TestAsset\InvokableObject;

/**
* @covers \Zend\ServiceManager\Exception\CyclicAliasException
*/
class CyclicAliasExceptionTest extends TestCase
{
/**
* @dataProvider aliasesProvider
*
* @param string[] $aliases
* @param string $expectedMessage
*
* @return void
*/
public function testFromAliasesMap(array $aliases, $expectedMessage)
{
$exception = CyclicAliasException::fromAliasesMap($aliases);

self::assertInstanceOf(CyclicAliasException::class, $exception);
self::assertSame($expectedMessage, $exception->getMessage());
}

/**
* @return string[][]|string[][][]
*/
public function aliasesProvider()
{
return [
'empty set' => [
[],
'A cycle was detected within the following aliases map:
[
]'
],
'acyclic set' => [
[
'b' => 'a',
'd' => 'c',
],
'A cycle was detected within the following aliases map:
[
"b" => "a"
"d" => "c"
]'
],
'acyclic self-referencing set' => [
[
'b' => 'a',
'c' => 'b',
'd' => 'c',
],
'A cycle was detected within the following aliases map:
[
"b" => "a"
"c" => "b"
"d" => "c"
]'
],
'cyclic set' => [
[
'b' => 'a',
'a' => 'b',
],
'A cycle was detected within the provided aliases:
[
"b" => "a" => "b"
"a" => "b" => "a"
]
The cycle was detected in the following alias map:
[
"b" => "a"
"a" => "b"
]'
],
'cyclic set (indirect)' => [
[
'b' => 'a',
'c' => 'b',
'a' => 'c',
],
'A cycle was detected within the provided aliases:
[
"b" => "a" => "c" => "b"
"c" => "b" => "a" => "c"
"a" => "c" => "b" => "a"
]
The cycle was detected in the following alias map:
[
"b" => "a"
"c" => "b"
"a" => "c"
]'
],
'cyclic set + acyclic set' => [
[
'b' => 'a',
'a' => 'b',
'd' => 'c',
],
'A cycle was detected within the provided aliases:
[
"b" => "a" => "b"
"a" => "b" => "a"
]
The cycle was detected in the following alias map:
[
"b" => "a"
"a" => "b"
"d" => "c"
]'
],
'cyclic set + reference to cyclic set' => [
[
'b' => 'a',
'a' => 'b',
'c' => 'a',
],
'A cycle was detected within the provided aliases:
[
"b" => "a" => "b"
"a" => "b" => "a"
"c" => "a" => "b" => "c"
]
The cycle was detected in the following alias map:
[
"b" => "a"
"a" => "b"
"c" => "a"
]'
],
];
}
}

0 comments on commit b195a48

Please sign in to comment.