# XComponentProcessor - Unified Component Syntax **Einheitliche `` Syntax für LiveComponents und HTML Components im Custom PHP Framework.** ## Übersicht Der `XComponentProcessor` ist ein DOM-Processor, der eine einheitliche Syntax für **zwei verschiedene Component-Typen** bietet: 1. **LiveComponents** (Interaktiv/Stateful) - Komponenten mit #[LiveComponent] Attribut 2. **HTML Components** (Statisch) - Komponenten mit #[ComponentName] Attribut **Unified Syntax**: `` ```html Click me ``` ## Architektur ### Component Auto-Detection Flow ``` Syntax ↓ XComponentProcessor ↓ ┌─────┴─────┐ ↓ ↓ LiveComponent? HTML Component? ↓ ↓ YES NO ↓ ↓ Process as Process as Interactive Static ``` **Detection Priority**: 1. **LiveComponent** prüfen (via `ComponentRegistry::isRegistered()`) 2. **HTML Component** prüfen (via `HtmlComponentRegistry::has()`) 3. **Not Found** → Helpful error message ### Klassen-Struktur ```php final readonly class XComponentProcessor implements DomProcessor { public function __construct( private ComponentRegistry $liveComponentRegistry, private HtmlComponentRegistry $htmlComponentRegistry, private ComponentMetadataCache $metadataCache, private DomComponentService $componentService ) {} public function process(DomWrapper $dom, RenderContext $context): DomWrapper { // Find all elements $xComponents = $this->findXComponents($dom); foreach ($xComponents as $element) { try { $this->processXComponent($dom, $element, $context); } catch (\Throwable $e) { $this->handleProcessingError($dom, $element, $e); } } return $dom; } } ``` ## LiveComponents (Interactive) ### Verwendung ```html ``` ### LiveComponent Definition ```php use App\Framework\LiveComponents\Attributes\LiveComponent; use App\Framework\LiveComponents\Attributes\LiveProp; #[LiveComponent(name: 'datatable')] final class DataTableComponent { #[LiveProp] public int $page = 1; #[LiveProp] public int $pageSize = 25; #[LiveProp] public string $sortBy = 'id'; #[LiveProp] public string $sortOrder = 'asc'; public function render(): RenderData { $data = $this->loadData(); return new RenderData( template: 'components/datatable', data: [ 'rows' => $data, 'currentPage' => $this->page, 'totalPages' => $this->calculateTotalPages() ] ); } } ``` ### Processing Flow ``` ↓ 1. Extract component name: "datatable" 2. Check ComponentRegistry: isRegistered("datatable") → true 3. Extract props: ["id" => "users", "page" => "1", "pageSize" => "25"] 4. Type coercion: ["id" => "users", "page" => 1, "pageSize" => 25] 5. Validate props gegen ComponentMetadata 6. Create ComponentId: ComponentId::create("datatable", "users") 7. Create ComponentData: ComponentData::fromArray(["page" => 1, "pageSize" => 25]) 8. Resolve component: $registry->resolve($componentId, $initialState) 9. Render with wrapper: $registry->renderWithWrapper($component) 10. Replace with rendered HTML ↓
``` ### Type Coercion (LiveComponents only) Der XComponentProcessor führt automatische **Type Coercion** für LiveComponent-Props durch: ```php // Input: HTML Attributes (always strings) // Output: Coerced PHP types [ 'stringProp' => 'text', // string 'intProp' => 123, // int 'floatProp' => 12.5, // float 'boolTrue' => true, // bool 'boolFalse' => false, // bool 'nullProp' => null, // null 'arrayProp' => [1, 2, 3], // array 'objectProp' => ['key' => 'value'] // array ] ``` **Coercion Rules**: - `"true"` / `"false"` → `bool` - `"null"` → `null` - `"123"` → `int` - `"12.5"` → `float` - `"[...]"` / `"{...}"` → JSON decode - Alles andere → `string` ### Prop Validation ```php // Component Definition #[LiveComponent(name: 'counter')] final class CounterComponent { #[LiveProp] public int $initialValue = 0; #[LiveProp] public int $step = 1; } // ✅ Valid Usage // ❌ Invalid: Unknown prop 'invalidProp' // → Development: Error message // → Production: Silent removal // ❌ Invalid: Wrong type (caught at component level) // → Type coercion to string, component validation throws error ``` **Validation Error (Development Mode)**: ```html
XComponentProcessor Error:
LiveComponent 'counter' has no property 'invalidProp'.
Available properties: initialValue, step
Component: x-counter
``` ## HTML Components (Static) ### Verwendung ```html Click me

User information goes here

