WordPress 記事本文をフックしてリンク先記事の Post ID 取得

WordPressの記事本文内にあるリンクタグ(<a  /a>)のURLを抽出して、リンク先がサイト内の記事ならばその Post ID を取得する方法を紹介します

リンク先の記事情報を取得するにはどうすれば良いのかを調べていて、ようやく情報を取得できるようになったので手順を紹介致します

  1. the_content にフィルターフックさせます
  2. フックした記事からリンクタグ抽出
  3. リンクタグ内から正規表現を使い URL を取得
  4. URLが内部リンクならリンク先記事の Post ID取得
  5. Post ID から様々な情報を取得

the_content にフィルターフック

add_filter を用いて the_content にフィルターフックさせます
今回は、get_url_to_postID という関数を作成して、プラグインのコンストラクタで管理画面以外の場合にフックさせました

 if(!is_admin()) {
     //記事内容 the_content にフックさせ、サイト内リンク記事のポストID収集
     add_filter('the_content',array(&$this, 'get_url_to_postID'));
 }

フックさせる関数内で投稿記事か固定ページの記事か判定して、記事コンテンツ $text にフィルター処理を行い必要なデータを抽出します

また、$text をリターンしないと記事が表示されなくなるので注意です (^^)

 //記事のサイト内リンク先記事のポストID収集
 public function get_url_to_postID($text)
 {    
     if(is_single() || is_page()){
         $this->post_idlist = CeltisLib2::html_linkurl_postid($text);
     }
     return $text;
 }    

フックさせるフィルター関数はこんな感じです

    //HTML 内のリンクタグからリンク先記事のポストIDを取り出す
    // $html 検索するHTML文
    // 戻り値 ポストID配列(PostID, Title) 
    public static function html_linkurl_postid($html)
    {
        //コンテンツから <a /a>タグ を抽出して配列に保存(画像等の埋め込みタグの含まれているものを除く)
        $exkey = array('<img', '<embed', '<iframe', '<object', '<param', '<video', '<audio', '<source', '<track', '<canvas', '<map', '<area');
        $alink = CeltisLib2::htmltagsplit_exclude($html, '<a', '/a>', $exkey);
        //リンクタグ内のURLが内部リンクならポストID、記事タイトルを取得して重複しないよう保存
        $pidlist = array();
        for($n=1; $n <count($alink); $n++ ){
            $pid = CeltisLib2::linkurl_postid($alink[$n]);
            if($pid !== false)
                $pidlist += $pid;
        }
        return $pidlist;
    }

以下、もう少し細かく処理内容を紹介します

リンクタグ抽出

記事内の <a /a>タグを抽出して配列に保存します

これは、リンクタグからURL部分を抽出するための前処理です

また、、今回は記事のリンクタグを対象としているので、リンクタグ内に画像等の埋め込みタグが含まれているものを事前にチェックして対象外とします

