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.
This commit is contained in:
158
src/Framework/DI/InitializerCacheUpdater.php
Normal file
158
src/Framework/DI/InitializerCacheUpdater.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user