attributeMappers = $attributeMappers; $this->buildMapperIndex(); } /** * Erstellt einen Index der Mapper nach Attributklasse für schnelleren Zugriff */ private function buildMapperIndex(): void { $this->mapperByClass = []; foreach ($this->attributeMappers as $mapper) { $attributeClass = $mapper->getAttributeClass(); $this->mapperByClass[$attributeClass] = $mapper; } } public function onScanStart(): void { $this->mappedAttributes = []; $this->processingStats = [ 'total_classes' => 0, 'classes_with_attributes' => 0, 'total_attributes' => 0, 'processed_attributes' => 0, 'skipped_reflectors' => 0, ]; } public function onIncrementalScanStart(): void { // Bei inkrementellem Scan behalten wir die vorhandenen Mappings bei $this->processingStats = [ 'total_classes' => 0, 'classes_with_attributes' => 0, 'total_attributes' => 0, 'processed_attributes' => 0, 'skipped_reflectors' => 0, ]; } public function onIncrementalScanComplete(): void { // Logging der Verarbeitungsstatistiken für inkrementellen Scan // Processing stats removed for production } public function visitClass(ClassName $className, FilePath $filePath): void { // This method is kept for backward compatibility but should not be used // when ReflectionProvider is available. The UnifiedDiscoveryService will // prefer visitClassWithReflection() when possible. } public function visitClassWithReflection(ClassName $className, FilePath $filePath, WrappedReflectionClass $reflection): void { $this->processingStats['total_classes']++; $hasAttributes = false; try { // Schnelle Vorprüfung: Hat die Klasse überhaupt relevante Attribute? $relevantAttributes = $this->hasRelevantAttributesWrapped($reflection); if (! $relevantAttributes) { return; } $hasAttributes = true; $this->processingStats['classes_with_attributes']++; // Verarbeite alle Attribute auf Klassenebene $this->processAttributesWrapped($reflection->getAttributes(), $reflection); // Verarbeite alle Methoden $methods = $reflection->getMethods(); foreach ($methods as $method) { // Schnelle Vorprüfung für jede Methode if ($this->hasRelevantAttributesWrappedMethod($method)) { $this->processAttributesWrappedMethod($method->getAttributes(), $method); } } // Verarbeite alle Eigenschaften $properties = $reflection->getProperties(); foreach ($properties as $property) { // Schnelle Vorprüfung für jede Eigenschaft if ($this->hasRelevantAttributesWrappedProperty($property)) { $this->processAttributesWrappedProperty($property->getAttributes(), $property); } } } catch (\Throwable $e) { // Fehler beim Verarbeiten der Klasse protokollieren // Silent failure - skip this class and continue } } /** * Schnelle Vorprüfung, ob eine WrappedReflectionClass relevante Attribute hat */ private function hasRelevantAttributesWrapped(WrappedReflectionClass $reflection): bool { $attributes = $reflection->getAttributes(); $this->processingStats['total_attributes'] += $attributes->count(); if ($attributes->isEmpty()) { return false; } foreach ($attributes as $attribute) { $attributeClass = $attribute->getName(); if (isset($this->mapperByClass[$attributeClass])) { $mapper = $this->mapperByClass[$attributeClass]; // For wrapped reflection, we need to create native reflection for mapper compatibility try { $nativeReflection = new \ReflectionClass($reflection->getName()); if ($mapper->canProcess($nativeReflection)) { return true; } else { $this->processingStats['skipped_reflectors']++; } } catch (\Throwable) { $this->processingStats['skipped_reflectors']++; } } } return false; } /** * Schnelle Vorprüfung für WrappedReflectionMethod */ private function hasRelevantAttributesWrappedMethod($method): bool { $attributes = $method->getAttributes(); $this->processingStats['total_attributes'] += $attributes->count(); if ($attributes->isEmpty()) { return false; } foreach ($attributes as $attribute) { $attributeClass = $attribute->getName(); if (isset($this->mapperByClass[$attributeClass])) { $mapper = $this->mapperByClass[$attributeClass]; // For wrapped reflection, we need to create native reflection for mapper compatibility try { $nativeClass = new \ReflectionClass($method->getDeclaringClass()->getName()); $nativeMethod = $nativeClass->getMethod($method->getName()); if ($mapper->canProcess($nativeMethod)) { return true; } else { $this->processingStats['skipped_reflectors']++; } } catch (\Throwable) { $this->processingStats['skipped_reflectors']++; } } } return false; } /** * Schnelle Vorprüfung für WrappedReflectionProperty */ private function hasRelevantAttributesWrappedProperty($property): bool { $attributes = $property->getAttributes(); $this->processingStats['total_attributes'] += $attributes->count(); if ($attributes->isEmpty()) { return false; } foreach ($attributes as $attribute) { $attributeClass = $attribute->getName(); if (isset($this->mapperByClass[$attributeClass])) { $mapper = $this->mapperByClass[$attributeClass]; // For wrapped reflection, we need to create native reflection for mapper compatibility try { $nativeClass = new \ReflectionClass($property->getDeclaringClass()->getName()); $nativeProperty = $nativeClass->getProperty($property->getName()); if ($mapper->canProcess($nativeProperty)) { return true; } else { $this->processingStats['skipped_reflectors']++; } } catch (\Throwable) { $this->processingStats['skipped_reflectors']++; } } } return false; } /** * Verarbeitet Attribute einer WrappedReflectionClass */ private function processAttributesWrapped($attributes, WrappedReflectionClass $reflection): void { foreach ($attributes as $attribute) { $attributeClass = $attribute->getName(); if (isset($this->mapperByClass[$attributeClass])) { $mapper = $this->mapperByClass[$attributeClass]; try { $nativeReflection = new \ReflectionClass($reflection->getName()); if ($mapper->canProcess($nativeReflection)) { $attributeInstance = $attribute->newInstance(); $mapper->map($attributeInstance, $nativeReflection); $this->processingStats['processed_attributes']++; if (! isset($this->mappedAttributes[$attributeClass])) { $this->mappedAttributes[$attributeClass] = []; } $reflectorKey = [ 'type' => 'class', 'name' => $reflection->getName(), ]; $this->mappedAttributes[$attributeClass][] = [ 'attribute' => $attributeInstance, 'reflector' => $reflectorKey, 'metadata' => $mapper->getAttributeMetadata(), ]; } } catch (\Throwable $e) { // Silent failure - skip this attribute and continue } } } } /** * Verarbeitet Attribute einer WrappedReflectionMethod */ private function processAttributesWrappedMethod($attributes, $method): void { foreach ($attributes as $attribute) { $attributeClass = $attribute->getName(); if (isset($this->mapperByClass[$attributeClass])) { $mapper = $this->mapperByClass[$attributeClass]; try { $nativeClass = new \ReflectionClass($method->getDeclaringClass()->getName()); $nativeMethod = $nativeClass->getMethod($method->getName()); if ($mapper->canProcess($nativeMethod)) { $attributeInstance = $attribute->newInstance(); $mapper->map($attributeInstance, $nativeMethod); $this->processingStats['processed_attributes']++; if (! isset($this->mappedAttributes[$attributeClass])) { $this->mappedAttributes[$attributeClass] = []; } $reflectorKey = [ 'type' => 'method', 'class' => $method->getDeclaringClass()->getName(), 'name' => $method->getName(), ]; $this->mappedAttributes[$attributeClass][] = [ 'attribute' => $attributeInstance, 'reflector' => $reflectorKey, 'metadata' => $mapper->getAttributeMetadata(), ]; } } catch (\Throwable $e) { // Silent failure - skip this attribute and continue } } } } /** * Verarbeitet Attribute einer WrappedReflectionProperty */ private function processAttributesWrappedProperty($attributes, $property): void { foreach ($attributes as $attribute) { $attributeClass = $attribute->getName(); if (isset($this->mapperByClass[$attributeClass])) { $mapper = $this->mapperByClass[$attributeClass]; try { $nativeClass = new \ReflectionClass($property->getDeclaringClass()->getName()); $nativeProperty = $nativeClass->getProperty($property->getName()); if ($mapper->canProcess($nativeProperty)) { $attributeInstance = $attribute->newInstance(); $mapper->map($attributeInstance, $nativeProperty); $this->processingStats['processed_attributes']++; if (! isset($this->mappedAttributes[$attributeClass])) { $this->mappedAttributes[$attributeClass] = []; } $reflectorKey = [ 'type' => 'property', 'class' => $property->getDeclaringClass()->getName(), 'name' => $property->getName(), ]; $this->mappedAttributes[$attributeClass][] = [ 'attribute' => $attributeInstance, 'reflector' => $reflectorKey, 'metadata' => $mapper->getAttributeMetadata(), ]; } } catch (\Throwable $e) { // Silent failure - skip this attribute and continue } } } } /** * Schnelle Vorprüfung, ob ein Reflector relevante Attribute hat */ private function hasRelevantAttributes(\Reflector $reflector): bool { if (method_exists($reflector, 'getAttributes')) { $attributes = $reflector->getAttributes(); $this->processingStats['total_attributes'] += count($attributes); if (empty($attributes)) { return false; } foreach ($attributes as $attribute) { $attributeClass = $attribute->getName(); if (isset($this->mapperByClass[$attributeClass])) { $mapper = $this->mapperByClass[$attributeClass]; if ($mapper->canProcess($reflector)) { return true; } else { $this->processingStats['skipped_reflectors']++; } } } } return false; } private function processAttributes(array $attributes, \Reflector $reflector): void { foreach ($attributes as $attribute) { $attributeClass = $attribute->getName(); // Direkter Zugriff auf den Mapper über den Index if (isset($this->mapperByClass[$attributeClass])) { $mapper = $this->mapperByClass[$attributeClass]; // Zusätzliche Prüfung, ob der Mapper diesen Reflector verarbeiten kann if ($mapper->canProcess($reflector)) { try { $attributeInstance = $attribute->newInstance(); $mapper->map($attributeInstance, $reflector); $this->processingStats['processed_attributes']++; // Speichere für Caching if (! isset($this->mappedAttributes[$attributeClass])) { $this->mappedAttributes[$attributeClass] = []; } // Serialisierbare Repräsentation des Reflectors $reflectorKey = $this->serializeReflector($reflector); $this->mappedAttributes[$attributeClass][] = [ 'attribute' => $attributeInstance, 'reflector' => $reflectorKey, 'metadata' => $mapper->getAttributeMetadata(), ]; } catch (\Throwable $e) { // Silent failure - skip this attribute and continue } } } } } /** * Erstellt eine serialisierbare Repräsentation eines Reflectors */ private function serializeReflector(\Reflector $reflector): array { if ($reflector instanceof \ReflectionClass) { return [ 'type' => 'class', 'name' => $reflector->getName(), ]; } elseif ($reflector instanceof \ReflectionMethod) { return [ 'type' => 'method', 'class' => $reflector->getDeclaringClass()->getName(), 'name' => $reflector->getName(), ]; } elseif ($reflector instanceof \ReflectionProperty) { return [ 'type' => 'property', 'class' => $reflector->getDeclaringClass()->getName(), 'name' => $reflector->getName(), ]; } return [ 'type' => 'unknown', 'name' => method_exists($reflector, 'getName') ? $reflector->getName() : 'unknown', ]; } public function onScanComplete(): void { // Logging der Verarbeitungsstatistiken // Processing completed - stats removed for production } public function loadFromCache(Cache $cache): void { $cacheItem = $cache->get(CacheKey::fromString($this->getCacheKey())); if ($cacheItem->isHit) { $this->mappedAttributes = $cacheItem->value; } } public function getCacheKey(): CacheKey { return CacheKey::fromString('attribute_mappings'); } public function getCacheableData(): mixed { return $this->mappedAttributes; } /** * Fügt einen weiteren AttributeMapper hinzu */ public function addAttributeMapper(AttributeMapper $mapper): void { $this->attributeMappers[] = $mapper; // Aktualisiere den Mapper-Index $attributeClass = $mapper->getAttributeClass(); $this->mapperByClass[$attributeClass] = $mapper; } /** * Gibt alle Attribute eines bestimmten Typs zurück */ public function getAttributesOfType(string $attributeClass): array { return $this->mappedAttributes[$attributeClass] ?? []; } /** * Gibt alle gemappten Attribute zurück */ public function getAllMappedAttributes(): array { return $this->mappedAttributes; } /** * Gibt die Verarbeitungsstatistiken zurück */ public function getProcessingStats(): array { return $this->processingStats; } }