refactor(di): add InitializerDependencyAnalyzer for enhanced dependency analysis and messaging
- Introduce `InitializerDependencyAnalyzer` to analyze constructor and `container->get()` dependencies. - Enhance `CyclicDependencyException` with warnings for `container->get()` usage and explicit guidance for resolving cycles. - Improve error messaging with detailed dependency sources and actionable best practices.
This commit is contained in:
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Framework\DI\Exceptions;
|
||||
|
||||
use App\Framework\DI\Initializer;
|
||||
use App\Framework\DI\InitializerDependencyAnalyzer;
|
||||
use App\Framework\Discovery\Results\DiscoveryRegistry;
|
||||
use App\Framework\Exception\ExceptionContext;
|
||||
|
||||
@@ -105,6 +106,21 @@ final class CyclicDependencyException extends ContainerException
|
||||
if ($initializerInfo['isInitializerCycle']) {
|
||||
$message .= "⚠️ Initializer-Zyklus erkannt!\n\n";
|
||||
|
||||
$dependencyAnalysis = $initializerInfo['dependencyAnalysis'] ?? null;
|
||||
$hasContainerGetCalls = $dependencyAnalysis !== null && $dependencyAnalysis['hasContainerGetCalls'];
|
||||
|
||||
// Warnung über container->get() Anti-Pattern
|
||||
if ($hasContainerGetCalls) {
|
||||
$message .= " ⚠️ WICHTIG: Initializer verwendet container->get() Aufrufe!\n\n";
|
||||
$message .= " Problem: container->get() Aufrufe umgehen die Dependency-Analyse.\n";
|
||||
$message .= " Dadurch werden zyklische Abhängigkeiten schwer erkennbar und\n";
|
||||
$message .= " die Fehlermeldung kann unvollständig sein.\n\n";
|
||||
$message .= " 💡 Best Practice:\n";
|
||||
$message .= " • Verwende Constructor Injection statt container->get()\n";
|
||||
$message .= " • Das macht Dependencies explizit und nachvollziehbar\n";
|
||||
$message .= " • Erleichtert die Dependency-Analyse und Fehlerdiagnose\n\n";
|
||||
}
|
||||
|
||||
// Finde welche Dependency das Interface benötigt
|
||||
$problematicInfo = $this->findProblematicDependency(
|
||||
$initializerInfo['initializerDependencies'] ?? [],
|
||||
@@ -123,7 +139,13 @@ final class CyclicDependencyException extends ContainerException
|
||||
$message .= " Der Initializer '{$initializerInfo['initializerClass']}' benötigt\n";
|
||||
$message .= " das Interface/abstrakte Klasse '{$this->cycleStart}',\n";
|
||||
$message .= " welches wiederum den Initializer benötigt.\n";
|
||||
$message .= " (Konnte die problematische Dependency nicht eindeutig identifizieren)\n\n";
|
||||
if ($hasContainerGetCalls) {
|
||||
$message .= " Hinweis: Da container->get() verwendet wird, könnte die problematische\n";
|
||||
$message .= " Dependency in den container->get() Aufrufen versteckt sein.\n";
|
||||
} else {
|
||||
$message .= " (Konnte die problematische Dependency nicht eindeutig identifizieren)\n";
|
||||
}
|
||||
$message .= "\n";
|
||||
}
|
||||
|
||||
$message .= "🔧 Spezifische Lösung für Initializer-Zyklen:\n";
|
||||
@@ -131,7 +153,11 @@ final class CyclicDependencyException extends ContainerException
|
||||
if ($problematicDependency !== null) {
|
||||
$message .= " • '{$problematicDependency}' sollte '{$this->cycleStart}' NICHT im Constructor benötigen\n";
|
||||
$message .= " • Verwende Lazy Loading für '{$problematicDependency}' → '{$this->cycleStart}'\n";
|
||||
$message .= " (z.B. über Container->get() statt Constructor-Injection)\n";
|
||||
$message .= " (z.B. über Container->get() im __invoke(), NICHT im Constructor)\n";
|
||||
}
|
||||
if ($hasContainerGetCalls) {
|
||||
$message .= " • Refactoriere zu Constructor Injection statt container->get()\n";
|
||||
$message .= " (macht Dependencies explizit und vermeidet versteckte Zyklen)\n";
|
||||
}
|
||||
$message .= " • Verwende Container als Parameter und hole das Interface erst im __invoke()\n";
|
||||
$message .= " • Oder: Refactoriere den Initializer, sodass er keine zirkuläre Abhängigkeit hat\n\n";
|
||||
@@ -153,7 +179,8 @@ final class CyclicDependencyException extends ContainerException
|
||||
private function buildInitializerChainMessage(string $firstClass, array $initializerInfo): string
|
||||
{
|
||||
$initializerClass = $initializerInfo['initializerClass'];
|
||||
$dependencies = $initializerInfo['initializerDependencies'] ?? [];
|
||||
$allDependencies = $initializerInfo['initializerDependencies'] ?? [];
|
||||
$dependencyAnalysis = $initializerInfo['dependencyAnalysis'] ?? null;
|
||||
$interface = $this->cycleStart;
|
||||
|
||||
$message = "📋 Vollständige Abhängigkeitskette:\n\n";
|
||||
@@ -162,18 +189,36 @@ final class CyclicDependencyException extends ContainerException
|
||||
$message .= " ↓ wird erstellt durch Initializer\n";
|
||||
$message .= " 3. '{$initializerClass}' (Initializer)\n";
|
||||
|
||||
if (!empty($dependencies)) {
|
||||
// Warnung wenn container->get() verwendet wird
|
||||
if ($dependencyAnalysis !== null && $dependencyAnalysis['hasContainerGetCalls']) {
|
||||
$message .= " ⚠️ WARNUNG: Initializer verwendet container->get() Aufrufe!\n";
|
||||
$message .= " Dies umgeht die Dependency-Analyse und macht Zyklen schwer erkennbar.\n\n";
|
||||
}
|
||||
|
||||
if (!empty($allDependencies)) {
|
||||
$message .= " ↓ benötigt folgende Dependencies:\n";
|
||||
|
||||
// Finde problematische Dependency für Hervorhebung
|
||||
$problematicInfo = $this->findProblematicDependency($dependencies, $interface);
|
||||
$problematicInfo = $this->findProblematicDependency($allDependencies, $interface);
|
||||
$problematicDependency = $problematicInfo['problematicDependency'];
|
||||
|
||||
foreach ($dependencies as $index => $dependency) {
|
||||
$isLast = $index === count($dependencies) - 1;
|
||||
$constructorDeps = $dependencyAnalysis !== null ? $dependencyAnalysis['constructorDeps'] : [];
|
||||
$containerGetDeps = $dependencyAnalysis !== null ? $dependencyAnalysis['containerGetDeps'] : [];
|
||||
|
||||
foreach ($allDependencies as $index => $dependency) {
|
||||
$isLast = $index === count($allDependencies) - 1;
|
||||
$arrow = $isLast ? '└─→' : '├─→';
|
||||
|
||||
// Bestimme Quelle der Dependency
|
||||
$source = '';
|
||||
if (in_array($dependency, $containerGetDeps, true)) {
|
||||
$source = ' (via container->get())';
|
||||
} elseif (in_array($dependency, $constructorDeps, true)) {
|
||||
$source = ' (Constructor)';
|
||||
}
|
||||
|
||||
$highlight = ($dependency === $problematicDependency) ? ' ⚠️ (benötigt Interface!)' : '';
|
||||
$message .= " {$arrow} '{$dependency}'{$highlight}\n";
|
||||
$message .= " {$arrow} '{$dependency}'{$source}{$highlight}\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,7 +362,12 @@ final class CyclicDependencyException extends ContainerException
|
||||
}
|
||||
|
||||
if (!$isInterfaceOrAbstract) {
|
||||
return ['isInitializerCycle' => false, 'initializerClass' => null, 'initializerDependencies' => null];
|
||||
return [
|
||||
'isInitializerCycle' => false,
|
||||
'initializerClass' => null,
|
||||
'initializerDependencies' => null,
|
||||
'dependencyAnalysis' => null,
|
||||
];
|
||||
}
|
||||
|
||||
// Suche nach Initializer-Klassen in der Kette
|
||||
@@ -335,16 +385,31 @@ final class CyclicDependencyException extends ContainerException
|
||||
}
|
||||
|
||||
if ($initializerClass === null) {
|
||||
return ['isInitializerCycle' => false, 'initializerClass' => null, 'initializerDependencies' => null];
|
||||
return [
|
||||
'isInitializerCycle' => false,
|
||||
'initializerClass' => null,
|
||||
'initializerDependencies' => null,
|
||||
'dependencyAnalysis' => null,
|
||||
];
|
||||
}
|
||||
|
||||
// Analysiere Dependencies des Initializers
|
||||
$dependencies = $this->analyzeInitializerDependencies($initializerClass);
|
||||
$analyzer = new InitializerDependencyAnalyzer();
|
||||
$dependencyAnalysis = $analyzer->analyze($initializerClass);
|
||||
|
||||
// Kombiniere Constructor- und container->get() Dependencies
|
||||
$allDependencies = array_merge(
|
||||
$dependencyAnalysis['constructorDeps'],
|
||||
$dependencyAnalysis['containerGetDeps']
|
||||
);
|
||||
$allDependencies = array_unique($allDependencies);
|
||||
$allDependencies = array_values($allDependencies);
|
||||
|
||||
return [
|
||||
'isInitializerCycle' => true,
|
||||
'initializerClass' => $initializerClass,
|
||||
'initializerDependencies' => $dependencies,
|
||||
'initializerDependencies' => $allDependencies,
|
||||
'dependencyAnalysis' => $dependencyAnalysis,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -355,30 +420,60 @@ final class CyclicDependencyException extends ContainerException
|
||||
*/
|
||||
private function findInitializerForInterface(string $interface): ?string
|
||||
{
|
||||
// Versuche über DiscoveryRegistry
|
||||
try {
|
||||
// Prüfe ob DiscoveryRegistry global verfügbar ist (z.B. über Container)
|
||||
// Da wir keinen direkten Zugriff haben, versuchen wir eine Namenskonvention
|
||||
// oder schauen ob wir DiscoveryRegistry über einen Singleton-Pattern erreichen können
|
||||
} catch (\Throwable) {
|
||||
// Ignoriere Fehler
|
||||
}
|
||||
// Versuche über DiscoveryRegistry (falls verfügbar)
|
||||
// Note: DiscoveryRegistry benötigt selbst Dependencies, daher kann es hier fehlschlagen
|
||||
// Wir verwenden daher primär Namenskonvention
|
||||
|
||||
// Fallback: Namenskonvention
|
||||
// ComponentRegistryInterface -> ComponentRegistryInitializer
|
||||
// Namenskonvention: ComponentRegistryInterface -> ComponentRegistryInitializer
|
||||
$interfaceName = basename(str_replace('\\', '/', $interface));
|
||||
$suggestedName = str_replace('Interface', '', $interfaceName) . 'Initializer';
|
||||
|
||||
// Suche in gleichem Namespace
|
||||
$namespace = substr($interface, 0, strrpos($interface, '\\'));
|
||||
$suggestedClass = $namespace . '\\' . $suggestedName;
|
||||
|
||||
// Strategie 1: Suche im gleichen Namespace (falls Interface nicht in Contracts ist)
|
||||
$suggestedClass = $namespace . '\\' . $suggestedName;
|
||||
if (class_exists($suggestedClass)) {
|
||||
return $suggestedClass;
|
||||
}
|
||||
|
||||
// Versuche auch ohne Namespace-Präfix
|
||||
$suggestedClassShort = 'App\\Framework\\' . str_replace('Interface', '', $interfaceName) . 'Initializer';
|
||||
// Strategie 2: Entferne "Contracts" aus dem Namespace (Initializer sind normalerweise nicht im Contracts-Namespace)
|
||||
// App\Framework\LiveComponents\Contracts\ComponentRegistryInterface
|
||||
// -> App\Framework\LiveComponents\ComponentRegistryInitializer
|
||||
if (str_ends_with($namespace, '\\Contracts')) {
|
||||
$parentNamespace = substr($namespace, 0, -10); // Entferne '\Contracts'
|
||||
$suggestedClass = $parentNamespace . '\\' . $suggestedName;
|
||||
if (class_exists($suggestedClass)) {
|
||||
return $suggestedClass;
|
||||
}
|
||||
}
|
||||
|
||||
// Strategie 3: Suche im übergeordneten Namespace (einen Level höher)
|
||||
if (strrpos($namespace, '\\') !== false) {
|
||||
$parentNamespace = substr($namespace, 0, strrpos($namespace, '\\'));
|
||||
$suggestedClass = $parentNamespace . '\\' . $suggestedName;
|
||||
if (class_exists($suggestedClass)) {
|
||||
return $suggestedClass;
|
||||
}
|
||||
}
|
||||
|
||||
// Strategie 4: Suche mit vollständigem App\Framework\ Präfix
|
||||
// Für: App\Framework\LiveComponents\Contracts\ComponentRegistryInterface
|
||||
// Suche: App\Framework\LiveComponents\ComponentRegistryInitializer
|
||||
$interfaceParts = explode('\\', $interface);
|
||||
if (count($interfaceParts) >= 3 && $interfaceParts[0] === 'App' && $interfaceParts[1] === 'Framework') {
|
||||
// Entferne 'Contracts' falls vorhanden
|
||||
$filteredParts = array_filter($interfaceParts, fn($part) => $part !== 'Contracts');
|
||||
$filteredParts = array_values($filteredParts);
|
||||
|
||||
// Baue Namespace ohne Interface-Name, aber mit Initializer-Name
|
||||
$baseNamespace = implode('\\', array_slice($filteredParts, 0, -1));
|
||||
$suggestedClass = $baseNamespace . '\\' . $suggestedName;
|
||||
if (class_exists($suggestedClass)) {
|
||||
return $suggestedClass;
|
||||
}
|
||||
}
|
||||
|
||||
// Strategie 5: Fallback - direkter App\Framework\ Präfix
|
||||
$suggestedClassShort = 'App\\Framework\\' . $suggestedName;
|
||||
if (class_exists($suggestedClassShort)) {
|
||||
return $suggestedClassShort;
|
||||
}
|
||||
@@ -386,39 +481,6 @@ final class CyclicDependencyException extends ContainerException
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysiere Dependencies eines Initializers über Reflection
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
private function analyzeInitializerDependencies(string $initializerClass): array
|
||||
{
|
||||
try {
|
||||
if (!class_exists($initializerClass)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$reflection = new \ReflectionClass($initializerClass);
|
||||
$constructor = $reflection->getConstructor();
|
||||
|
||||
if ($constructor === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$dependencies = [];
|
||||
foreach ($constructor->getParameters() as $parameter) {
|
||||
$type = $parameter->getType();
|
||||
if ($type instanceof \ReflectionNamedType && !$type->isBuiltin()) {
|
||||
$dependencies[] = $type->getName();
|
||||
}
|
||||
}
|
||||
|
||||
return $dependencies;
|
||||
} catch (\Throwable) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the detected cycle (without full dependency chain)
|
||||
*/
|
||||
|
||||
226
src/Framework/DI/InitializerDependencyAnalyzer.php
Normal file
226
src/Framework/DI/InitializerDependencyAnalyzer.php
Normal file
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\DI;
|
||||
|
||||
/**
|
||||
* Analysiert Dependencies von Initializern
|
||||
*
|
||||
* Erkennt sowohl Constructor-Dependencies als auch container->get() Aufrufe
|
||||
* und prüft ob Container verfügbar ist, bevor nach container->get() gesucht wird.
|
||||
*/
|
||||
final readonly class InitializerDependencyAnalyzer
|
||||
{
|
||||
/**
|
||||
* Analysiere Dependencies eines Initializers
|
||||
*
|
||||
* @return array{
|
||||
* constructorDeps: array<string>,
|
||||
* containerGetDeps: array<string>,
|
||||
* hasContainerGetCalls: bool,
|
||||
* hasContainerAvailable: bool
|
||||
* }
|
||||
*/
|
||||
public function analyze(string $initializerClass): array
|
||||
{
|
||||
try {
|
||||
if (!class_exists($initializerClass)) {
|
||||
return [
|
||||
'constructorDeps' => [],
|
||||
'containerGetDeps' => [],
|
||||
'hasContainerGetCalls' => false,
|
||||
'hasContainerAvailable' => false,
|
||||
];
|
||||
}
|
||||
|
||||
$reflection = new \ReflectionClass($initializerClass);
|
||||
$constructorDeps = [];
|
||||
|
||||
// 1. Analysiere Constructor-Dependencies
|
||||
$constructor = $reflection->getConstructor();
|
||||
$hasContainerInConstructor = false;
|
||||
|
||||
if ($constructor !== null) {
|
||||
foreach ($constructor->getParameters() as $parameter) {
|
||||
$type = $parameter->getType();
|
||||
if ($type instanceof \ReflectionNamedType && !$type->isBuiltin()) {
|
||||
$typeName = $type->getName();
|
||||
$constructorDeps[] = $typeName;
|
||||
|
||||
// Prüfe ob Container verfügbar ist
|
||||
if ($typeName === Container::class || $typeName === 'App\Framework\DI\Container') {
|
||||
$hasContainerInConstructor = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Analysiere __invoke() Method-Dependencies via Code-Parsing
|
||||
$containerGetDeps = [];
|
||||
$hasContainerGetCalls = false;
|
||||
$hasContainerInInvoke = false;
|
||||
|
||||
try {
|
||||
$invokeMethod = $reflection->getMethod('__invoke');
|
||||
|
||||
// Prüfe ob Container als Parameter in __invoke() verfügbar ist
|
||||
foreach ($invokeMethod->getParameters() as $parameter) {
|
||||
$type = $parameter->getType();
|
||||
if ($type instanceof \ReflectionNamedType && !$type->isBuiltin()) {
|
||||
$typeName = $type->getName();
|
||||
if ($typeName === Container::class || $typeName === 'App\Framework\DI\Container') {
|
||||
$hasContainerInInvoke = true;
|
||||
}
|
||||
|
||||
// Füge auch zu constructorDeps hinzu wenn nicht schon vorhanden
|
||||
if (!in_array($typeName, $constructorDeps, true)) {
|
||||
$constructorDeps[] = $typeName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Nur nach container->get() suchen wenn Container verfügbar ist
|
||||
$hasContainerAvailable = $hasContainerInConstructor || $hasContainerInInvoke;
|
||||
|
||||
if ($hasContainerAvailable) {
|
||||
// Parse Code der __invoke() Methode um container->get() Aufrufe zu finden
|
||||
$parsedDeps = $this->parseContainerGetCalls($invokeMethod);
|
||||
$containerGetDeps = $parsedDeps['dependencies'];
|
||||
$hasContainerGetCalls = $parsedDeps['hasCalls'];
|
||||
}
|
||||
} catch (\ReflectionException) {
|
||||
// __invoke() existiert nicht oder ist nicht analysierbar - das ist okay
|
||||
$hasContainerAvailable = $hasContainerInConstructor;
|
||||
}
|
||||
|
||||
return [
|
||||
'constructorDeps' => $constructorDeps,
|
||||
'containerGetDeps' => $containerGetDeps,
|
||||
'hasContainerGetCalls' => $hasContainerGetCalls,
|
||||
'hasContainerAvailable' => $hasContainerAvailable ?? $hasContainerInConstructor,
|
||||
];
|
||||
} catch (\Throwable) {
|
||||
return [
|
||||
'constructorDeps' => [],
|
||||
'containerGetDeps' => [],
|
||||
'hasContainerGetCalls' => false,
|
||||
'hasContainerAvailable' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse container->get() Aufrufe aus einer Method
|
||||
*
|
||||
* @return array{dependencies: array<string>, hasCalls: bool}
|
||||
*/
|
||||
private function parseContainerGetCalls(\ReflectionMethod $method): array
|
||||
{
|
||||
try {
|
||||
$fileName = $method->getFileName();
|
||||
if ($fileName === false || !file_exists($fileName)) {
|
||||
return ['dependencies' => [], 'hasCalls' => false];
|
||||
}
|
||||
|
||||
$fileContent = file_get_contents($fileName);
|
||||
if ($fileContent === false) {
|
||||
return ['dependencies' => [], 'hasCalls' => false];
|
||||
}
|
||||
|
||||
$startLine = $method->getStartLine();
|
||||
$endLine = $method->getEndLine();
|
||||
|
||||
if ($startLine === false || $endLine === false) {
|
||||
return ['dependencies' => [], 'hasCalls' => false];
|
||||
}
|
||||
|
||||
// Extrahiere nur die Method
|
||||
$lines = explode("\n", $fileContent);
|
||||
$methodLines = array_slice($lines, $startLine - 1, $endLine - $startLine + 1);
|
||||
$methodCode = implode("\n", $methodLines);
|
||||
|
||||
// Finde container->get(...) Aufrufe
|
||||
// Pattern: $container->get(ClassName::class) oder $this->container->get(ClassName::class)
|
||||
// Unterstützt auch ::class Notation
|
||||
$pattern = '/(?:\$container|\$this->container)->get\(([^,\)]+::class|[^,\)]+)\)/';
|
||||
preg_match_all($pattern, $methodCode, $matches);
|
||||
|
||||
$dependencies = [];
|
||||
if (!empty($matches[1])) {
|
||||
foreach ($matches[1] as $match) {
|
||||
$match = trim($match);
|
||||
|
||||
// Entferne ::class falls vorhanden
|
||||
$className = str_replace('::class', '', $match);
|
||||
$className = trim($className, '\'"');
|
||||
|
||||
// Prüfe ob es ein gültiger Klassenname ist (beginnt mit \ oder Namespace)
|
||||
if (preg_match('/^\\\\?[A-Z][A-Za-z0-9\\\\]*$/', $className)) {
|
||||
// Normalisiere (füge \ am Anfang hinzu wenn nicht vorhanden)
|
||||
if (!str_starts_with($className, '\\')) {
|
||||
// Versuche vollständigen Namespace zu finden (z.B. über use statements)
|
||||
$fullClassName = $this->resolveClassNameFromMethod($className, $fileContent, $method);
|
||||
if ($fullClassName !== null) {
|
||||
$dependencies[] = $fullClassName;
|
||||
} else {
|
||||
// Fallback: Verwende wie angegeben
|
||||
$dependencies[] = $className;
|
||||
}
|
||||
} else {
|
||||
$dependencies[] = $className;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Entferne Duplikate
|
||||
$dependencies = array_unique($dependencies);
|
||||
$dependencies = array_values($dependencies);
|
||||
}
|
||||
|
||||
return [
|
||||
'dependencies' => $dependencies,
|
||||
'hasCalls' => !empty($matches[0]),
|
||||
];
|
||||
} catch (\Throwable) {
|
||||
return ['dependencies' => [], 'hasCalls' => false];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Versuche vollständigen Klassenname aus use statements zu resolven
|
||||
*/
|
||||
private function resolveClassNameFromMethod(string $shortName, string $fileContent, \ReflectionMethod $method): ?string
|
||||
{
|
||||
try {
|
||||
// Finde use statements im File
|
||||
preg_match_all('/^use\s+([^;]+);/m', $fileContent, $useMatches);
|
||||
|
||||
// Suche nach exakter Übereinstimmung oder Alias
|
||||
foreach ($useMatches[1] as $useStatement) {
|
||||
$useStatement = trim($useStatement);
|
||||
|
||||
// Prüfe auf Alias (use Full\Class\Name as Alias)
|
||||
if (preg_match('/^(.+?)\s+as\s+(\w+)$/', $useStatement, $aliasMatch)) {
|
||||
$fullClassName = $aliasMatch[1];
|
||||
$alias = $aliasMatch[2];
|
||||
if ($alias === $shortName) {
|
||||
return $fullClassName;
|
||||
}
|
||||
} else {
|
||||
// Prüfe ob letzter Teil übereinstimmt
|
||||
$parts = explode('\\', $useStatement);
|
||||
$lastPart = end($parts);
|
||||
if ($lastPart === $shortName) {
|
||||
return $useStatement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (\Throwable) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user