';
$listOpen = false;
}
continue;
}
if (preg_match('/^[-*]\s+(.+)$/u', $line, $match)) {
if (!$listOpen) {
$html[] = '
';
$listOpen = true;
}
$html[] = '- ' . self::inline($match[1]) . '
';
continue;
}
if ($listOpen) {
$html[] = '
';
$listOpen = false;
}
if (preg_match('/^(#{1,3})\s+(.+)$/u', $line, $match)) {
$level = strlen($match[1]) + 1;
$html[] = '' . self::inline($match[2]) . '';
continue;
}
$html[] = '' . self::inline($line) . '
';
}
if ($listOpen) {
$html[] = '';
}
return implode("\n", $html);
}
private static function inline(string $text): string
{
$images = [];
$text = preg_replace_callback('/!\[([^\]]*)\]\((\/media\/site\/[A-Za-z0-9._%-]+)\)/u', static function (array $match) use (&$images): string {
$token = "\x1B" . count($images) . "\x1B";
$images[] = '
';
return $token;
}, $text) ?? $text;
$links = [];
$text = preg_replace_callback('/\[([^\]]+)\]\((https:\/\/[^\s)]+)\)/u', static function (array $match) use (&$links): string {
if (!filter_var($match[2], FILTER_VALIDATE_URL)) {
return $match[0];
}
$token = "\x1A" . count($links) . "\x1A";
$links[] = '' . htmlspecialchars($match[1], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '';
return $token;
}, $text) ?? $text;
$text = htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$text = preg_replace('/\*\*(.+?)\*\*/u', '$1', $text) ?? $text;
$text = preg_replace('/(?$1', $text) ?? $text;
foreach ($links as $index => $link) {
$text = str_replace("\x1A" . $index . "\x1A", $link, $text);
}
foreach ($images as $index => $image) {
$text = str_replace("\x1B" . $index . "\x1B", $image, $text);
}
return $text;
}
}