' . $this->renderInline($text) . '
'; $paragraph = []; }; $flushList = function () use (&$listType, &$html): void { if ($listType !== null) { $html[] = '' . $listType . '>'; $listType = null; } }; $flushTable = function () use (&$table, &$html): void { if ($table === null) { return; } $html[] = '| ' . $this->renderInline($cell) . ' | ', $table['headers'])) . '
|---|
| ' . $this->renderInline($cell) . ' | ', $row)) . '
' . htmlspecialchars(implode("\n", $codeLines), ENT_QUOTES, 'UTF-8') . '';
$codeLines = [];
$inCodeBlock = false;
} else {
$inCodeBlock = true;
}
continue;
}
if ($inCodeBlock) {
$codeLines[] = $line;
continue;
}
$trimmed = trim($line);
if ($trimmed === '') {
$flushParagraph();
$flushList();
$flushTable();
continue;
}
if (preg_match('/^(#{1,6})\s+(.+)$/', $trimmed, $matches)) {
$flushParagraph();
$flushList();
$flushTable();
$level = strlen($matches[1]);
$html[] = sprintf('' . $this->renderInline($matches[1]) . ''; continue; } if (preg_match('/^---+$/', $trimmed)) { $flushParagraph(); $flushList(); $flushTable(); $html[] = '
' . htmlspecialchars(implode("\n", $codeLines), ENT_QUOTES, 'UTF-8') . '';
}
$flushParagraph();
$flushList();
$flushTable();
return implode("\n", $html);
}
private function renderInline(string $text): string
{
$text = htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
$text = preg_replace('/`([^`]+)`/', '$1', $text) ?? $text;
$text = preg_replace('/\*\*([^*]+)\*\*/', '$1', $text) ?? $text;
$text = preg_replace('/\*([^*]+)\*/', '$1', $text) ?? $text;
$text = preg_replace('/\[(.+?)\]\((.+?)\)/', '$1', $text) ?? $text;
return $text;
}
private function isTableDelimiter(string $line): bool
{
return (bool) preg_match('/^\|?[\s:-]+\|[\s|:-]*$/', $line);
}
private function tableCells(string $line): array
{
$line = trim($line);
$line = trim($line, '|');
return array_map(static fn (string $cell): string => trim($cell), explode('|', $line));
}
}