Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
186
src/Framework/DI/InitializerDependencyGraph.php
Normal file
186
src/Framework/DI/InitializerDependencyGraph.php
Normal file
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\DI;
|
||||
|
||||
use App\Framework\Core\ValueObjects\ClassName;
|
||||
use App\Framework\Core\ValueObjects\MethodName;
|
||||
use App\Framework\DI\ValueObjects\DependencyGraphNode;
|
||||
use App\Framework\Reflection\ReflectionProvider;
|
||||
|
||||
/**
|
||||
* Dependency Graph für Initializer-Ausführung
|
||||
* Analysiert Dependencies und sorgt für korrekte Ausführungsreihenfolge
|
||||
*/
|
||||
final class InitializerDependencyGraph
|
||||
{
|
||||
/** @var array<string, DependencyGraphNode> */
|
||||
private array $nodes = [];
|
||||
|
||||
/** @var array<string, array<string>> */
|
||||
private array $adjacencyList = [];
|
||||
|
||||
/** @var array<string, bool> */
|
||||
private array $visited = [];
|
||||
|
||||
/** @var array<string, bool> */
|
||||
private array $inStack = [];
|
||||
|
||||
public function __construct(
|
||||
private readonly ReflectionProvider $reflectionProvider,
|
||||
) {
|
||||
}
|
||||
|
||||
public function addInitializer(string $returnType, ClassName $className, MethodName $methodName): void
|
||||
{
|
||||
if ($returnType === 'null' || $returnType === 'void') {
|
||||
return; // Setup-Initializer werden sofort ausgeführt
|
||||
}
|
||||
|
||||
$dependencies = $this->analyzeDependencies($className, $methodName);
|
||||
|
||||
$node = new DependencyGraphNode(
|
||||
returnType: $returnType,
|
||||
className: $className,
|
||||
methodName: $methodName,
|
||||
dependencies: $dependencies
|
||||
);
|
||||
|
||||
$this->nodes[$returnType] = $node;
|
||||
$this->adjacencyList[$returnType] = $dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Berechnet die optimale Ausführungsreihenfolge basierend auf Dependencies
|
||||
* @return array<string> Sortierte Liste von Return-Types
|
||||
*/
|
||||
public function getExecutionOrder(): array
|
||||
{
|
||||
$this->visited = [];
|
||||
$this->inStack = [];
|
||||
$result = [];
|
||||
|
||||
foreach (array_keys($this->nodes) as $returnType) {
|
||||
if (! isset($this->visited[$returnType])) {
|
||||
$this->topologicalSort($returnType, $result);
|
||||
}
|
||||
}
|
||||
|
||||
return array_reverse($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft ob ein Node für den gegebenen Return-Type existiert
|
||||
*/
|
||||
public function hasNode(string $returnType): bool
|
||||
{
|
||||
return isset($this->nodes[$returnType]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt einen spezifischen Node zurück
|
||||
*/
|
||||
public function getNode(string $returnType): ?DependencyGraphNode
|
||||
{
|
||||
return $this->nodes[$returnType] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle Nodes zurück
|
||||
*
|
||||
* @return array<string, DependencyGraphNode>
|
||||
*/
|
||||
public function getNodes(): array
|
||||
{
|
||||
return $this->nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle Nodes als Array zurück (backward compatibility)
|
||||
*
|
||||
* @deprecated Use getNodes() for DependencyGraphNode objects
|
||||
* @return array<string, array{class: string, method: string, dependencies: string[]}>
|
||||
*/
|
||||
public function getNodesAsArray(): array
|
||||
{
|
||||
return array_map(fn ($node) => $node->toArray(), $this->nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysiert die Dependencies eines Initializers durch Reflection
|
||||
*/
|
||||
private function analyzeDependencies(CLassName $className, MethodName $methodName): array
|
||||
{
|
||||
try {
|
||||
$reflection = $this->reflectionProvider->getClass($className);
|
||||
#$reflection = new \ReflectionClass($className);
|
||||
$method = $reflection->getMethod($methodName->toString());
|
||||
;
|
||||
$dependencies = [];
|
||||
|
||||
foreach ($method->getParameters() as $parameter) {
|
||||
$paramType = $parameter->getType();
|
||||
if ($paramType instanceof \ReflectionNamedType) {
|
||||
$typeName = $paramType->getName();
|
||||
|
||||
// Nur Klassen/Interfaces als Dependencies, keine primitiven Typen
|
||||
if (! empty($typeName) && ClassName::create($typeName)->exists()) {
|
||||
$dependencies[] = $typeName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $dependencies;
|
||||
} catch (\Throwable $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Topologische Sortierung mit Cycle-Detection
|
||||
*/
|
||||
private function topologicalSort(string $returnType, array &$result): void
|
||||
{
|
||||
$this->visited[$returnType] = true;
|
||||
$this->inStack[$returnType] = true;
|
||||
|
||||
foreach ($this->adjacencyList[$returnType] ?? [] as $dependency) {
|
||||
if (isset($this->nodes[$dependency])) {
|
||||
if (isset($this->inStack[$dependency]) && $this->inStack[$dependency]) {
|
||||
// Cycle detected - ignore this dependency to prevent infinite loops
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! isset($this->visited[$dependency])) {
|
||||
$this->topologicalSort($dependency, $result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->inStack[$returnType] = false;
|
||||
$result[] = $returnType;
|
||||
}
|
||||
|
||||
private function hasCycle(string $returnType): bool
|
||||
{
|
||||
$this->visited[$returnType] = true;
|
||||
$this->inStack[$returnType] = true;
|
||||
|
||||
foreach ($this->adjacencyList[$returnType] ?? [] as $dependency) {
|
||||
if (isset($this->nodes[$dependency])) {
|
||||
if (isset($this->inStack[$dependency]) && $this->inStack[$dependency]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! isset($this->visited[$dependency]) && $this->hasCycle($dependency)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->inStack[$returnType] = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user