使用 PHP 與 DOMDocument 自動產生 WordPress 文章預覽內容

前言

在 WordPress 開發中,有時候需要自動從文章內容擷取一部分作為預覽,方便在前台或後台顯示摘要。這段程式碼示範如何利用 PHP 的 DOMDocument 解析文章 HTML,並將第一個區塊內容存入自訂欄位,適合希望自動管理文章預覽的開發者或自學者。

主要功能說明

這段程式碼的核心目標是:

  • 避免在自動儲存或文章修訂版本時重複執行
  • 只處理文章(post)類型
  • 取得文章內容並套用 WordPress 內建的內容過濾器(the_content)
  • 使用 DOMDocument 將 HTML 內容解析為節點樹
  • 擷取第一個 HTML 區塊(元素節點)作為預覽內容
  • 將擷取結果寫入 Advanced Custom Fields (ACF) 的自訂欄位

這樣的設計能確保預覽內容是有效的 HTML 片段,且不會因為純文字截斷而破壞標籤結構。

程式碼解析

避免不必要的觸發

if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if (wp_is_post_revision($post_id)) return;

這兩個判斷用來避免在 WordPress 自動儲存或文章修訂版本時執行預覽生成,減少不必要的資源消耗。

文章類型與內容驗證

$post = get_post($post_id);
if ($post->post_type !== 'post') return;
$content = $post->post_content;
if (empty($content)) return;

確保只處理標準文章(post),且內容不為空。

套用內容過濾器

$filtered_content = apply_filters('the_content', $content);

這步驟會讓內容經過 WordPress 內建的過濾器處理,例如短碼解析、自動換行等,確保後續解析的 HTML 是完整且符合前台顯示的狀態。

使用 DOMDocument 解析 HTML

libxml_use_internal_errors(true);
$doc = new DOMDocument();
$doc->loadHTML('<?xml encoding="utf-8" ?>' . $filtered_content);
libxml_clear_errors();

因為 HTML 可能不完全符合 XML 規範,先抑制 libxml 錯誤,並在字串前加上 XML 編碼宣告,確保 UTF-8 正確處理。

擷取第一個區塊元素

$body = $doc->getElementsByTagName('body')->item(0);
$output_blocks = [];
$count = 0;
$max_blocks = 1;

foreach ($body->childNodes as $node) {
    if ($node->nodeType === XML_ELEMENT_NODE) {
        $output_blocks[] = $doc->saveHTML($node);
        $count++;
        if ($count >= $max_blocks) break;
    }
}

$preview_html = implode("\n", $output_blocks);

這段程式碼從 body 標籤下取得第一個元素節點(例如第一個段落或區塊標籤),並將其轉成 HTML 字串。這樣做的好處是避免截取不完整的 HTML,保持標籤結構完整。

寫入自訂欄位

update_field('ks_post_preview', $preview_html, $post_id);

利用 ACF 提供的 update_field 函式,將預覽內容寫入名為 ks_post_preview 的自訂欄位,方便後續在前台或後台調用。

實務應用與優化建議

  • 可依需求調整 $max_blocks 參數,擷取多個區塊作為預覽
  • 若文章內容包含複雜結構,DOMDocument 解析可確保 HTML 不會因截斷而破損
  • 可結合 WordPress 的鉤子(如 save_post)自動更新預覽,減少手動維護成本
  • 注意 ACF 欄位名稱需與後台設定一致,否則無法成功更新

常見問題與注意事項

  • DOMDocument 解析 HTML 時可能因不標準標籤產生警告,使用 libxml_use_internal_errors 可避免影響
  • 若文章內容過短或無有效區塊,預覽欄位可能為空,需在前端做好容錯處理
  • 自動儲存與修訂版本判斷是避免重複觸發的關鍵,缺少可能導致效能問題

完整程式碼

<?php
function ks_generate_post_preview($post_id) {
    // 避免自動儲存時觸發
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
    if (wp_is_post_revision($post_id)) return;

    $post = get_post($post_id);
    if ($post->post_type !== 'post') return;

    $content = $post->post_content;
    if (empty($content)) return;

    // 套用 the_content 濾鏡
    $filtered_content = apply_filters('the_content', $content);

    // 使用 DOMDocument 分析 HTML
    libxml_use_internal_errors(true);
    $doc = new DOMDocument();
    $doc->loadHTML('<?xml encoding="utf-8" ?>' . $filtered_content);
    libxml_clear_errors();

    $body = $doc->getElementsByTagName('body')->item(0);
    $output_blocks = [];
    $count = 0;
    $max_blocks = 1;

    foreach ($body->childNodes as $node) {
        if ($node->nodeType === XML_ELEMENT_NODE) {
            $output_blocks[] = $doc->saveHTML($node);
            $count++;
            if ($count >= $max_blocks) break;
        }
    }

    $preview_html = implode("\n", $output_blocks);

    // 寫入 ACF 欄位
    update_field('ks_post_preview', $preview_html, $post_id);
}
add_action('save_post', 'ks_generate_post_preview');