- 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
203 lines
6.3 KiB
PHP
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;
|
|
}
|
|
}
|