depth}, isRoot=" . ($hierarchy->isRoot() ? 'true' : 'false') . "\n"; $parentId = ComponentId::fromString('todo-list:main'); $childId = ComponentId::fromString('todo-item:1'); $childHierarchy = ComponentHierarchy::fromParent($parentId, $childId); echo "โœ“ Created child hierarchy: depth={$childHierarchy->depth}, isChild=" . ($childHierarchy->isChild() ? 'true' : 'false') . "\n"; echo "โœ“ Child has parent: " . ($childHierarchy->parentId !== null ? 'true' : 'false') . "\n"; echo "โœ“ Path length: " . count($childHierarchy->path) . "\n\n"; // Test 2: Nested Component Manager echo "Test 2: Nested Component Manager\n"; echo str_repeat('-', 70) . "\n"; $nestedManager = new NestedComponentManager(); // Register parent (root) $parentId = ComponentId::fromString('todo-list:main'); $nestedManager->registerHierarchy($parentId, ComponentHierarchy::root()); echo "โœ“ Registered parent component: {$parentId->toString()}\n"; // Register children $childIds = [ ComponentId::fromString('todo-item:1'), ComponentId::fromString('todo-item:2'), ComponentId::fromString('todo-item:3'), ]; foreach ($childIds as $childId) { $hierarchy = ComponentHierarchy::fromParent($parentId, $childId); $nestedManager->registerHierarchy($childId, $hierarchy); echo "โœ“ Registered child component: {$childId->toString()}\n"; } // Verify hierarchy queries echo "\nHierarchy Queries:\n"; echo " Parent has children: " . ($nestedManager->hasChildren($parentId) ? 'true' : 'false') . "\n"; echo " Number of children: " . count($nestedManager->getChildIds($parentId)) . "\n"; echo " First child's parent: " . $nestedManager->getParentId($childIds[0])?->toString() . "\n"; echo " Parent is root: " . ($nestedManager->isRoot($parentId) ? 'true' : 'false') . "\n"; echo " Child is root: " . ($nestedManager->isRoot($childIds[0]) ? 'true' : 'false') . "\n"; // Statistics $stats = $nestedManager->getStats(); echo "\nHierarchy Statistics:\n"; echo " Total components: {$stats['total_components']}\n"; echo " Root components: {$stats['root_components']}\n"; echo " Child components: {$stats['child_components']}\n"; echo " Max nesting depth: {$stats['max_nesting_depth']}\n"; echo " Parents with children: {$stats['parent_components_with_children']}\n\n"; // Test 3: TodoList Component with Children echo "Test 3: TodoList Component with Children\n"; echo str_repeat('-', 70) . "\n"; $todoListId = ComponentId::fromString('todo-list:demo'); $initialTodos = [ [ 'id' => 'todo_1', 'title' => 'Learn Nested Components', 'completed' => false, 'created_at' => time(), ], [ 'id' => 'todo_2', 'title' => 'Implement Event Bubbling', 'completed' => true, 'created_at' => time() - 3600, ], [ 'id' => 'todo_3', 'title' => 'Write Integration Tests', 'completed' => false, 'created_at' => time() - 7200, ], ]; $todoList = new TodoListComponent( id: $todoListId, initialData: null, todos: $initialTodos, filter: 'all' ); echo "โœ“ Created TodoList component: {$todoListId->toString()}\n"; echo " Initial todos count: " . count($initialTodos) . "\n"; // Get child component IDs $childComponents = $todoList->getChildComponents(); echo " Child components declared: " . count($childComponents) . "\n"; foreach ($childComponents as $childIdString) { echo " - {$childIdString}\n"; } // Test child compatibility $validChild = ComponentId::fromString('todo-item:test'); $invalidChild = ComponentId::fromString('other-component:test'); echo "\n Child compatibility tests:\n"; echo " todo-item:test can be child: " . ($todoList->canHaveChild($validChild) ? 'true' : 'false') . "\n"; echo " other-component:test can be child: " . ($todoList->canHaveChild($invalidChild) ? 'true' : 'false') . "\n"; // Test Actions echo "\n Testing Actions:\n"; // Add Todo $newData = $todoList->addTodo('New Todo Item'); $newDataArray = $newData->toArray(); echo " โœ“ Added todo, new count: " . count($newDataArray['todos']) . "\n"; // Set Filter $filteredData = $todoList->setFilter('active'); echo " โœ“ Changed filter to 'active'\n"; // Clear Completed $clearedData = $todoList->clearCompleted(); echo " โœ“ Cleared completed todos\n\n"; // Test 4: TodoItem Component echo "Test 4: TodoItem Component\n"; echo str_repeat('-', 70) . "\n"; $todoItemId = ComponentId::fromString('todo-item:demo_1'); $todoData = [ 'id' => 'demo_1', 'title' => 'Test TodoItem', 'completed' => false, 'created_at' => time(), ]; // Create event dispatcher $eventDispatcher = new NestedComponentEventDispatcher(); $todoItem = new TodoItemComponent( id: $todoItemId, eventDispatcher: $eventDispatcher, initialData: null, todoData: $todoData ); echo "โœ“ Created TodoItem component: {$todoItemId->toString()}\n"; echo " Title: " . $todoData['title'] . "\n"; echo " Completed: " . ($todoData['completed'] ? 'true' : 'false') . "\n\n"; echo " Testing TodoItem Actions:\n"; // Toggle $eventDispatcher->clear(); $todoItem->toggle(); echo " โœ“ Toggled todo\n"; echo " Events dispatched: " . $eventDispatcher->count() . "\n"; if ($eventDispatcher->hasEvents()) { foreach ($eventDispatcher->getEvents() as $event) { echo " ๐Ÿ“ก {$event['event_name']} from {$event['component_id']}\n"; } } echo "\n"; // Start Edit $editData = $todoItem->startEdit(); echo " โœ“ Started edit mode\n"; // Save Edit $eventDispatcher->clear(); $savedData = $todoItem->saveEdit('Updated Title'); echo " โœ“ Saved edit\n"; echo " Events dispatched: " . $eventDispatcher->count() . "\n"; if ($eventDispatcher->hasEvents()) { foreach ($eventDispatcher->getEvents() as $event) { echo " ๐Ÿ“ก {$event['event_name']} from {$event['component_id']}\n"; } } echo "\n"; // Delete $eventDispatcher->clear(); $todoItem->delete(); echo " โœ“ Deleted todo\n"; echo " Events dispatched: " . $eventDispatcher->count() . "\n"; if ($eventDispatcher->hasEvents()) { foreach ($eventDispatcher->getEvents() as $event) { echo " ๐Ÿ“ก {$event['event_name']} from {$event['component_id']}\n"; } } echo "\n"; // Test 5: Event Bubbling Simulation echo "Test 5: Event Bubbling Simulation\n"; echo str_repeat('-', 70) . "\n"; // Create parent with event handling $parentId = ComponentId::fromString('todo-list:bubble-test'); $parent = new TodoListComponent( id: $parentId, initialData: null, todos: [ ['id' => 'child_1', 'title' => 'First Todo', 'completed' => false, 'created_at' => time()], ] ); $childId = ComponentId::fromString('todo-item:child_1'); // Test different events $events = [ ['todo-completed', ['todo_id' => 'child_1', 'completed' => true]], ['todo-deleted', ['todo_id' => 'child_1']], ['todo-title-changed', ['todo_id' => 'child_1', 'title' => 'Updated Title']], ]; echo "Testing event bubbling (check error_log for details):\n"; foreach ($events as [$eventName, $payload]) { $shouldContinue = $parent->onChildEvent($childId, $eventName, $payload); echo " โœ“ {$eventName}: bubbling " . ($shouldContinue ? 'continues' : 'stopped') . "\n"; } echo "\n"; // Test 6: Circular Dependency Detection echo "Test 6: Circular Dependency Detection\n"; echo str_repeat('-', 70) . "\n"; try { $selfId = ComponentId::fromString('component:self'); $circularHierarchy = ComponentHierarchy::fromParent($selfId, $selfId); $manager = new NestedComponentManager(); $manager->registerHierarchy($selfId, $circularHierarchy); echo "โŒ Should have thrown exception for circular dependency\n"; } catch (\InvalidArgumentException $e) { echo "โœ“ Circular dependency correctly detected and prevented\n"; echo " Error message: {$e->getMessage()}\n"; } echo "\n"; // Summary echo str_repeat('=', 70) . "\n"; echo "โœ… All Nested Components Tests Completed!\n\n"; echo "Summary:\n"; echo " โœ“ Component Hierarchy Value Objects\n"; echo " โœ“ Nested Component Manager\n"; echo " โœ“ TodoList Parent Component\n"; echo " โœ“ TodoItem Child Component\n"; echo " โœ“ Event Dispatching\n"; echo " โœ“ Event Bubbling\n"; echo " โœ“ Circular Dependency Protection\n\n"; echo "Next Steps:\n"; echo " - Integrate with LiveComponentHandler\n"; echo " - Test in browser with real DOM\n"; echo " - Add client-side event bubbling tests\n"; echo " - Implement Slot System for flexible composition\n";