getMatches()[1]); if ($text === '') { return false; } $inlineContext->getCursor()->advanceBy($inlineContext->getFullMatchLength()); $inlineContext->getContainer()->appendChild(new Note($text)); return true; } } final class NoteRenderer implements NodeRendererInterface { public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable { if (!$node instanceof Note) { throw new \InvalidArgumentException('Expected Note'); } // HtmlElement accepts already rendered inner HTML: escape the plain note explicitly. return new HtmlElement('span', ['class' => 'lab-note', 'role' => 'note'], htmlspecialchars($node->text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')); } } final class NoteExtension implements ExtensionInterface { public function register(EnvironmentBuilderInterface $environment): void { $environment->addInlineParser(new NoteParser(), 200); $environment->addRenderer(Note::class, new NoteRenderer()); } }