' . 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), false) . '
'; 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 { $markdown = preg_replace('/<\?(?:php|=)?[\s\S]*?\?>/i', ' ', $markdown) ?? $markdown; $html = self::toHtml($markdown, false); $html = preg_replace('#<(script|style)\b[^>]*>.*?#is', ' ', $html) ?? $html; $text = trim(preg_replace('/\s+/', ' ', strip_tags($html)) ?? ''); 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 self::startsHtmlBlock($line) || (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 = preg_replace_callback('/<\/?[A-Za-z][A-Za-z0-9:-]*(?:\s+[^<>]*)?>||]*>/s', static function (array $matches) use (&$placeholders): string { $key = "\x1A" . count($placeholders) . "\x1A"; $placeholders[$key] = $matches[0]; 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 executePhp(string $source): string { if (!str_contains($source, '' . $source); return strtr((string) ob_get_clean(), $placeholders); } catch (\Throwable $error) { ob_end_clean(); return strtr($source, $placeholders) . "\n\n"; } } private static function startsHtmlBlock(string $line): bool { $line = trim($line); return (bool) preg_match('/^(')) { $block[] = rtrim($lines[$i]); $i++; } return [implode("\n", $block), $i]; } if (!preg_match('/^<([A-Za-z][A-Za-z0-9:-]*)\b/i', $trimmed, $matches)) { return [implode("\n", $block), $i]; } $tag = strtolower($matches[1]); $voidTags = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']; if (in_array($tag, $voidTags, true) || str_ends_with($trimmed, '/>') || stripos($trimmed, '') !== false) { return [implode("\n", $block), $i]; } while ($i < $count) { $block[] = rtrim($lines[$i]); if (stripos((string) end($block), '') !== false) { $i++; break; } if (trim($lines[$i]) === '' && !in_array($tag, ['article', 'aside', 'div', 'footer', 'form', 'header', 'iframe', 'main', 'nav', 'pre', 'script', 'section', 'style', 'table', 'textarea'], true)) { $i++; break; } $i++; } return [implode("\n", $block), $i]; } private static function e(string $value): string { return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); } }