ちなみに、除外するリンクは画像がほとんどではないかと思いますが、<img 以外のタグも入れてありますので、必要に応じて修正して下さい

    //HTML の指定タグ位置を取得(大文字、小文字を区別しない)
    // $html 検索するHTML文
    // $stag スタートタグ 例 <script
    // $etag エンドタグ   例 /script>
    // $ofset検索開始位置 0- (strlen()-1)
    // 戻り値 位置情報配列(start, end)  エラー時は FALSE
    public static function htmltagpos($html, $stag, $etag, $ofset=0)
    {
        $pos = FALSE;
        $start = stripos($html, $stag, $ofset);
        if($start !== FALSE){
            $end = stripos($html, $etag, $start);
            if($end !== FALSE){
                $end += (strlen($etag)-1);
                $pos = array($start, $end);
            }
        }
        return $pos;
    }
    
    //HTML の指定タグを分割して取り出す(但しタグ内に除外キーワードが含まれているものを除く)
    // $html 検索するHTML文
    // $stag スタートタグ 例 <a
    // $etag エンドタグ   例 /a>
    // $exkeylist 除外キーワードリスト 例えば画像等の埋め込みタグを指定して除外する
    // $ofset検索開始位置 0- (strlen()-1)
    // 戻り値 分割 HTML文配列 $newhtml(指定タグ以外の部分, 指定タグ1, 指定タグ2, --- 指定タグN)  
    public static function htmltagsplit_exclude($html, $stag, $etag, $exkeylist, $ofset=0)
    {
        $newhtml[0] = '';
        $exkeystr = implode('|', $exkeylist);
        
        if(strlen($html) > 0){
            $start = $ofset;
            $end = strlen($html) - $ofset - 1;
            if($start != 0)
                $newhtml[0] = substr($html, 0, $start);
            for($cnt=0; ($pos = CeltisLib2::htmltagpos($html, $stag, $etag, $start)) !== FALSE; ){
                $len = $pos[1] - $pos[0] + 1;
                $sephtml = substr($html, $pos[0], $len);
               
                //タグ内に禁止文字列のリストが含まれているかチェック
                if (! preg_match("/$exkeystr/", $sephtml)){
                    $newhtml[0] = $newhtml[0] . substr($html, $start, $pos[0] - $start);
                    $newhtml[$cnt+1] = $sephtml;
                    $cnt++;
                }
                else {
                    $newhtml[0] = $newhtml[0] . substr($html, $start, $pos[0] - $start) . $sephtml;
                }
                $start = $pos[1] + 1;
            }
            if($start < $end)
                $newhtml[0] = $newhtml[0] . substr($html, $start, $end - $start + 1);
        }
        return $newhtml;
    }

リンクタグから正規表現で URL 取得

次に取得したリンクタグの配列から正規表現を使用してURLを取得します

preg_ match を使い http もしくは https で始まるURLにマッチしたら、$match にURLが抽出されてセットされます

正直なところ正規表現は難しいです。検索してもいろいろなパターンが紹介されていて、URLを取得するのにどれが正しいのか自信がありません (^_^;)

とりあえず下記のようなパターンで試してみましたが、今のところ問題ないような感じです。必要に応じて修正して下さい

    // <a /a>タグ内のURLが内部リンクならリンク先記事のポストIDとタイトルを取得
    // 戻り値 ポストID配列(PostID, Title)  エラー時は false
    public static function linkurl_postid($atagstr)
    {
        $pattern = '/(https?):\/\/([-_.!~*\'()a-zA-Z0-9;\/?:\@&=+\$,%#]+)/u';
        $match = array();
        if(preg_match($pattern, $atagstr, $match)){
            $pid = url_to_postid($match[0]);
            if($pid > 0){
                return( array($pid => get_the_title($pid)));
            }
        }
        return false;
    }

URLが内部リンクならリンク先記事の Post ID取得

次に取得したURLがサイト内の記事であるかを調べ、そのポストIDを取得します

WordPress の関数 url_to_postid を使えば、URLから記事のポストIDが取得出来ます

URLがサイト内の記事ならば、Post ID の番号が、それ以外は、0となります

url_to_postid 関数内では、正規表現を使い様々なマッチングを行なってポストIDを取得しています。ポストIDを取得するだけでもかなり複雑な処理をしているようです

Post ID から様々な情報を取得

post ID が取得出来れば、あとはこのIDをを使っていろいろな情報を取得出来るようになります。今回は、デバッグ作業がやりやすいように get_the_title でタイトルを取得して正しい Post ID が取得できたことを確認してみました

Post ID から様々な情報を取得することも出来るので、アイデア次第で何か面白いことが出来るようになるかもしれません (^^)


まとめ記事紹介

search star user home refresh tag chevron-left chevron-right exclamation-triangle calendar comment folder thumb-tack navicon angle-double-up angle-double-down angle-up angle-down quote-left googleplus facebook instagram twitter rss