@@ -101,8 +101,67 @@ class HtmlRenderer implements RendererInterface
101101 */
102102 protected array $ collectedFootnotes = [];
103103
104+ /**
105+ * Dispatch table mapping node class names to render method names
106+ *
107+ * @var array<class-string<\Djot\Node\Node>, string>
108+ */
109+ protected array $ nodeRenderers = [];
110+
104111 public function __construct (protected bool $ xhtml = false )
105112 {
113+ $ this ->initNodeRenderers ();
114+ }
115+
116+ /**
117+ * Initialize the node renderer dispatch table
118+ *
119+ * Maps node class names to render method names for O(1) lookup.
120+ */
121+ protected function initNodeRenderers (): void
122+ {
123+ $ this ->nodeRenderers = [
124+ Document::class => 'renderChildren ' ,
125+ Paragraph::class => 'renderParagraph ' ,
126+ Heading::class => 'renderHeading ' ,
127+ CodeBlock::class => 'renderCodeBlock ' ,
128+ Comment::class => '' ,
129+ RawBlock::class => 'renderRawBlock ' ,
130+ BlockQuote::class => 'renderBlockQuote ' ,
131+ DefinitionList::class => 'renderDefinitionList ' ,
132+ DefinitionTerm::class => 'renderDefinitionTerm ' ,
133+ DefinitionDescription::class => 'renderDefinitionDescription ' ,
134+ ListBlock::class => 'renderList ' ,
135+ ListItem::class => 'renderListItem ' ,
136+ ThematicBreak::class => 'renderThematicBreak ' ,
137+ Div::class => 'renderDiv ' ,
138+ Figure::class => 'renderFigure ' ,
139+ Caption::class => 'renderCaption ' ,
140+ Table::class => 'renderTable ' ,
141+ TableRow::class => 'renderTableRow ' ,
142+ TableCell::class => 'renderTableCell ' ,
143+ LineBlock::class => 'renderLineBlock ' ,
144+ Footnote::class => 'renderFootnote ' ,
145+ Text::class => 'renderText ' ,
146+ Emphasis::class => 'renderEmphasis ' ,
147+ Strong::class => 'renderStrong ' ,
148+ Link::class => 'renderLink ' ,
149+ Image::class => 'renderImage ' ,
150+ Code::class => 'renderCode ' ,
151+ RawInline::class => 'renderRawInline ' ,
152+ Math::class => 'renderMath ' ,
153+ Symbol::class => 'renderSymbol ' ,
154+ FootnoteRef::class => 'renderFootnoteRef ' ,
155+ SoftBreak::class => 'renderSoftBreak ' ,
156+ HardBreak::class => 'renderHardBreak ' ,
157+ Span::class => 'renderSpan ' ,
158+ Highlight::class => 'renderHighlight ' ,
159+ Superscript::class => 'renderSuperscript ' ,
160+ Subscript::class => 'renderSubscript ' ,
161+ Insert::class => 'renderInsert ' ,
162+ Delete::class => 'renderDelete ' ,
163+ Abbreviation::class => 'renderAbbreviation ' ,
164+ ];
106165 }
107166
108167 /**
@@ -362,64 +421,36 @@ protected function renderAttributesExcluding(Node $node, array $exclude): string
362421
363422 protected function renderNode (Node $ node ): string
364423 {
365- // Dispatch render event
366- $ eventName = 'render. ' . $ node ->getType ();
367- $ event = new RenderEvent ($ node );
424+ // Only dispatch events if listeners are registered (avoid object allocation)
425+ if ($ this ->hasAnyListeners ()) {
426+ $ eventName = 'render. ' . $ node ->getType ();
427+ $ event = new RenderEvent ($ node );
368428
369- // Call specific listeners
370- $ this ->dispatchEvent ($ eventName , $ event );
429+ // Call specific listeners
430+ $ this ->dispatchEvent ($ eventName , $ event );
371431
372- // Call wildcard listeners
373- $ this ->dispatchEvent ('render.* ' , $ event );
432+ // Call wildcard listeners
433+ $ this ->dispatchEvent ('render.* ' , $ event );
374434
375- // If listener provided custom HTML, use it
376- if ($ event ->isDefaultPrevented ()) {
377- return $ event ->getHtml () ?? '' ;
435+ // If listener provided custom HTML, use it
436+ if ($ event ->isDefaultPrevented ()) {
437+ return $ event ->getHtml () ?? '' ;
438+ }
378439 }
379440
380- return match (true ) {
381- $ node instanceof Document => $ this ->renderChildren ($ node ),
382- $ node instanceof Paragraph => $ this ->renderParagraph ($ node ),
383- $ node instanceof Heading => $ this ->renderHeading ($ node ),
384- $ node instanceof CodeBlock => $ this ->renderCodeBlock ($ node ),
385- $ node instanceof Comment => '' , // Comments are stripped from output
386- $ node instanceof RawBlock => $ this ->renderRawBlock ($ node ),
387- $ node instanceof BlockQuote => $ this ->renderBlockQuote ($ node ),
388- $ node instanceof DefinitionList => $ this ->renderDefinitionList ($ node ),
389- $ node instanceof DefinitionTerm => $ this ->renderDefinitionTerm ($ node ),
390- $ node instanceof DefinitionDescription => $ this ->renderDefinitionDescription ($ node ),
391- $ node instanceof ListBlock => $ this ->renderList ($ node ),
392- $ node instanceof ListItem => $ this ->renderListItem ($ node ),
393- $ node instanceof ThematicBreak => $ this ->renderThematicBreak ($ node ),
394- $ node instanceof Div => $ this ->renderDiv ($ node ),
395- $ node instanceof Figure => $ this ->renderFigure ($ node ),
396- $ node instanceof Caption => $ this ->renderCaption ($ node ),
397- $ node instanceof Table => $ this ->renderTable ($ node ),
398- $ node instanceof TableRow => $ this ->renderTableRow ($ node ),
399- $ node instanceof TableCell => $ this ->renderTableCell ($ node ),
400- $ node instanceof LineBlock => $ this ->renderLineBlock ($ node ),
401- $ node instanceof Footnote => $ this ->renderFootnote ($ node ),
402- $ node instanceof Text => $ this ->renderText ($ node ),
403- $ node instanceof Emphasis => $ this ->renderEmphasis ($ node ),
404- $ node instanceof Strong => $ this ->renderStrong ($ node ),
405- $ node instanceof Link => $ this ->renderLink ($ node ),
406- $ node instanceof Image => $ this ->renderImage ($ node ),
407- $ node instanceof Code => $ this ->renderCode ($ node ),
408- $ node instanceof RawInline => $ this ->renderRawInline ($ node ),
409- $ node instanceof Math => $ this ->renderMath ($ node ),
410- $ node instanceof Symbol => $ this ->renderSymbol ($ node ),
411- $ node instanceof FootnoteRef => $ this ->renderFootnoteRef ($ node ),
412- $ node instanceof SoftBreak => $ this ->renderSoftBreak (),
413- $ node instanceof HardBreak => $ this ->renderHardBreak (),
414- $ node instanceof Span => $ this ->renderSpan ($ node ),
415- $ node instanceof Highlight => $ this ->renderHighlight ($ node ),
416- $ node instanceof Superscript => $ this ->renderSuperscript ($ node ),
417- $ node instanceof Subscript => $ this ->renderSubscript ($ node ),
418- $ node instanceof Insert => $ this ->renderInsert ($ node ),
419- $ node instanceof Delete => $ this ->renderDelete ($ node ),
420- $ node instanceof Abbreviation => $ this ->renderAbbreviation ($ node ),
421- default => $ this ->renderChildren ($ node ),
422- };
441+ // Use dispatch table for O(1) lookup instead of instanceof chain
442+ $ class = $ node ::class;
443+ if (isset ($ this ->nodeRenderers [$ class ])) {
444+ $ method = $ this ->nodeRenderers [$ class ];
445+ if ($ method === '' ) {
446+ return '' ; // Comment nodes
447+ }
448+
449+ /** @var string */
450+ return $ this ->$ method ($ node );
451+ }
452+
453+ return $ this ->renderChildren ($ node );
423454 }
424455
425456 protected function renderChildren (Node $ node ): string
0 commit comments