- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
194 lines
5.8 KiB
PHP
194 lines
5.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\View\Processors;
|
|
|
|
use App\Framework\LiveComponents\Contracts\SupportsSlots;
|
|
use App\Framework\LiveComponents\SlotManager;
|
|
use App\Framework\LiveComponents\ValueObjects\SlotContent;
|
|
use App\Framework\Template\Processing\DomProcessor;
|
|
use App\Framework\View\DomWrapper;
|
|
use App\Framework\View\RenderContext;
|
|
|
|
/**
|
|
* Slot Processor for Template System
|
|
*
|
|
* Processes slot tags in templates and resolves them using the SlotManager.
|
|
*
|
|
* Syntax:
|
|
* - <slot name="header">Default content</slot>
|
|
* - <slot>Default slot content</slot>
|
|
* - <slot name="content" scope="true">Scoped content with {context.key}</slot>
|
|
*
|
|
* Features:
|
|
* - Named slots (header, footer, sidebar, etc.)
|
|
* - Default (unnamed) slots
|
|
* - Scoped slots with context injection
|
|
* - Default content fallbacks
|
|
* - Validation of required slots
|
|
* - Integration with SlotManager and SupportsSlots
|
|
*
|
|
* Example:
|
|
* ```html
|
|
* <component name="card">
|
|
* <slot name="header">Default Header</slot>
|
|
* <slot>Main Content</slot>
|
|
* </component>
|
|
* ```
|
|
*/
|
|
final readonly class SlotProcessor implements DomProcessor
|
|
{
|
|
public function __construct(
|
|
private SlotManager $slotManager
|
|
) {
|
|
}
|
|
|
|
public function process(DomWrapper $dom, RenderContext $context): DomWrapper
|
|
{
|
|
// Check if component supports the new Slot System
|
|
$component = $context->component ?? null;
|
|
|
|
if ($component instanceof SupportsSlots) {
|
|
return $this->processWithSlotSystem($dom, $context, $component);
|
|
}
|
|
|
|
// Fallback to legacy slot processing
|
|
return $this->processLegacySlots($dom, $context);
|
|
}
|
|
|
|
/**
|
|
* Process slots using the new Slot System with SlotManager
|
|
*/
|
|
private function processWithSlotSystem(
|
|
DomWrapper $dom,
|
|
RenderContext $context,
|
|
SupportsSlots $component
|
|
): DomWrapper {
|
|
// Extract provided slots from context
|
|
$providedSlots = $this->extractProvidedSlotsFromContext($context);
|
|
|
|
// Validate slots
|
|
$errors = $this->slotManager->validateSlots($component, $providedSlots);
|
|
if (! empty($errors)) {
|
|
$this->handleValidationErrors($errors, $context);
|
|
}
|
|
|
|
// Process each slot element in the template
|
|
foreach ($dom->querySelectorAll('slot') as $slotNode) {
|
|
$slotName = $slotNode->getAttribute('name') ?: 'default';
|
|
|
|
// Find slot definition
|
|
$definition = $this->slotManager->getSlotDefinition($component, $slotName);
|
|
|
|
if ($definition === null) {
|
|
// Unknown slot, keep default content
|
|
continue;
|
|
}
|
|
|
|
// Resolve slot content using SlotManager
|
|
$resolvedContent = $this->slotManager->resolveSlotContent(
|
|
component: $component,
|
|
definition: $definition,
|
|
providedContents: $providedSlots
|
|
);
|
|
|
|
// Replace slot node with resolved content
|
|
$this->replaceSlotNode($dom, $slotNode, $resolvedContent);
|
|
}
|
|
|
|
return $dom;
|
|
}
|
|
|
|
/**
|
|
* Legacy slot processing for backward compatibility
|
|
*/
|
|
private function processLegacySlots(DomWrapper $dom, RenderContext $context): DomWrapper
|
|
{
|
|
foreach ($dom->querySelectorAll('slot[name]') as $slotNode) {
|
|
$slotName = $slotNode->getAttribute('name');
|
|
$html = $context->slots[$slotName] ?? null;
|
|
|
|
$replacement = $dom->createDocumentFragment();
|
|
|
|
if ($html !== null) {
|
|
@$replacement->appendXML($html);
|
|
} else {
|
|
// Fallback-Inhalt erhalten (die inneren Nodes des slot-Tags)
|
|
foreach ($slotNode->childNodes as $child) {
|
|
$replacement->appendChild($child->cloneNode(true));
|
|
}
|
|
}
|
|
|
|
$slotNode->parentNode?->replaceChild($replacement, $slotNode);
|
|
}
|
|
|
|
return $dom;
|
|
}
|
|
|
|
/**
|
|
* Extract SlotContent objects from RenderContext
|
|
*
|
|
* @return array<SlotContent>
|
|
*/
|
|
private function extractProvidedSlotsFromContext(RenderContext $context): array
|
|
{
|
|
$slots = [];
|
|
|
|
// Check if context has slot data (legacy format)
|
|
if (isset($context->slots) && is_array($context->slots)) {
|
|
foreach ($context->slots as $name => $content) {
|
|
$slots[] = SlotContent::named(
|
|
slotName: $name,
|
|
content: $content
|
|
);
|
|
}
|
|
}
|
|
|
|
// Check if context has SlotContent objects (new format)
|
|
if (isset($context->slotContents) && is_array($context->slotContents)) {
|
|
foreach ($context->slotContents as $slotContent) {
|
|
if ($slotContent instanceof SlotContent) {
|
|
$slots[] = $slotContent;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $slots;
|
|
}
|
|
|
|
/**
|
|
* Replace slot node with resolved content
|
|
*/
|
|
private function replaceSlotNode(DomWrapper $dom, \DOMElement $slotNode, string $content): void
|
|
{
|
|
$replacement = $dom->createDocumentFragment();
|
|
|
|
if (! empty($content)) {
|
|
@$replacement->appendXML($content);
|
|
}
|
|
|
|
$slotNode->parentNode?->replaceChild($replacement, $slotNode);
|
|
}
|
|
|
|
/**
|
|
* Handle slot validation errors
|
|
*/
|
|
private function handleValidationErrors(array $errors, RenderContext $context): void
|
|
{
|
|
$errorMessage = "Slot validation failed:\n" . implode("\n", $errors);
|
|
|
|
// In development, we might want to show errors
|
|
// In production, log them
|
|
$isDevelopment = ($context->environment ?? 'production') === 'development';
|
|
|
|
if ($isDevelopment) {
|
|
// Store errors in context for debugging
|
|
$context->slotValidationErrors = $errors;
|
|
}
|
|
|
|
// Always log errors
|
|
error_log($errorMessage);
|
|
}
|
|
}
|