Skip to content

Commit

Permalink
way to detecte circular deependencies between components
Browse files Browse the repository at this point in the history
  • Loading branch information
Halleck45 committed Feb 2, 2025
1 parent 46bb2ad commit a32ce21
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 1 deletion.
65 changes: 65 additions & 0 deletions src/Toolkit/src/Registry/DependenciesResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\UX\Toolkit\Registry;

/**
* @author Jean-François Lépine
*
* @internal
*/
class DependenciesResolver
{
public function resolve(Registry $registry): array
{
$resolved = [];
$unresolved = [];
foreach (array_keys($registry->all()) as $itemName) {
[$resolved, $unresolved] = $this->resolveDependency($registry, $itemName, $resolved, $unresolved);
}

$sorted = [];
foreach ($resolved as $itemName) {
$sorted[] = $registry->get($itemName);
}

return $sorted;
}

private function resolveDependency(Registry $registry, string $itemName, array $resolved, array $unresolved)
{
$unresolved[] = $itemName;
$dependencies = [];
if (null !== $registry->get($itemName)->parentName) {
$dependencies[] = $registry->get($itemName)->parentName;
}

foreach ($dependencies as $dep) {
if (!\in_array($dep, $resolved)) {
if (!\in_array($dep, $unresolved)) {
$unresolved[] = $dep;
[$resolved, $unresolved] = $this->resolveDependency($registry, $dep, $resolved, $unresolved);
} else {
throw new \RuntimeException("Circular dependency detected: $itemName -> $dep.");
}
}
}
if (!\in_array($itemName, $resolved)) {
$resolved[] = $itemName;
}

while (($index = array_search($itemName, $unresolved)) !== false) {
unset($unresolved[$index]);
}

return [$resolved, $unresolved];
}
}
10 changes: 9 additions & 1 deletion src/Toolkit/src/Registry/Registry.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@ public static function empty(): self

public function add(RegistryItem $item): void
{
$this->items[] = $item;
$this->items[$item->name] = $item;
}

/**
* @return RegistryItem[]
*/
public function all(): array
{
return $this->items;
}

public function has(string $name): bool
Expand Down
129 changes: 129 additions & 0 deletions src/Toolkit/tests/Registry/DependenciesResolverTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\UX\Toolkit\Tests\Registry;

use PHPUnit\Framework\TestCase;
use Symfony\UX\Toolkit\Registry\DependenciesResolver;
use Symfony\UX\Toolkit\Registry\Registry;
use Symfony\UX\Toolkit\Registry\RegistryItem;
use Symfony\UX\Toolkit\Registry\RegistryItemType;

/**
* @author Jean-François Lépine
*
* @group wip
*/
class DependenciesResolverTest extends TestCase
{
public function testItShouldResolveDependenciesOrder(): void
{
$registry = new Registry();

$registry->add(
new RegistryItem(
'cell',
RegistryItemType::Component,
'default',
'row',
''
)
);

$registry->add(
new RegistryItem(
'table',
RegistryItemType::Component,
'default',
null,
''
)
);

$registry->add(
new RegistryItem(
'row',
RegistryItemType::Component,
'default',
'table',
''
)
);

$registry->add(
new RegistryItem(
'button',
RegistryItemType::Component,
'default',
null,
''
)
);

$registry->add(
new RegistryItem(
'icon',
RegistryItemType::Component,
'default',
'button',
''
)
);

$resolver = new DependenciesResolver();
$resolved = $resolver->resolve($registry);
$this->assertEquals('table', $resolved[0]->name);
$this->assertEquals('row', $resolved[1]->name);
$this->assertEquals('cell', $resolved[2]->name);
$this->assertEquals('button', $resolved[3]->name);
$this->assertEquals('icon', $resolved[4]->name);
}

public function testCircularDependency(): void
{
$registry = new Registry();

$registry->add(
new RegistryItem(
'cell',
RegistryItemType::Component,
'default',
'row',
''
)
);

$registry->add(
new RegistryItem(
'table',
RegistryItemType::Component,
'default',
'cell',
''
)
);

$registry->add(
new RegistryItem(
'row',
RegistryItemType::Component,
'default',
'table',
''
)
);

$resolver = new DependenciesResolver();
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Circular dependency detected: table -> cell');
$resolver->resolve($registry);
}
}

0 comments on commit a32ce21

Please sign in to comment.