Steamer Lane Studio技術備忘録WordPress

投稿内の特定のキーワードに指定のリンクを自動で貼る-ACF対応改修ver

wordpress 投稿内の特定のキーワードに指定のリンクを自動で貼る-ACF対応改修ver 作成日: 2025年8月11日

MTのkotonohaリンクplに似せたfunction.php記述コードで作ってきたが・・・
管理サイトに実装するうち「あれもこれも」となって追記などしてきたが、改めて直してみた。

修正点

  • the_contentは動的、acfのフィールドのものは静的=コード記述した時点でDB内の文字列にリンクが貼られるものを、動的に統一した。
    ここちょっと疎かにしてて確認してなかったのだが、ページが重くなるリスクはあるが全て動的にしてもとの文章・テキストはRAW状態にした。
  • 除外処理の優先度確認。オプション的な機能なので、システム上のエラーで自動リンクが為されなくてもOKだが、pのインラインのbやspanなどまた、除外ワードの厳格化を施した。
  • function.php記述のための可読性とか。設定項目を上位にして、設定しやすくね。設定入力inputとかを備えた設定ページ付ければプラグインにもなるけど、エンドユーザーによる利用は考えてない(ここらへんで作ってる機能はみんなそう!)ので、管理者レベルで使いやすく程度かな。

で、コードは以下の通り

/* MTのkotonohalink的-wiki的なコンテンツ制作用重くなること注意。キーワードで重複的になるもの、「日経平均」と「日経」などを指定する場合は重複部分を内包する「日経平均」を処理順上にする */// Type1内部・外部ページへのリンクと除外ページ指定$my_autolink_settings = [    // 内部リンクのキーワードとページID    'internal' => [        'ことのは' => 38,/////////////////////////////////////////////////-*-初期設定/確認-*-    ],    // 外部リンクのキーワードとURL    'external' => [        '除外' => 'https://リンク先1',/////////////////////////////////////////////////-*-初期設定/確認-*-        'リンク先2' => 'https://リンク先2',    ],    // 除外キーワード(これらを含む単語はリンク化しない)    'excluded' => [        'ことのはリンク',/////////////////////////////////////////////////-*-初期設定/確認-*-        '除外2',    ],    // 処理をスキップするページID(リンク先になるページなど)    'excluded_page_ids' => [9999, 9998],/////////////////////////////////////////////////-*-初期設定/確認-*-    // ACFの対象フィールド名'acf_target_fields' => ['access', 'field'],/////////////////////////////////////////////////-*-初期設定/確認-*-!!!ACFフィールドはその出力でpかliにラップされている必要があるのでtextやtextareaではなくリッチエディターにしておく];function my_autolink_keywords_process($content) {    global $my_autolink_settings;    $internal_keywords_and_ids = $my_autolink_settings['internal'];    $external_keywords_and_urls = $my_autolink_settings['external'];    $excluded_keywords = $my_autolink_settings['excluded'];    // pタグとliタグの中身だけを処理する    $content = preg_replace_callback('/<(p|li)([^>]*)>(.*?)<\/\1>/is', function($matches) use ($internal_keywords_and_ids, $external_keywords_and_urls, $excluded_keywords) {        $tag_name = $matches[1];        $tag_attributes = $matches[2];        $inner_content = $matches[3];        $placeholders = [];        $placeholder_count = 0;// <a>タグとインラインタグ(b, strong, span, em, i)を一時的にプレースホルダーに置き換える//これにより、これらのタグ内のキーワードがリンク化されるのを防ぎます。$inner_content = preg_replace_callback('/(<a\b[^>]*>.*?<\/a>)|(<(b|strong|span|em|i)[^>]*>.*?<\/\3>)/is', function($matches) use (&$placeholders, &$placeholder_count) {$placeholder = '__PLACEHOLDER_' . $placeholder_count++ . '__';$placeholders[$placeholder] = $matches[0];return $placeholder;}, $inner_content);        // 除外キーワードをプレースホルダーに置き換える        // 除外キーワードを優先して処理するため、長いキーワードからソート        $excluded_keywords_sorted = $excluded_keywords;        usort($excluded_keywords_sorted, function($a, $b) {            return mb_strlen($b) - mb_strlen($a);        });        foreach ($excluded_keywords_sorted as $keyword) {            $keyword_escaped = preg_quote($keyword, '/');            $inner_content = preg_replace_callback('/(' . $keyword_escaped . ')/u', function($matches) use (&$placeholders, &$placeholder_count) {                $placeholder = '__PLACEHOLDER_' . $placeholder_count++ . '__';                $placeholders[$placeholder] = $matches[0];                return $placeholder;            }, $inner_content, 1);        }        // 内部・外部リンクのキーワードをプレースホルダーに置き換える(リンクを生成するため)        $link_keywords_to_process = array_merge($internal_keywords_and_ids, $external_keywords_and_urls);        uksort($link_keywords_to_process, function($a, $b) {            return mb_strlen($b) - mb_strlen($a);        });        foreach ($link_keywords_to_process as $keyword => $data) {            $keyword_escaped = preg_quote($keyword, '/');            $replacement = '';            if (isset($internal_keywords_and_ids[$keyword])) {                $url = get_permalink($internal_keywords_and_ids[$keyword]);                $replacement = '<a href="' . esc_url($url) . '" class="totri">' . $keyword . '</a>';            } elseif (isset($external_keywords_and_urls[$keyword])) {                $url = $external_keywords_and_urls[$keyword];                $replacement = '<a href="' . esc_url($url) . '" class="totri" target="_blank">' . $keyword . '</a>';            }            if ($replacement) {                $inner_content = preg_replace('/(' . $keyword_escaped . ')(?![^<]*>)/u', $replacement, $inner_content, 1);            }        }        // 最後に、すべてのプレースホルダーを元の文字列に戻す        $inner_content = str_replace(array_keys($placeholders), array_values($placeholders), $inner_content);        return '<' . $tag_name . $tag_attributes . '>' . $inner_content . '</' . $tag_name . '>';    }, $content);    return $content;}//the_contentフィルターに自動リンク機能を適用function my_autolink_keywords_in_paragraphs($content) {    global $my_autolink_settings;    $excluded_page_ids = $my_autolink_settings['excluded_page_ids'];    $current_page_id = get_the_ID();    if (in_array($current_page_id, $excluded_page_ids)) {        return $content;    }    return my_autolink_keywords_process($content);}add_filter('the_content', 'my_autolink_keywords_in_paragraphs', 10);//ACFフィールドに自動リンク機能を適用function my_autolink_keywords_acf_filter($value, $post_id, $field) {    global $my_autolink_settings;    $excluded_page_ids = $my_autolink_settings['excluded_page_ids'];    $current_page_id = get_the_ID();    $target_fields = $my_autolink_settings['acf_target_fields'];    if (in_array($current_page_id, $excluded_page_ids) || !in_array($field['name'], $target_fields) || !is_string($value)) {        return $value;    }    return my_autolink_keywords_process($value);}add_filter('acf/format_value', 'my_autolink_keywords_acf_filter', 10, 3);