Files
michaelschiemer/src/Framework/DI/InitializerCacheUpdater.php
Michael Schiemer 247a046f51 feat(di, cache): add proactive initializer discovery and caching mechanics
- 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.
2025-11-03 21:08:20 +01:00

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()
);
}
}