78 lines
3.1 KiB
PHP
78 lines
3.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
final class PublicContentFormatter
|
|
{
|
|
public static function render(string $text): string
|
|
{
|
|
$lines = preg_split('/\R/u', trim($text)) ?: [];
|
|
$html = [];
|
|
$listOpen = false;
|
|
|
|
foreach ($lines as $line) {
|
|
$line = trim($line);
|
|
if ($line === '') {
|
|
if ($listOpen) {
|
|
$html[] = '</ul>';
|
|
$listOpen = false;
|
|
}
|
|
continue;
|
|
}
|
|
if (preg_match('/^[-*]\s+(.+)$/u', $line, $match)) {
|
|
if (!$listOpen) {
|
|
$html[] = '<ul>';
|
|
$listOpen = true;
|
|
}
|
|
$html[] = '<li>' . self::inline($match[1]) . '</li>';
|
|
continue;
|
|
}
|
|
if ($listOpen) {
|
|
$html[] = '</ul>';
|
|
$listOpen = false;
|
|
}
|
|
if (preg_match('/^(#{1,3})\s+(.+)$/u', $line, $match)) {
|
|
$level = strlen($match[1]) + 1;
|
|
$html[] = '<h' . $level . '>' . self::inline($match[2]) . '</h' . $level . '>';
|
|
continue;
|
|
}
|
|
$html[] = '<p>' . self::inline($line) . '</p>';
|
|
}
|
|
if ($listOpen) {
|
|
$html[] = '</ul>';
|
|
}
|
|
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[] = '<img class="public-content-image" src="' .
|
|
htmlspecialchars($match[2], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '" alt="' .
|
|
htmlspecialchars($match[1], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '" loading="lazy">';
|
|
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[] = '<a href="' . htmlspecialchars($match[2], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') .
|
|
'" rel="noopener noreferrer">' . htmlspecialchars($match[1], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '</a>';
|
|
return $token;
|
|
}, $text) ?? $text;
|
|
$text = htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
|
$text = preg_replace('/\*\*(.+?)\*\*/u', '<strong>$1</strong>', $text) ?? $text;
|
|
$text = preg_replace('/(?<!\*)\*([^*]+)\*(?!\*)/u', '<em>$1</em>', $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;
|
|
}
|
|
}
|