Files
michaelschiemer/src/Framework/DI/InitializerDependencyGraph.php
Michael Schiemer 5050c7d73a docs: consolidate documentation into organized structure
- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

Root directory cleanup:
- Reduced from 16 to 4 markdown files in root
- Only essential project files remain:
  • CLAUDE.md (AI instructions)
  • README.md (Main project readme)
  • CLEANUP_PLAN.md (Current cleanup plan)
  • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements)

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
2025-10-05 11:05:04 +02:00

203 lines
6.3 KiB
PHP

<?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
);
// Debug RouterSetup specifically
if (str_contains($className->toString(), 'RouterSetup')) {
error_log("InitializerDependencyGraph: Adding RouterSetup node - " . $className->toString() . " -> " . $returnType);
error_log("InitializerDependencyGraph: RouterSetup dependencies: " . implode(', ', $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
{
$exists = isset($this->nodes[$returnType]);
// Debug RouterSetup specifically
if ($returnType === 'App\\Framework\\Router\\Router') {
error_log("InitializerDependencyGraph: hasNode check for Router - exists: " . ($exists ? 'YES' : 'NO'));
error_log("InitializerDependencyGraph: Current node count: " . count($this->nodes));
error_log("InitializerDependencyGraph: Available nodes: " . implode(', ', array_keys($this->nodes)));
}
return $exists;
}
/**
* 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;
}
}