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:
2025-11-03 21:08:20 +01:00
parent 1655248de5
commit 247a046f51
4 changed files with 466 additions and 8 deletions

View File

@@ -9,6 +9,7 @@ use App\Framework\DI\Exceptions\ClassNotInstantiable;
use App\Framework\DI\Exceptions\ClassNotResolvableException;
use App\Framework\DI\Exceptions\ClassResolutionException;
use App\Framework\DI\ProactiveInitializerFinder;
use App\Framework\DI\ValueObjects\InitializerInfo;
use App\Framework\DI\Exceptions\ContainerException;
use App\Framework\DI\Exceptions\CyclicDependencyException;
use App\Framework\DI\Exceptions\LazyLoadingException;
@@ -40,6 +41,8 @@ final class DefaultContainer implements Container
public readonly FrameworkMetricsCollector $metrics;
private readonly InitializerDependencyAnalyzer $dependencyAnalyzer;
public function __construct(
private readonly InstanceRegistry $instances = new InstanceRegistry(),
private readonly BindingRegistry $bindings = new BindingRegistry(),
@@ -60,6 +63,7 @@ final class DefaultContainer implements Container
fn (): array => $this->resolving
);
$this->metrics = new FrameworkMetricsCollector();
$this->dependencyAnalyzer = new InitializerDependencyAnalyzer($this);
$this->registerSelf();
$this->instance(ReflectionProvider::class, $this->reflectionProvider);
@@ -144,7 +148,10 @@ final class DefaultContainer implements Container
if (in_array($class, $this->resolving, true)) {
throw new CyclicDependencyException(
dependencyChain: $this->resolving,
class: $class
class: $class,
code: 0,
previous: null,
dependencyAnalyzer: $this->dependencyAnalyzer
);
}
@@ -522,7 +529,8 @@ final class DefaultContainer implements Container
return $container->invoker->invoke($initializerClass, $methodName);
});
// TODO: Gefundenen Initializer in Discovery Cache nachtragen (später implementieren)
// Gefundenen Initializer in Discovery Cache nachtragen
$this->updateDiscoveryCache($initializerInfo);
return true;
} catch (\Throwable $e) {
@@ -530,4 +538,74 @@ final class DefaultContainer implements Container
return false;
}
}
/**
* Aktualisiert den Discovery Cache mit einem proaktiv gefundenen Initializer
*/
private function updateDiscoveryCache(InitializerInfo $initializerInfo): void
{
try {
$updater = $this->getCacheUpdater();
if ($updater === null) {
return;
}
// Versuche zuerst Registry aus Container zu laden
$registry = $this->has(DiscoveryRegistry::class)
? $this->get(DiscoveryRegistry::class)
: null;
$updater->updateCache($initializerInfo, $registry);
} catch (\Throwable $e) {
// Silently ignore errors during cache update
// Initializer funktioniert trotzdem, nur Cache-Update schlägt fehl
}
}
/**
* Holt oder erstellt einen InitializerCacheUpdater
*/
private function getCacheUpdater(): ?InitializerCacheUpdater
{
try {
// Prüfe ob alle benötigten Abhängigkeiten verfügbar sind
if (!$this->has(\App\Framework\Discovery\Storage\DiscoveryCacheManager::class)) {
// Versuche DiscoveryCacheManager zu erstellen
if (!$this->has(\App\Framework\Cache\Cache::class) ||
!$this->has(\App\Framework\DateTime\Clock::class) ||
!$this->has(\App\Framework\Filesystem\FileSystemService::class)) {
return null;
}
$cache = $this->get(\App\Framework\Cache\Cache::class);
$clock = $this->get(\App\Framework\DateTime\Clock::class);
$fileSystemService = $this->get(\App\Framework\Filesystem\FileSystemService::class);
$cacheManager = new \App\Framework\Discovery\Storage\DiscoveryCacheManager(
cache: $cache,
clock: $clock,
fileSystemService: $fileSystemService
);
} else {
$cacheManager = $this->get(\App\Framework\Discovery\Storage\DiscoveryCacheManager::class);
}
$pathProvider = $this->has(\App\Framework\Core\PathProvider::class)
? $this->get(\App\Framework\Core\PathProvider::class)
: new \App\Framework\Core\PathProvider(getcwd() ?: '.');
$clock = $this->has(\App\Framework\DateTime\Clock::class)
? $this->get(\App\Framework\DateTime\Clock::class)
: new \App\Framework\DateTime\SystemClock();
return new InitializerCacheUpdater(
reflectionProvider: $this->reflectionProvider,
cacheManager: $cacheManager,
pathProvider: $pathProvider,
clock: $clock
);
} catch (\Throwable $e) {
return null;
}
}
}