New ``` ### HTML Component Definition ```php use App\Framework\View\Attributes\ComponentName; #[ComponentName('button')] final readonly class ButtonComponent { public function render(string $content, array $attributes): string { $variant = $attributes['variant'] ?? 'default'; $class = "btn btn-{$variant}"; return ""; } } ``` ### Processing Flow ``` Click me ↓ 1. Extract component name: "button" 2. Check ComponentRegistry: isRegistered("button") → false 3. Check HtmlComponentRegistry: has("button") → true 4. Extract content: "Click me" 5. Extract attributes: ["variant" => "primary"] (as strings) 6. Render: $registry->render("button", "Click me", ["variant" => "primary"]) 7. Replace with rendered HTML ↓ ``` ### Unterschiede zu LiveComponents **HTML Components**: - ❌ **Keine Type Coercion** - alle Attribute bleiben strings - ❌ **Keine Prop Validation** - Component entscheidet selbst - ❌ **Kein State Management** - rein statisches Rendering - ❌ **Keine ComponentId/ComponentData** - nur template rendering - ✅ **Schneller** - keine Metadata-Prüfung, kein State-Handling - ✅ **Einfacher** - nur `render(content, attributes)` Method **LiveComponents**: - ✅ **Type Coercion** für alle Props - ✅ **Prop Validation** gegen Metadata - ✅ **State Management** mit ComponentData - ✅ **Interactive** mit AJAX/WebSocket updates - ⚠️ **Komplexer** - mehr Overhead für Metadata/State ## Component Registration ### LiveComponent Registration ```php // Automatisch via #[LiveComponent] Attribute Discovery use App\Framework\LiveComponents\Attributes\LiveComponent; #[LiveComponent(name: 'datatable')] final class DataTableComponent implements LiveComponentContract { // Component implementation } // Framework registriert automatisch bei Bootstrap: // ComponentRegistry::register('datatable', DataTableComponent::class) ``` ### HTML Component Registration ```php // Automatisch via #[ComponentName] Attribute Discovery use App\Framework\View\Attributes\ComponentName; #[ComponentName('button')] final readonly class ButtonComponent { public function render(string $content, array $attributes): string { // Rendering logic } } // Framework registriert automatisch: // HtmlComponentRegistry::register('button', ButtonComponent::class) ``` ## Error Handling ### Component Not Found ```html ``` **Development Mode**: ```html
XComponentProcessor Error:
Unknown component: 

Available LiveComponents: datatable, counter, chart
Available HTML Components: button, card, badge
Component: x-unknown-component
``` **Production Mode**: - Element wird **silently removed** - Kein Error-HTML im Output - Optional: Logging via `error_log()` ### Invalid Props (LiveComponents) ```html ``` **Development Mode**: ```html
XComponentProcessor Error:
LiveComponent 'counter' has no property 'invalidProp'.
Available properties: initialValue, step
Component: x-counter
``` **Production Mode**: - Component wird **nicht gerendert** - Silent removal - Optional: Logging ### Rendering Errors Wenn `ComponentRegistry::renderWithWrapper()` oder `HtmlComponentRegistry::render()` fehlschlägt: **Development Mode**: Error message with stack trace **Production Mode**: Silent removal + logging ## Auto-Detection Priority **Wenn Component in BEIDEN Registries existiert**: ``` LiveComponent Priority > HTML Component ``` **Beispiel**: ```php // BOTH registered: #[LiveComponent(name: 'button')] class ButtonLiveComponent { ... } #[ComponentName('button')] class ButtonHtmlComponent { ... } // Usage: // → Processed as LiveComponent (Priority!) ``` **Best Practice**: Verwende unterschiedliche Namen für LiveComponents und HTML Components, um Verwirrung zu vermeiden. ## Performance Characteristics ### LiveComponent Processing - **Prop Extraction**: O(n) wo n = Anzahl Attribute - **Type Coercion**: O(n) wo n = Anzahl Props - **Prop Validation**: O(n) wo n = Anzahl Props (Metadata lookup cached) - **Component Resolution**: Container DI Resolution - **Rendering**: Depends on component logic **Durchschnittliche Latenz**: ~5-15ms pro LiveComponent ### HTML Component Processing - **Attribute Extraction**: O(n) wo n = Anzahl Attribute - **Content Extraction**: O(1) - **Rendering**: Component-spezifische Logic **Durchschnittliche Latenz**: ~1-5ms pro HTML Component ### Optimization Strategies ```php // 1. ComponentMetadataCache nutzen (automatisch) $metadata = $this->metadataCache->get($className); // 2. Registry Lookups cachen (automatisch) if ($this->liveComponentRegistry->isRegistered($name)) { ... } // 3. Batch-Processing für multiple Components foreach ($xComponents as $element) { $this->processXComponent($dom, $element, $context); } ``` ## Framework Integration ### DomProcessingPipeline Integration ```php // In TemplateRendererInitializer $domProcessors = [ ForProcessor::class, // Priority 100 IfProcessor::class, // Priority 200 XComponentProcessor::class, // Priority 300 ← Here ComponentProcessor::class, // Priority 400 (legacy ) IncludeProcessor::class, // Priority 500 // ... ]; ``` **Processing Order**: 1. `` loops werden zuerst verarbeitet 2. `` conditionals danach 3. **`` Components** werden resolved 4. Legacy `` tags (fallback) 5. `` templates einbinden ### RenderContext Integration ```php $context = RenderContext::create([ 'users' => $users, 'currentPage' => $page ]); // XComponentProcessor hat Zugriff auf $context->data // Aber: Props werden via Attribute übergeben, nicht via Context ``` ## Testing ### Unit Tests Der XComponentProcessor hat **umfassende Test-Coverage**: ```php describe('XComponentProcessor', function () { describe('LiveComponent Processing', function () { it('processes LiveComponent with basic props'); it('coerces prop types correctly'); it('validates props against ComponentMetadata'); it('generates unique ID if not provided'); }); describe('HTML Component Processing', function () { it('processes HTML Component'); it('extracts content from HTML Component'); }); describe('Error Handling', function () { it('shows helpful error when component not found'); it('removes component silently in production'); }); describe('Auto-Detection Priority', function () { it('prioritizes LiveComponent over HTML Component'); }); }); ``` **Test File**: `tests/Unit/Framework/View/Processors/XComponentProcessorTest.php` ### Integration Testing ```php // Full Template Processing mit XComponentProcessor $template = <<

Dashboard

Add User HTML; $context = RenderContext::create([]); $rendered = $this->templateProcessor->render($context, $template); // Assert: LiveComponent rendered with data-component-id expect($rendered)->toContain('data-component-id="datatable:users"'); // Assert: HTML Component rendered as button expect($rendered)->toContain('