feat(di): add proactive initializer finder for interface resolution
- Add ProactiveInitializerFinder to search for initializers when not found in registry - Add InitializerInfo value object to store initializer metadata - Implement multi-step search strategy: DefaultImplementation, naming convention, directory, subdirectories, module - Integrate proactive finder into DefaultContainer for better interface resolution - Simplify AppBootstrapper by moving initialization logic to DefaultContainer - Improve error messages in ClassNotInstantiable with proactive finder context
This commit is contained in:
@@ -8,6 +8,7 @@ use App\Framework\Core\ValueObjects\ClassName;
|
||||
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\Exceptions\ContainerException;
|
||||
use App\Framework\DI\Exceptions\CyclicDependencyException;
|
||||
use App\Framework\DI\Exceptions\LazyLoadingException;
|
||||
@@ -193,6 +194,12 @@ final class DefaultContainer implements Container
|
||||
|
||||
// Check if class is instantiable using the framework's method
|
||||
if (! $reflection->isInstantiable()) {
|
||||
// Proaktive Suche nach Initializern für Interfaces
|
||||
if ($reflection->isInterface() && $this->tryFindAndRegisterInitializer($class)) {
|
||||
// Initializer gefunden und registriert - versuche erneut zu resolven
|
||||
return $this->get($class);
|
||||
}
|
||||
|
||||
$this->throwDetailedBindingException($class);
|
||||
}
|
||||
|
||||
@@ -217,6 +224,24 @@ final class DefaultContainer implements Container
|
||||
{
|
||||
$availableBindings = array_keys($this->bindings->getAllBindings());
|
||||
|
||||
// Bestimme den Typ der Klasse mit Reflection
|
||||
$className = ClassName::create($class);
|
||||
$isInterface = false;
|
||||
$isAbstract = false;
|
||||
$isTrait = false;
|
||||
|
||||
try {
|
||||
$reflection = $this->reflectionProvider->getClass($className);
|
||||
$isInterface = $reflection->isInterface();
|
||||
$isAbstract = $reflection->isAbstract();
|
||||
$isTrait = $reflection->isTrait();
|
||||
} catch (\Throwable $e) {
|
||||
// Fallback zu einfachen Prüfungen wenn Reflection fehlschlägt
|
||||
$isInterface = interface_exists($class);
|
||||
// Für Abstract und Trait können wir nicht so einfach prüfen, aber wir haben bereits
|
||||
// festgestellt dass die Klasse nicht instanziierbar ist
|
||||
}
|
||||
|
||||
// Try to get DiscoveryRegistry from container and include discovered initializers
|
||||
$discoveredInitializers = [];
|
||||
$matchingInitializers = [];
|
||||
@@ -235,7 +260,7 @@ final class DefaultContainer implements Container
|
||||
);
|
||||
|
||||
// Spezielle Behandlung für Interfaces: Suche nach Initializern die dieses Interface zurückgeben
|
||||
if (interface_exists($class)) {
|
||||
if ($isInterface) {
|
||||
foreach ($initializerResults as $initializer) {
|
||||
$returnType = $initializer->additionalData['return'] ?? null;
|
||||
if ($returnType === $class) {
|
||||
@@ -279,7 +304,10 @@ final class DefaultContainer implements Container
|
||||
discoveredInitializers: $discoveredInitializers,
|
||||
matchingInitializers: $matchingInitializers,
|
||||
suggestedInitializer: $suggestedInitializer,
|
||||
failedInitializer: $failedInitializer
|
||||
failedInitializer: $failedInitializer,
|
||||
isInterface: $isInterface,
|
||||
isAbstract: $isAbstract,
|
||||
isTrait: $isTrait
|
||||
);
|
||||
}
|
||||
|
||||
@@ -436,4 +464,70 @@ final class DefaultContainer implements Container
|
||||
$this->instances->setSingleton(DefaultContainer::class, $this);
|
||||
$this->instances->setSingleton(Container::class, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy-getter für ProactiveInitializerFinder
|
||||
*/
|
||||
private function getProactiveFinder(): ProactiveInitializerFinder
|
||||
{
|
||||
// Erstelle Finder nur wenn benötigt
|
||||
$discoveryRegistry = $this->has(DiscoveryRegistry::class) ? $this->get(DiscoveryRegistry::class) : null;
|
||||
if ($discoveryRegistry === null) {
|
||||
// Fallback: Leere Registry erstellen
|
||||
$discoveryRegistry = \App\Framework\Discovery\Results\DiscoveryRegistry::empty();
|
||||
}
|
||||
|
||||
$fileScanner = $this->has(\App\Framework\Filesystem\FileScanner::class)
|
||||
? $this->get(\App\Framework\Filesystem\FileScanner::class)
|
||||
: new \App\Framework\Filesystem\FileScanner(null, null, new \App\Framework\Filesystem\FileSystemService());
|
||||
|
||||
$fileSystemService = new \App\Framework\Filesystem\FileSystemService();
|
||||
$classExtractor = new \App\Framework\Discovery\Processing\ClassExtractor($fileSystemService);
|
||||
|
||||
$pathProvider = $this->has(\App\Framework\Core\PathProvider::class)
|
||||
? $this->get(\App\Framework\Core\PathProvider::class)
|
||||
: new \App\Framework\Core\PathProvider(getcwd() ?: '.');
|
||||
|
||||
return new ProactiveInitializerFinder(
|
||||
reflectionProvider: $this->reflectionProvider,
|
||||
discoveryRegistry: $discoveryRegistry,
|
||||
fileScanner: $fileScanner,
|
||||
classExtractor: $classExtractor,
|
||||
pathProvider: $pathProvider
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Versucht proaktiv einen Initializer für ein Interface zu finden und zu registrieren
|
||||
*
|
||||
* @param string $interface Interface-Klasse
|
||||
* @return bool True wenn Initializer gefunden und registriert wurde, false sonst
|
||||
*/
|
||||
private function tryFindAndRegisterInitializer(string $interface): bool
|
||||
{
|
||||
try {
|
||||
$finder = $this->getProactiveFinder();
|
||||
$initializerInfo = $finder->findInitializerForInterface($interface);
|
||||
|
||||
if ($initializerInfo === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Registriere Initializer als lazy binding
|
||||
$initializerClass = $initializerInfo->initializerClass->getFullyQualified();
|
||||
$methodName = $initializerInfo->methodName->toString();
|
||||
|
||||
$this->singleton($interface, function (Container $container) use ($initializerClass, $methodName) {
|
||||
$instance = $container->get($initializerClass);
|
||||
return $container->invoker->invoke($initializerClass, $methodName);
|
||||
});
|
||||
|
||||
// TODO: Gefundenen Initializer in Discovery Cache nachtragen (später implementieren)
|
||||
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
// Silently ignore errors during proactive search
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user