Architecture
Virtual DOM Tree
All nodes implement the Node
interface:
Node (interface)
├── TextNode — escaped text content
├── RawNode — raw HTML (bypasses escaping)
├── FragmentNode — groups children, no HTML tag
└── *Node (generated) — typed HTML elements (~100 classes)
├── attributes: NodeAttributes — typed element attributes
└── children(): ?NodeCollection — child nodes (null for void elements)
Each generated element node (e.g. DivNode
, InputNode
) is a readonly class
with a corresponding attributes class (DivAttributes
, InputAttributes
).
The NodeName
enum lists all HTML tag names and is used by renderers to
produce the correct opening/closing tags.
Code Generation Pipeline
Domovoy parses official W3C JSON specifications and generates PHP code:
W3C specs (JSON)
│
├── elements.json (element definitions)
├── idlparsed.json (WebIDL interface definitions)
│
▼
Parser layer
│
├── ElementsParser — parses element spec
├── IdlParsedParser — parses IDL spec, filters HTML interfaces
│
▼
Generator layer
│
├── AttributesClassGenerator — ~100 attribute classes
├── NodeClassGenerator — ~100 node classes
├── NodeNameEnumGenerator — NodeName enum
├── NodeFunctionGenerator — node_fn.php helper functions
│
▼
src/VirtualDom/Generated/
│
├── Enum/NodeName.php
├── Node/*.php (DivNode, InputNode, ...)
├── NodeAttributes/*.php (DivAttributes, InputAttributes, ...)
└── node_fn.php (div(), input(), ...)
The pipeline is orchestrated by CodeGenerator
and triggered via:
just generate
This downloads fresh W3C specs and regenerates all files.
Generated vs Hand-Written Code
The src/VirtualDom/Generated/
directory contains
350 generated files.
These are committed to the repository so that users don't need to run
the generator. They are excluded from code coverage analysis.
Hand-written code lives in:
src/VirtualDom/Contract/— public interfaces (Node,Renderer, etc.)src/VirtualDom/Renderer/— HTML rendering implementationssrc/VirtualDom/Node/— built-in node types (TextNode, RawNode, FragmentNode)src/Template/— template system (Template, BlockRegistry, TemplateResolver)src/Tool/— FutureNode (two-step factory)
Attribute Mapping
WebIDL property names don't always match HTML attribute names.
The generator uses #[HtmlName('class')]
PHP attributes to annotate
properties where the names differ:
| IDL name | HTML name |
|---|---|
className
|
class
|
htmlFor
|
for
|
httpEquiv
|
http-equiv
|
The renderer resolves these mappings at render time via reflection, with results cached per class for performance.
Zero Runtime Dependencies
The library's runtime has no Composer dependencies beyond PHP 8.4 itself
(plus psr/container
for the optional TemplateResolver
).
Code generation tools (nette/php-generator
, cuyz/valinor
) are dev-only
dependencies and not required at runtime.