chore: complete update
This commit is contained in:
270
src/Framework/Core/AttributeMappingVisitor.php
Normal file
270
src/Framework/Core/AttributeMappingVisitor.php
Normal file
@@ -0,0 +1,270 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Core;
|
||||
|
||||
use App\Framework\Cache\Cache;
|
||||
use App\Framework\Discovery\FileVisitor;
|
||||
|
||||
/**
|
||||
* Visitor zum Verarbeiten von Attributen in Klassen
|
||||
*/
|
||||
final class AttributeMappingVisitor implements FileVisitor
|
||||
{
|
||||
private array $attributeMappers = [];
|
||||
private array $mappedAttributes = [];
|
||||
private array $mapperByClass = [];
|
||||
private array $processingStats = [];
|
||||
|
||||
/**
|
||||
* @param array $attributeMappers Array von AttributeMapper-Instanzen
|
||||
*/
|
||||
public function __construct(array $attributeMappers = [])
|
||||
{
|
||||
$this->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
|
||||
error_log("Inkrementelle Attribut-Verarbeitung abgeschlossen: " . json_encode($this->processingStats));
|
||||
}
|
||||
|
||||
public function visitClass(string $className, string $filePath): void
|
||||
{
|
||||
if (!class_exists($className)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->processingStats['total_classes']++;
|
||||
$hasAttributes = false;
|
||||
|
||||
try {
|
||||
$reflection = new \ReflectionClass($className);
|
||||
|
||||
// Schnelle Vorprüfung: Hat die Klasse überhaupt relevante Attribute?
|
||||
$relevantAttributes = $this->hasRelevantAttributes($reflection);
|
||||
if (!$relevantAttributes) {
|
||||
return;
|
||||
}
|
||||
|
||||
$hasAttributes = true;
|
||||
$this->processingStats['classes_with_attributes']++;
|
||||
|
||||
// Verarbeite alle Attribute auf Klassenebene
|
||||
$this->processAttributes($reflection->getAttributes(), $reflection);
|
||||
|
||||
// Verarbeite alle Methoden
|
||||
foreach ($reflection->getMethods() as $method) {
|
||||
// Schnelle Vorprüfung für jede Methode
|
||||
if ($this->hasRelevantAttributes($method)) {
|
||||
$this->processAttributes($method->getAttributes(), $method);
|
||||
}
|
||||
}
|
||||
|
||||
// Verarbeite alle Eigenschaften
|
||||
foreach ($reflection->getProperties() as $property) {
|
||||
// Schnelle Vorprüfung für jede Eigenschaft
|
||||
if ($this->hasRelevantAttributes($property)) {
|
||||
$this->processAttributes($property->getAttributes(), $property);
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Fehler beim Verarbeiten der Klasse protokollieren
|
||||
error_log("Fehler bei der Attributverarbeitung von {$className}: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
error_log("Fehler beim Verarbeiten des Attributs {$attributeClass}: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
error_log("Attribut-Verarbeitung abgeschlossen: " . json_encode($this->processingStats));
|
||||
}
|
||||
|
||||
public function loadFromCache(Cache $cache): void
|
||||
{
|
||||
$cacheItem = $cache->get($this->getCacheKey());
|
||||
if ($cacheItem->isHit) {
|
||||
$this->mappedAttributes = $cacheItem->value;
|
||||
}
|
||||
}
|
||||
|
||||
public function getCacheKey(): string
|
||||
{
|
||||
return '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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user