Major refactoring cleanup: - Add new controller architecture (class-controller-*.php) - Add new settings-v2 UI (views/settings-v2/) - Add new CSS architecture (agentic-sidebar.css, tokens) - Add esbuild build pipeline (scripts/build.js, package.json) - Add composer dependencies (vendor/) - Add frontend src directory (assets/js/src/index.jsx) - Add documentation files - Remove old/obsolete files (class-settings.php, old CSS) This commits all pending changes from previous refactoring efforts.
131 lines
4.4 KiB
PHP
131 lines
4.4 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This file is part of the Symfony package.
|
|
*
|
|
* (c) Fabien Potencier <fabien@symfony.com>
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace Symfony\Component\DependencyInjection\Compiler;
|
|
|
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
|
use Symfony\Component\DependencyInjection\Definition;
|
|
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
|
|
use Symfony\Component\DependencyInjection\Reference;
|
|
|
|
/**
|
|
* Detects cycles where a service's factory is `[Definition $b, $method]` and $b's
|
|
* own properties/method calls/configurator transitively require that same service
|
|
* through constructor references.
|
|
*
|
|
* The soft-circular instantiation pattern relies on storing the service's instance
|
|
* before deferred edges fire, but a factory consumes the builder's state at call
|
|
* time: deferring $b's setup until after `$b->method(...)` would feed the factory
|
|
* an unconfigured builder. The cycle is unresolvable; bail out instead of letting
|
|
* the dumper produce silently wrong code or `ContainerBuilder::createService()`
|
|
* silently return a half-built instance.
|
|
*/
|
|
class CheckFactoryBuilderCircularReferencePass implements CompilerPassInterface
|
|
{
|
|
private ContainerBuilder $container;
|
|
private array $visited;
|
|
private array $path;
|
|
private string $currentId;
|
|
|
|
public function process(ContainerBuilder $container): void
|
|
{
|
|
$this->container = $container;
|
|
|
|
try {
|
|
foreach ($container->getDefinitions() as $id => $definition) {
|
|
$factory = $definition->getFactory();
|
|
if (!\is_array($factory) || !$factory[0] instanceof Definition) {
|
|
continue;
|
|
}
|
|
|
|
$builder = $factory[0];
|
|
if (!$builder->getMethodCalls() && !$builder->getProperties() && null === $builder->getConfigurator()) {
|
|
continue;
|
|
}
|
|
|
|
$this->currentId = $id;
|
|
$this->visited = [$id => true];
|
|
$this->path = [$id];
|
|
|
|
$setup = [$builder->getProperties(), $builder->getMethodCalls(), $builder->getConfigurator()];
|
|
if ($this->setupReferencesCurrent($setup)) {
|
|
$this->path[] = $id;
|
|
|
|
throw new ServiceCircularReferenceException($id, $this->path);
|
|
}
|
|
}
|
|
} finally {
|
|
unset($this->container, $this->visited, $this->path, $this->currentId);
|
|
}
|
|
}
|
|
|
|
private function setupReferencesCurrent(mixed $value): bool
|
|
{
|
|
if (\is_array($value)) {
|
|
foreach ($value as $v) {
|
|
if ($this->setupReferencesCurrent($v)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if ($value instanceof Reference) {
|
|
$id = (string) $value;
|
|
while ($this->container->hasAlias($id)) {
|
|
$id = (string) $this->container->getAlias($id);
|
|
}
|
|
|
|
if ($id === $this->currentId) {
|
|
return true;
|
|
}
|
|
|
|
if (isset($this->visited[$id]) || !$this->container->hasDefinition($id)) {
|
|
return false;
|
|
}
|
|
|
|
$def = $this->container->getDefinition($id);
|
|
|
|
// Lazy services break the eager-construction chain through a proxy.
|
|
if ($def->isLazy() || $def->isSynthetic()) {
|
|
return false;
|
|
}
|
|
|
|
$this->visited[$id] = true;
|
|
$this->path[] = $id;
|
|
|
|
// Only constructor edges propagate the "needed during construction" reachability.
|
|
if ($this->setupReferencesCurrent([$def->getArguments(), $def->getFactory()])) {
|
|
return true;
|
|
}
|
|
|
|
array_pop($this->path);
|
|
|
|
return false;
|
|
}
|
|
|
|
if ($value instanceof Definition) {
|
|
// Inlined sub-definition: every part of it runs while the outer service
|
|
// is still being constructed, so walk its full structure.
|
|
return $this->setupReferencesCurrent([
|
|
$value->getArguments(),
|
|
$value->getFactory(),
|
|
$value->getProperties(),
|
|
$value->getMethodCalls(),
|
|
$value->getConfigurator(),
|
|
]);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|