/', '', $html);
$html = preg_replace('##is', '', $html);
$html = preg_replace('##is', '', $html);
$html = preg_replace('##is', '', $html);
// Convert YouTube/Vimeo iframes to markdown links (must run before generic iframe strip).
$html = preg_replace_callback(
'##is',
function ($m) {
$src = $m[1];
$title = isset($m[2]) && $m[2] !== '' ? $m[2] : 'Video';
if (preg_match('#(youtube\.com|youtu\.be|vimeo\.com)#i', $src)) {
return "\n[" . $title . '](' . $src . ")\n";
}
return '';
},
$html
);
// Convert
to newline and
]*>\s*]*>(.*?)\s*#is',
function ($m) {
$code = html_entity_decode($m[1], ENT_QUOTES | ENT_HTML5, 'UTF-8');
return "\n\n```\n" . trim($code) . "\n```\n\n";
},
$html
);
$html = preg_replace_callback(
'#]*>(.*?)#is', function ($m) { $code = html_entity_decode(wp_strip_all_tags($m[1]), ENT_QUOTES | ENT_HTML5, 'UTF-8'); return "\n\n```\n" . trim($code) . "\n```\n\n"; }, $html ); // Inline code. $html = preg_replace_callback( '#
]*>(.*?)#is',
function ($m) {
$code = html_entity_decode(wp_strip_all_tags($m[1]), ENT_QUOTES | ENT_HTML5, 'UTF-8');
return '`' . $code . '`';
},
$html
);
// Blockquotes.
$html = preg_replace_callback(
'#]*>(.*?)#is', function ($m) { $inner = trim(self::strip_tags_preserve($m[1])); $lines = preg_split('/\r?\n/', $inner); $lines = array_map(function ($l) { return '> ' . ltrim($l); }, $lines); return "\n\n" . implode("\n", $lines) . "\n\n"; }, $html ); // Tables. $html = preg_replace_callback( '#
]*>(.*?)
#is', function ($m) { $text = trim(self::strip_tags_preserve($m[1])); if ($text === '') { return ''; } return "\n\n" . $text . "\n\n"; }, $html ); // Strip any remaining tags but keep the inner text. $html = self::strip_tags_preserve($html); // Decode entities after stripping tags. $html = html_entity_decode($html, ENT_QUOTES | ENT_HTML5, 'UTF-8'); // Collapse excessive blank lines. $html = preg_replace("/\n{3,}/", "\n\n", $html); return trim($html); } /** * Convert a WordPress post to markdown. * * Runs `the_content` filter and do_shortcode() first so page-builder * content (Elementor, Divi, Beaver Builder, etc.) is rendered before * conversion. * * @param int $post_id Post ID. * @param array $options { * Optional. { * @type bool $include_frontmatter Prepend YAML frontmatter. Default true. * @type bool $include_featured_image Prepend featured image as markdown. Default true. * } * } * @return string Markdown output (empty string if post not found). */ public static function convert_post($post_id, $options = []) { $post = get_post(absint($post_id)); if (!$post) { return ''; } $options = wp_parse_args($options, [ 'include_frontmatter' => true, 'include_featured_image' => true, ]); $raw_content = $post->post_content; $filtered = apply_filters('the_content', $raw_content); $filtered = do_shortcode($filtered); $markdown = self::convert($filtered); $prefix = ''; if (!empty($options['include_frontmatter'])) { $author_name = ''; if (!empty($post->post_author)) { $author = get_userdata($post->post_author); if ($author && !empty($author->display_name)) { $author_name = $author->display_name; } } $frontmatter = "---\n"; $frontmatter .= 'title: ' . self::yaml_escape($post->post_title) . "\n"; $frontmatter .= 'slug: ' . self::yaml_escape($post->post_name) . "\n"; $frontmatter .= 'date: ' . self::yaml_escape($post->post_date) . "\n"; if ($author_name !== '') { $frontmatter .= 'author: ' . self::yaml_escape($author_name) . "\n"; } $frontmatter .= "---\n\n"; $prefix .= $frontmatter; } if (!empty($options['include_featured_image'])) { $thumb_id = get_post_thumbnail_id($post_id); if ($thumb_id) { $src = wp_get_attachment_url($thumb_id); if ($src) { $alt = get_post_meta($thumb_id, '_wp_attachment_image_alt', true); if (!is_string($alt)) { $alt = ''; } if ($alt === '') { $alt = $post->post_title; } $prefix .= '\n\n"; } } } return $prefix . $markdown; } /** * Escape a string for safe use as a YAML scalar value. * * @param string $value * @return string */ private static function yaml_escape($value) { $value = (string) $value; // Wrap in double quotes and escape backslashes and quotes. $escaped = str_replace(['\\', '"'], ['\\\\', '\\"'], $value); return '"' . $escaped . '"'; } /** * Strip HTML tags but leave a usable inline form (decodes entities later). * * @param string $html * @return string */ private static function strip_tags_preserve($html) { // Replace block-ish containers with newlines so stripping doesn't mash content. $html = preg_replace('#(?:div|section|article|aside|header|footer|main|nav|figure|figcaption)>#i', "\n", $html); $html = preg_replace('#<(?:div|section|article|aside|header|footer|main|nav|figure|figcaption)\b[^>]*>#i', '', $html); // Strip remaining tags. return strip_tags($html); // phpcs:ignore WordPress.WP.AlternativeFunctions.strip_tags_strip_tags -- wp_strip_all_tags() also strips script/style contents and trims; plain strip_tags() is required } /** * Convert a