{$text}

"; $para = []; } }; $flushList = static function () use (&$list, &$listType, &$blocks) { if ($list) { $tag = $listType === 'ol' ? 'ol' : 'ul'; $attr = $listType === 'ol' ? ' {"ordered":true}' : ''; $items = ''; foreach ($list as $i) { $items .= '
  • ' . self::inline($i) . '
  • '; } $blocks[] = "<{$tag}>{$items}"; $list = []; $listType = null; } }; foreach ($lines as $line) { $t = rtrim($line); if (trim($t) === '') { $flushPara(); $flushList(); continue; } if (preg_match('/^(#{1,6})\s+(.*)$/', $t, $m)) { $flushPara(); $flushList(); $level = strlen($m[1]); $text = self::inline(trim($m[2])); $blocks[] = "{$text}"; continue; } if (preg_match('/^(\*\*\*+|---+|___+)\s*$/', $t)) { $flushPara(); $flushList(); $blocks[] = '
    '; continue; } if (preg_match('/^>\s?(.*)$/', $t, $m)) { $flushPara(); $flushList(); $blocks[] = '

    ' . self::inline($m[1]) . '

    '; continue; } if (preg_match('/^[-*+]\s+(.*)$/', $t, $m)) { $flushPara(); if ($listType === 'ol') { $flushList(); } $listType = 'ul'; $list[] = $m[1]; continue; } if (preg_match('/^\d+[.)]\s+(.*)$/', $t, $m)) { $flushPara(); if ($listType === 'ul') { $flushList(); } $listType = 'ol'; $list[] = $m[1]; continue; } $flushList(); $para[] = trim($t); } $flushPara(); $flushList(); return implode("\n\n", $blocks); } /** Inline formatting: escape, then bold / italic / code / links. */ private static function inline(string $s): string { $s = esc_html($s); $s = (string) preg_replace('/\*\*(.+?)\*\*/s', '$1', $s); $s = (string) preg_replace('/__(.+?)__/s', '$1', $s); $s = (string) preg_replace('/(?$1', $s); $s = (string) preg_replace('/(?$1', $s); $s = (string) preg_replace('/`(.+?)`/s', '$1', $s); $s = (string) preg_replace_callback( '/\[([^\]]+)\]\(([^)]+)\)/', static function ($m) { return '' . $m[1] . ''; }, $s ); return $s; } }