' . self::e(implode("\n", $code)) . ''; $i++; continue; } if (preg_match('/^(#{1,6})\s+(.+)$/', $line, $matches)) { $level = strlen($matches[1]); $html[] = '' . self::inline($matches[2]) . ''; $i++; continue; } if (preg_match('/^(-{3,}|\*{3,}|_{3,})$/', trim($line))) { $html[] = '
'; $i++; continue; } if (preg_match('/^\s*>\s?(.*)$/', $line)) { $quote = []; while ($i < $count && preg_match('/^\s*>\s?(.*)$/', rtrim($lines[$i]), $matches)) { $quote[] = $matches[1]; $i++; } $html[] = '
' . self::toHtml(implode("\n", $quote)) . '
'; continue; } if (preg_match('/^\s*[-*+]\s+(.+)$/', $line)) { $items = []; while ($i < $count && preg_match('/^\s*[-*+]\s+(.+)$/', rtrim($lines[$i]), $matches)) { $items[] = '
  • ' . self::inline($matches[1]) . '
  • '; $i++; } $html[] = ''; continue; } if (preg_match('/^\s*\d+\.\s+(.+)$/', $line)) { $items = []; while ($i < $count && preg_match('/^\s*\d+\.\s+(.+)$/', rtrim($lines[$i]), $matches)) { $items[] = '
  • ' . self::inline($matches[1]) . '
  • '; $i++; } $html[] = '
      ' . implode('', $items) . '
    '; continue; } $paragraph = [$line]; $i++; while ($i < $count && trim($lines[$i]) !== '' && !self::startsBlock(rtrim($lines[$i]))) { $paragraph[] = trim($lines[$i]); $i++; } $html[] = '

    ' . self::inline(implode(' ', $paragraph)) . '

    '; } return implode("\n", $html); } public static function excerpt(string $markdown, int $words = 28): string { $text = trim(preg_replace('/\s+/', ' ', strip_tags(self::toHtml($markdown))) ?? ''); if ($text === '') { return ''; } $parts = preg_split('/\s+/', $text) ?: []; if (count($parts) <= $words) { return $text; } return implode(' ', array_slice($parts, 0, $words)) . '...'; } private static function startsBlock(string $line): bool { return (bool) preg_match('/^(#{1,6}\s+|```|^\s*[-*+]\s+|^\s*\d+\.\s+|^\s*>\s?|(-{3,}|\*{3,}|_{3,})$)/', $line); } private static function inline(string $text): string { $placeholders = []; $text = preg_replace_callback('/`([^`]+)`/', static function (array $matches) use (&$placeholders): string { $key = "\x1A" . count($placeholders) . "\x1A"; $placeholders[$key] = '' . self::e($matches[1]) . ''; return $key; }, $text) ?? $text; $text = self::e($text); $text = preg_replace_callback('/!\[([^\]]*)\]\(([^)\s]+)(?:\s+"([^"]+)")?\)/', static function (array $matches): string { $title = isset($matches[3]) ? ' title="' . self::e($matches[3]) . '"' : ''; return '' . self::e($matches[1]) . ''; }, $text) ?? $text; $text = preg_replace_callback('/\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]+)")?\)/', static function (array $matches): string { $title = isset($matches[3]) ? ' title="' . self::e($matches[3]) . '"' : ''; return '' . $matches[1] . ''; }, $text) ?? $text; $text = preg_replace('/\*\*([^*]+)\*\*/', '$1', $text) ?? $text; $text = preg_replace('/\*([^*]+)\*/', '$1', $text) ?? $text; return strtr($text, $placeholders); } private static function e(string $value): string { return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); } }