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:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,14 @@ final class CyclicDependencyException extends ContainerException
|
||||
private readonly string $cycleStart;
|
||||
private readonly array $fullChain;
|
||||
private readonly array $chainBeforeCycle;
|
||||
private readonly ?InitializerDependencyAnalyzer $dependencyAnalyzer;
|
||||
|
||||
public function __construct(
|
||||
array $dependencyChain,
|
||||
string $class,
|
||||
int $code = 0,
|
||||
?\Throwable $previous = null
|
||||
?\Throwable $previous = null,
|
||||
?InitializerDependencyAnalyzer $dependencyAnalyzer = null
|
||||
) {
|
||||
// Speichere vollständige Kette
|
||||
$this->fullChain = array_merge($dependencyChain, [$class]);
|
||||
@@ -44,6 +46,7 @@ final class CyclicDependencyException extends ContainerException
|
||||
|
||||
$this->cycle = $cycle;
|
||||
$this->cycleStart = $cycleStart;
|
||||
$this->dependencyAnalyzer = $dependencyAnalyzer;
|
||||
|
||||
$context = ExceptionContext::forOperation('dependency_resolution', 'DI')
|
||||
->withData([
|
||||
@@ -256,7 +259,8 @@ final class CyclicDependencyException extends ContainerException
|
||||
*/
|
||||
private function findProblematicDependency(array $initializerDependencies, string $interface): array
|
||||
{
|
||||
$analyzer = new InitializerDependencyAnalyzer();
|
||||
// Verwende Analyzer aus Exception (wenn verfügbar)
|
||||
$analyzer = $this->dependencyAnalyzer ?? new InitializerDependencyAnalyzer();
|
||||
|
||||
// Methode 1: Rekursive Suche - Finde vollständigen Pfad für jede Dependency
|
||||
foreach ($initializerDependencies as $dependency) {
|
||||
@@ -439,7 +443,7 @@ final class CyclicDependencyException extends ContainerException
|
||||
}
|
||||
|
||||
// Analysiere Dependencies des Initializers
|
||||
$analyzer = new InitializerDependencyAnalyzer();
|
||||
$analyzer = $this->dependencyAnalyzer ?? new InitializerDependencyAnalyzer();
|
||||
$dependencyAnalysis = $analyzer->analyze($initializerClass);
|
||||
|
||||
// Kombiniere Constructor- und container->get() Dependencies
|
||||
|
||||
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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,11 @@ namespace App\Framework\DI;
|
||||
final readonly class InitializerDependencyAnalyzer
|
||||
{
|
||||
private const MAX_RECURSION_DEPTH = 4;
|
||||
|
||||
public function __construct(
|
||||
private ?Container $container = null
|
||||
) {
|
||||
}
|
||||
/**
|
||||
* Analysiere Dependencies eines Initializers
|
||||
*
|
||||
@@ -351,18 +356,31 @@ final readonly class InitializerDependencyAnalyzer
|
||||
}
|
||||
|
||||
/**
|
||||
* Hole Dependencies einer Klasse (Constructor-Parameter)
|
||||
* Hole Dependencies einer Klasse (Constructor-Parameter + Array-Elemente)
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
private function getClassDependencies(string $className): array
|
||||
{
|
||||
try {
|
||||
if (!class_exists($className)) {
|
||||
if (!class_exists($className) && !interface_exists($className)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$reflection = new \ReflectionClass($className);
|
||||
|
||||
// Wenn es ein Interface ist, versuche die Implementierung zu finden
|
||||
if ($reflection->isInterface()) {
|
||||
// Suche nach bekannten Implementierungen (basierend auf Namenskonvention)
|
||||
$implClass = $this->findInterfaceImplementation($className);
|
||||
if ($implClass !== null && class_exists($implClass)) {
|
||||
$reflection = new \ReflectionClass($implClass);
|
||||
} else {
|
||||
// Kann keine Dependencies für Interfaces ohne Implementierung finden
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
$constructor = $reflection->getConstructor();
|
||||
|
||||
if ($constructor === null) {
|
||||
@@ -378,10 +396,210 @@ final readonly class InitializerDependencyAnalyzer
|
||||
}
|
||||
}
|
||||
|
||||
return $dependencies;
|
||||
// Zusätzlich: Prüfe ob Parameter Arrays sind, die Klassen-Namen enthalten könnten
|
||||
// (z.B. TemplateProcessor hat $astTransformers als Array von Klassen-Namen)
|
||||
foreach ($constructor->getParameters() as $parameter) {
|
||||
$type = $parameter->getType();
|
||||
if ($type instanceof \ReflectionNamedType && $type->getName() === 'array') {
|
||||
// Versuche Klassen-Namen aus dem Array zu extrahieren (via Code-Parsing)
|
||||
$arrayDeps = $this->extractClassNamesFromArrayParameter($reflection, $parameter);
|
||||
$dependencies = array_merge($dependencies, $arrayDeps);
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique($dependencies);
|
||||
} catch (\Throwable) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extrahiere Klassen-Namen aus Array-Parametern (z.B. $astTransformers = [XComponentTransformer::class])
|
||||
*
|
||||
* Sucht sowohl im Constructor als auch in Initializern (__invoke)
|
||||
*/
|
||||
private function extractClassNamesFromArrayParameter(\ReflectionClass $class, \ReflectionParameter $parameter): array
|
||||
{
|
||||
try {
|
||||
$fileName = $class->getFileName();
|
||||
if ($fileName === false || !file_exists($fileName)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$fileContent = file_get_contents($fileName);
|
||||
if ($fileContent === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$paramName = $parameter->getName();
|
||||
$classes = [];
|
||||
|
||||
// 1. Suche im Constructor
|
||||
$constructor = $class->getConstructor();
|
||||
if ($constructor !== null) {
|
||||
$startLine = $constructor->getStartLine();
|
||||
$endLine = $constructor->getEndLine();
|
||||
|
||||
if ($startLine !== false && $endLine !== false) {
|
||||
$lines = explode("\n", $fileContent);
|
||||
$constructorLines = array_slice($lines, $startLine - 1, $endLine - $startLine + 1);
|
||||
$constructorCode = implode("\n", $constructorLines);
|
||||
|
||||
// Finde Array-Initialisierungen für diesen Parameter
|
||||
// Pattern: [ClassName::class, ...] oder ['ClassName', ...]
|
||||
$pattern = '/\$' . preg_quote($paramName, '/') . '\s*=\s*\[(.*?)\]/s';
|
||||
if (preg_match($pattern, $constructorCode, $matches)) {
|
||||
$arrayClasses = $this->extractClassesFromArrayContent($matches[1], $fileContent, $constructor);
|
||||
$classes = array_merge($classes, $arrayClasses);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Suche auch in __invoke() (für Initializer)
|
||||
try {
|
||||
$invokeMethod = $class->getMethod('__invoke');
|
||||
$startLine = $invokeMethod->getStartLine();
|
||||
$endLine = $invokeMethod->getEndLine();
|
||||
|
||||
if ($startLine !== false && $endLine !== false) {
|
||||
$lines = explode("\n", $fileContent);
|
||||
$invokeLines = array_slice($lines, $startLine - 1, $endLine - $startLine + 1);
|
||||
$invokeCode = implode("\n", $invokeLines);
|
||||
|
||||
// Finde Array-Initialisierungen die an diesen Parameter übergeben werden
|
||||
// Pattern: [$paramName] = [...] oder new Class([...])
|
||||
// Oder: $paramName = [...]
|
||||
$pattern = '/\$' . preg_quote($paramName, '/') . '\s*=\s*\[(.*?)\]/s';
|
||||
if (preg_match($pattern, $invokeCode, $matches)) {
|
||||
$arrayClasses = $this->extractClassesFromArrayContent($matches[1], $fileContent, $invokeMethod);
|
||||
$classes = array_merge($classes, $arrayClasses);
|
||||
}
|
||||
|
||||
// Suche auch nach direkten Array-Definitionen die an den Parameter übergeben werden
|
||||
// z.B. new TemplateProcessor(astTransformers: [XComponentTransformer::class, ...])
|
||||
$pattern = '/\b' . preg_quote($paramName, '/') . '\s*:\s*\[(.*?)\]/s';
|
||||
if (preg_match($pattern, $invokeCode, $matches)) {
|
||||
$arrayClasses = $this->extractClassesFromArrayContent($matches[1], $fileContent, $invokeMethod);
|
||||
$classes = array_merge($classes, $arrayClasses);
|
||||
}
|
||||
}
|
||||
} catch (\ReflectionException) {
|
||||
// __invoke() existiert nicht - das ist okay
|
||||
}
|
||||
|
||||
return array_unique($classes);
|
||||
} catch (\Throwable) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extrahiere Klassen-Namen aus Array-Content-String
|
||||
*/
|
||||
private function extractClassesFromArrayContent(string $arrayContent, string $fileContent, \ReflectionMethod $method): array
|
||||
{
|
||||
// Finde alle Klassen-Namen (::class oder als String)
|
||||
$classPattern = '/([A-Z][A-Za-z0-9\\\\]+)(::class|\')/';
|
||||
preg_match_all($classPattern, $arrayContent, $classMatches);
|
||||
|
||||
$classes = [];
|
||||
if (!empty($classMatches[1])) {
|
||||
foreach ($classMatches[1] as $classMatch) {
|
||||
// Normalisiere (füge \ am Anfang hinzu wenn nicht vorhanden)
|
||||
if (!str_starts_with($classMatch, '\\') && !str_starts_with($classMatch, 'App\\')) {
|
||||
// Versuche vollständigen Namespace zu finden
|
||||
$fullClassName = $this->resolveClassNameFromMethod($classMatch, $fileContent, $method);
|
||||
if ($fullClassName !== null) {
|
||||
$classes[] = $fullClassName;
|
||||
} elseif (class_exists($classMatch)) {
|
||||
$classes[] = $classMatch;
|
||||
}
|
||||
} elseif (class_exists($classMatch)) {
|
||||
$classes[] = $classMatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Versuche Implementierung eines Interfaces zu finden
|
||||
*
|
||||
* Prüft zuerst Container-Bindings, dann Namenskonventionen
|
||||
*/
|
||||
private function findInterfaceImplementation(string $interface): ?string
|
||||
{
|
||||
// 1. Versuche über Container-Bindings (falls Container verfügbar)
|
||||
if ($this->container !== null) {
|
||||
try {
|
||||
// Prüfe ob Interface gebunden ist
|
||||
if ($this->container->has($interface)) {
|
||||
$binding = $this->getBindingForInterface($interface);
|
||||
if ($binding !== null) {
|
||||
// Wenn Binding ein String ist (Klassenname), verwende diesen
|
||||
if (is_string($binding) && class_exists($binding)) {
|
||||
return $binding;
|
||||
}
|
||||
|
||||
// Wenn Binding ein Objekt ist, verwende dessen Klasse
|
||||
if (is_object($binding)) {
|
||||
return $binding::class;
|
||||
}
|
||||
|
||||
// Wenn Binding ein Callable ist, können wir es nicht direkt auflösen
|
||||
// aber wir können versuchen, es zu instanziieren (wenn kein Zyklus)
|
||||
// Das ist riskant, also überspringen wir es für jetzt
|
||||
}
|
||||
}
|
||||
} catch (\Throwable) {
|
||||
// Container-Zugriff fehlgeschlagen - ignoriere und versuche Fallback
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Versuche Namenskonvention: Interface -> DefaultInterfaceName
|
||||
$interfaceName = basename(str_replace('\\', '/', $interface));
|
||||
$namespace = substr($interface, 0, strrpos($interface, '\\'));
|
||||
|
||||
// Versuche "Default" + InterfaceName ohne "Interface"
|
||||
$implName = str_replace('Interface', '', $interfaceName);
|
||||
$defaultImpl = $namespace . '\\Default' . $implName;
|
||||
if (class_exists($defaultImpl)) {
|
||||
return $defaultImpl;
|
||||
}
|
||||
|
||||
// Versuche einfach InterfaceName ohne "Interface"
|
||||
$simpleImpl = $namespace . '\\' . $implName;
|
||||
if (class_exists($simpleImpl)) {
|
||||
return $simpleImpl;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hole Binding für ein Interface aus dem Container
|
||||
*/
|
||||
private function getBindingForInterface(string $interface): callable|string|object|null
|
||||
{
|
||||
if ($this->container === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// Versuche über Introspector (sicherer, keine Instanziierung)
|
||||
// Introspector ist eine readonly Property im Container
|
||||
if (property_exists($this->container, 'introspector')) {
|
||||
$introspector = $this->container->introspector;
|
||||
return $introspector->getBinding($interface);
|
||||
}
|
||||
|
||||
// Fallback: Versuche direkt über BindingRegistry (wenn verfügbar)
|
||||
// Das ist nicht ideal, aber besser als nichts
|
||||
return null;
|
||||
} catch (\Throwable) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user