- Introduce `InitializerDependencyAnalyzer` to support dependency analysis during cyclic exceptions. - Add proactive initializer discovery with `InitializerCacheUpdater` for improved performance. - Integrate discovery cache updates and error handling for seamless caching of found initializers. - Extend `CyclicDependencyException` with `InitializerDependencyAnalyzer` for enhanced diagnostics and cycle analysis.
159 lines
5.2 KiB
PHP
159 lines
5.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\DI;
|
|
|
|
use App\Framework\Core\PathProvider;
|
|
use App\Framework\DateTime\Clock;
|
|
use App\Framework\DI\ValueObjects\InitializerInfo;
|
|
use App\Framework\Discovery\Results\DiscoveryRegistry;
|
|
use App\Framework\Discovery\Storage\DiscoveryCacheManager;
|
|
use App\Framework\Discovery\ValueObjects\AttributeTarget;
|
|
use App\Framework\Discovery\ValueObjects\DiscoveredAttribute;
|
|
use App\Framework\Discovery\ValueObjects\DiscoveryContext;
|
|
use App\Framework\Discovery\ValueObjects\DiscoveryOptions;
|
|
use App\Framework\Discovery\ValueObjects\ScanType;
|
|
use App\Framework\Filesystem\FileSystemService;
|
|
use App\Framework\Filesystem\ValueObjects\FilePath;
|
|
use App\Framework\Reflection\ReflectionProvider;
|
|
|
|
/**
|
|
* Verantwortlich für das Update des Discovery Caches mit proaktiv gefundenen Initializern
|
|
*/
|
|
final readonly class InitializerCacheUpdater
|
|
{
|
|
public function __construct(
|
|
private ReflectionProvider $reflectionProvider,
|
|
private DiscoveryCacheManager $cacheManager,
|
|
private PathProvider $pathProvider,
|
|
private Clock $clock
|
|
) {}
|
|
|
|
/**
|
|
* Aktualisiert den Discovery Cache mit einem proaktiv gefundenen Initializer
|
|
*/
|
|
public function updateCache(InitializerInfo $initializerInfo, ?DiscoveryRegistry $registry = null): bool
|
|
{
|
|
try {
|
|
// Konvertiere InitializerInfo zu DiscoveredAttribute
|
|
$discoveredAttribute = $this->convertToDiscoveredAttribute($initializerInfo);
|
|
if ($discoveredAttribute === null) {
|
|
return false;
|
|
}
|
|
|
|
// Lade aktuelle DiscoveryRegistry falls nicht übergeben
|
|
if ($registry === null) {
|
|
$registry = $this->loadDiscoveryRegistry();
|
|
}
|
|
|
|
if ($registry === null) {
|
|
return false;
|
|
}
|
|
|
|
// Füge DiscoveredAttribute zur Registry hinzu
|
|
$registry->attributes->add(Initializer::class, $discoveredAttribute);
|
|
|
|
// Speichere aktualisierte Registry zurück in Cache
|
|
return $this->storeDiscoveryRegistry($registry);
|
|
} catch (\Throwable $e) {
|
|
// Silently ignore errors during cache update
|
|
// Initializer funktioniert trotzdem, nur Cache-Update schlägt fehl
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Konvertiert InitializerInfo zu DiscoveredAttribute
|
|
*/
|
|
private function convertToDiscoveredAttribute(InitializerInfo $initializerInfo): ?DiscoveredAttribute
|
|
{
|
|
try {
|
|
$initializerClass = $initializerInfo->initializerClass;
|
|
$methodName = $initializerInfo->methodName;
|
|
|
|
// Bestimme filePath via Reflection
|
|
$reflection = $this->reflectionProvider->getClass($initializerClass);
|
|
$fileName = $reflection->getFileName();
|
|
$filePath = $fileName !== false ? FilePath::create($fileName) : null;
|
|
|
|
// Generiere additionalData via InitializerMapper
|
|
$methodReflectionWrapped = $this->reflectionProvider->getMethod(
|
|
$initializerClass,
|
|
$methodName->toString()
|
|
);
|
|
|
|
// Erstelle Initializer Attribut-Instanz für Mapper (keine Contexts = alle erlaubt)
|
|
$initializerAttr = new Initializer();
|
|
|
|
// Nutze InitializerMapper um additionalData zu generieren
|
|
$mapper = new InitializerMapper();
|
|
$additionalData = $mapper->map($methodReflectionWrapped, $initializerAttr);
|
|
|
|
return new DiscoveredAttribute(
|
|
className: $initializerClass,
|
|
attributeClass: Initializer::class,
|
|
target: AttributeTarget::METHOD,
|
|
methodName: $methodName,
|
|
propertyName: null,
|
|
arguments: [],
|
|
filePath: $filePath,
|
|
additionalData: $additionalData
|
|
);
|
|
} catch (\Throwable $e) {
|
|
// Silently ignore errors during conversion
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lädt die aktuelle DiscoveryRegistry aus Cache
|
|
*/
|
|
private function loadDiscoveryRegistry(): ?DiscoveryRegistry
|
|
{
|
|
try {
|
|
$context = $this->createDiscoveryContext();
|
|
$cached = $this->cacheManager->get($context);
|
|
|
|
if ($cached !== null) {
|
|
return $cached;
|
|
}
|
|
|
|
// Falls Cache leer, erstelle neue Registry
|
|
return DiscoveryRegistry::empty();
|
|
} catch (\Throwable $e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Speichert die DiscoveryRegistry zurück in den Cache
|
|
*/
|
|
private function storeDiscoveryRegistry(DiscoveryRegistry $registry): bool
|
|
{
|
|
try {
|
|
$context = $this->createDiscoveryContext();
|
|
|
|
return $this->cacheManager->store($context, $registry);
|
|
} catch (\Throwable $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Erstellt einen DiscoveryContext für Cache-Zugriff
|
|
*/
|
|
private function createDiscoveryContext(): DiscoveryContext
|
|
{
|
|
$paths = [$this->pathProvider->getSourcePath()];
|
|
|
|
return new DiscoveryContext(
|
|
paths: $paths,
|
|
scanType: ScanType::FULL,
|
|
options: DiscoveryOptions::default(),
|
|
startTime: $this->clock->now()
|
|
);
|
|
}
|
|
}
|
|
|