WordPressでオリジナルのブログカードを表示するために当サイトでおこなった方法をまとめました。プラグインは使わず、functions.phpを編集しています。

WordPress以外のブログサービスに引っ越したときでもリンク情報を維持できるよう、ショートコードやURL直打ちではなく<span>でくくった<a>タグをブログカード化するという、おそらくちょっとレアなやり方です。

WordPress特有の表記法はなるべく控えめにしておきたい! という、とても個人的な理由からチャレンジしました。

  • フィルターフック
  • 正規表現
  • サイトのOGP情報
  • Googleの非公開favicon用API

などを使って実装しています。

オリジナルのブログカードを実装した経緯

WordPress環境でブログカードを表示するには、(おそらく)以下の4つのやり方があります。

  1. 「はてな」や「Embedly」などの外部サービスを利用する
  2. WordPressオリジナルのものを利用する
  3. WordPressのプラグインを利用する
  4. 自前で実装する

楽なのは、上にあげた方法 1〜3 のいずれかですが、今後、別のブログサービスへ引っ越しすることが出てきたとしても、記事を修正する手間をなるべく少なくしたいと思い、オリジナルのブログカードを実装しました。

FC2ブログから引っ越してきたときの経験から、この発想にいたりました。
FC2ブログ(無料プラン)からWordPressへ全記事を移行した方法: パーマリンク使用、リダイレクト設定あり

外部サービスは、コードが長め

ブログカードを表示させるための外部サービスとしては、「はてなブログ」や「Embedly1」が有名です。これらのサービスでは、本文中に決まった形のHTMLコードを記述することで、ブログカードが表示されます。

<!-- たとえば、「Embedly」のブログカード埋め込み用コードの例 -->
<blockquote class="embedly-card"><h4><a href="https://cosybench.com/made-the-original-wordpress-theme/">【WP】オリジナルテーマに衣替え: 使ったフリー素材や参考にしたサイト集 - 日なたの縁台</a></h4><p>去年の春からWordPress環境でのブログ運営を始め、それ以来ずっと「 Simplicity 」をテーマとして使ってきたのですが、3日前から、自作テーマに衣替えしました。 2018年、装いも新たに。 去年の秋頃からコツコツと作り始め、ようやく形になったので、めでたくお目見えです。 今回オリジナルテーマを作るにあたり、色々なサイトにお世話になったので、お礼も兼ねてまとめてみました。 ...</p></blockquote>
<script async src="//cdn.embedly.com/widgets/platform.js" charset="UTF-8"></script>

これらのサービスは手軽で、WordPress以外の環境に移行しても記事を修正する必要がないので、「今後、別のブログサービスへ引っ越しすることが出てきたときに…」という観点からは、実は、、、OKです!

ですが、

  • 定型のコードは長いので、ちょっとした書き直しのときに、読み返しにくい?
  • 「はてな」のサービスを使った場合は、リンク参照元が「hatena」になってしまう
  • 外部サービスなので、万が一にも、仕様が変わり影響が出ないとは限らない

などから、自分で実装できなかったときの次善の策として、ひとまずは取っておくことにしました。

WPオリジナルのものは、ただの文字列に

WordPressもVer 4.4から、記事中にURLを書くとブログカードに変換されるようになりました。

たとえば、投稿画面で以下のように記事のURLを書いておくと、

WPの投稿画面でURLを書くと

このように(↓)表示されます。

【WP】オリジナルテーマ作成に欠かせなかった便利ツール集のまとめ

ただし、WordPress環境でない外部サイトについては、

  • oEmbed形式に対応している
  • WordPress公式からホワイトリスト登録されている

ものでなくてはブログカードとして表示されません。対応していない場合は、記述したURLがそのまま表示されます。(※ホワイトリストは編集可能)

この方法は、URLを記述するだけなので簡単ですが、WordPress以外の環境に移行すると、ただの文字列になり、リンクではなくなってしまいます。また対応していないサイトもあるので、ベストの選択ではないと思いました。

▼ WordPressのブログカード対応について
WordPress 4.4 から 他サイトの記事を引用埋め込みできるようになった「Embed」 – ねんでぶろぐ

▼ 埋め込みに対応している外部サイト
Embed(英語版 最新情報 「Okay, So What Sites Can I Embed From?」参照)
oEmbed – WordPress Codex 日本語版(情報が少し古い)

WPプラグインを利用した場合も、ただのテキストに

もう一つが、プラグインの利用です。

「WordPress ブログカード プラグイン」などで検索すると、最近は、「Pz-LinkCard」というプラグインの紹介記事がよくヒットします。日本の方が開発しているので、情報を得やすいプラグインです。

ですが、「Pz-LinkCard」などのプラグインは、WordPress環境で使いやすいよう、ショートコード([shortcode])でブログカード用のリンク情報を記述します。なので、やはり、WordPress以外の環境に移行すると、ただの文字列になってしまいます。

WP以外の環境に移行しても、リンク情報を維持したい

そんな訳で、当面はWordPress環境でサイトの運営をしていくつもりではあるのですが、

今後もし、他のブログサービスに運用環境を移行した場合でも、記事中のリンク情報をなるべくそのまま使える状態にしておきたい! との動機から、オリジナルブログカードの実装にチャレンジしました。

目指した仕様

他のブログサービスに移転したときにも、ブログカード部分の文字列を修正せずに済むよう、

  • 投稿画面では、普通に<a>タグ使い、リンク情報を記述
  • 記事が公開されるタイミングで、ブログカードになる

ようにします。より具体的に書くと、

▼ 投稿画面でこのように書いておけば

<span class="bcard ilink"><a href="https://example.com/blogcard/" title="オリジナルブログカードを実装したい" target="_blank">オリジナルブログカードを実装したい</a></span>

▼ こうなる
オリジナルブログカードの完成イメージ

ようにしたい! ということです。

<span>でくくることで、ブログカードにしない他のリンク情報とも区別できますし、WordPress以外の環境になっても、CSSをいじることで::before::afterアイコンを付けたりできます。

実装

そんな訳で前置きが長くなりましたが、以下、当サイトがとった実装方法です。

functions.php

まず、オリジナル関数を定義します(original_blog_card())。この関数は、「本文中からブログカードにしたい部分を検索 → 記事公開 → ブログカード用のHTMLコードに置換」という処理を行っています。

関数名は任意のもので構いません。
また配布されているテーマを利用している場合は、子テーマのfunctions.phpを編集します。(ない場合は、新しく作成)
そして、関数名が親テーマで定義されているものと被っていないかの確認もかねて、function hoge(){~~~}if (!function_exists('hoge'){~~~}で囲うのがオススメです。
PHP: function_exists – Manual

/**
* オリジナルブログカード表示用の関数
* @param   string   the_content(投稿本文)
* @return  string   the_content(ブログカード用に置換された投稿本文)
**/
if (!function_exists('original_blog_card')){ //子テーマを使っている場合、推奨
// @link http://php.net/manual/ja/function.function-exists.php
function original_blog_card ( $the_content ) {

/*== 解説(1) 参照 ==*/
    // ブログカードに変換する箇所を取得するための正規表現
    // <span class="bcard ilink(またはelink)"><a href="~" title="~" (target="_blank")>xxx</a></span>という文字列パターンを文中から探してもらう
    $pattern = '#<span class="bcard (i|e)link"><a href="((?:https?|ftp)(?:://.+?))" title="(.*?)"(?: target="_blank")?>(.*?)</a></span>#';

    // 投稿本文内の一致する文字列をすべて取得する
    preg_match_all($pattern, $the_content, $card_matches, PREG_SET_ORDER);
        /* 以下のように$card_matchesに格納される
        array(N) {
          [M]=>
              array(5) {
                [0]=> mathced content
                [1]=> $1 : Link type (i|e)
                [2]=> $2 : URL
                [3]=> $3 : Title
                [4]=> $4 : Link Text
              }
        }*/

/*== 解説(2) 参照 ==*/
    if ($card_matches) { //変換する要素があれば
        foreach ($card_matches as $m) {
                $url = $m[2];
                $domain = parse_url($url, PHP_URL_HOST);  //PHP関数でドメインを切り出す

            if ($m[1] == 'i') : //内部リンクの場合(internal)
                $id = url_to_postid( $url ); //IDを取得(URLから投稿ID変換)
                    if ( !$id ) return ''; //IDを取得できない場合は脱出

                //global $postの利用
                $post_data      = get_post($id);
                $title          = $post_data->post_title; //タイトルの取得
                $excerpt        = get_post_meta($id, 'meta_description', true) ? get_post_meta($id,'meta_description',true) : get_the_excerpt($id); //抜粋の取得
                if ( has_post_thumbnail($id) ): //thumbnail画像のHTML取得
                    $src    = get_the_post_thumbnail( $id, 'cb100-crop', array( 'alt' => $title ) ); //thumbnail画像('cb100-crop')は、add_image_size()関数でfunctions.php内で別途、独自に設定したものを使用. @link https://developer.wordpress.org/reference/functions/add_image_size/
                    $thumbnail  = '<div class="blog-card-thumbnail">' .$src. '</div>';
                else: //thumbnailがない場合
                    $src        = get_template_directory_uri() . '/images/ogp_no_image.jpg';
                    $thumbnail  = '<div class="blog-card-thumbnail"><img src="' .$src. '"></div>';
                endif;
                $date           = date('Y-m-d H:i', strtotime($post_data->post_date)); //投稿日の取得
                $target         =''; //target="_blank"の設定


/*== 解説(3) 参照 ==*/
            elseif ($m[1] == 'e') : //外部リンクの場合(external)
                //設定されているOGP情報を利用する
                //@link https://github.com/scottmac/opengraph
                require_once('opengraph.php');

                $graph = OpenGraph::fetch($url);
                if ($graph) : //OGP情報が設定されていたら
                    $title = $graph->title ? $graph->title : $m[3];
                    $excerpt = $graph->description ? $graph->description : '';
                    // 【★追記:2018/08/05★】
                    // 外部サイトのOGP画像を自サーバーに保存してから使えるようにしました。
                    // 詳細は、https://cosybench.com/save-ogp-images-with-wpfilesystem/ をご覧ください。
                    // 新verでは、「$src = $graph->image ? cb_fetch_ogp_image_file($graph->image) : get_template_directory_uri().'/images/ogp_no_image.jpg';」 になります。
                    $src = $graph->image ? $graph->image : get_template_directory_uri().'/images/ogp_no_image.jpg';
                    $thumbnail = '<div class="blog-card-thumbnail"><img src="' .$src. '"alt="' .$m[3]. '"></div>';
                endif;
                $date       = ''; //外部リンクは投稿日なし
                $target     = 'target="_blank"'; //外部リンクは新規タブで

            endif;

/*== 解説(4) 参照 ==*/
            //ブログカードのタグを設定
            $title_tag   = '<p class="blog-card-title subheading p_t">' .$title. '</p>';
            $excerpt_tag = '<p class="blog-card-excerpt p_t caption">' .$excerpt. '</p>';
            $favicon_tag = '<div class="blog-card-site-info"><span class="blog-card-favicon"><img src="//www.google.com/s2/favicons?domain=' .$domain. '" class="blog-card-favicon-img" alt="" />' .$domain. '</span></div>'; //GoogleファビコンAPI利用
            $date_tag    = '<div class="blog-card-date">' .$date. '</div>';
            //タグを合体
            $whole_tag = '<div class="blog-card"><a href="' .$url. '">' .$thumbnail. '<div class="blog-card-body">' .$title_tag .$excerpt_tag. '</div>' . '<div class="blog-card-footer caption s_t">' .$favicon_tag .$date_tag. '</div>' .'</a></div>';

            //置換する
            $the_content = preg_replace('{'.preg_quote($m[0]).'}', $whole_tag, $the_content);
        }
    }

    return $the_content;

}
} //子テーマを使っている場合、推奨。if (!function_exists('original_blog_card')){を閉じる。

/*== 解説(5) 参照 ==*/
add_filter('the_content', 'original_blog_card'); //フィルターフック

以下、コードのざっくり解説です。

コードの解説(1)

まず、正規表現を使い、preg_match_all()関数で、ブログカードに変換したい部分を探し出し、配列$card_matchesに格納させています。

正規表現を書くのが初めてだったので、ここに一番時間を取られました。
「正規表現 preg_match URL」などで検索して、コードをコピペしてみるのですが、上手く動かないことが多く…。(たとえば、URLやtitleに日本語が含まれているとNGだったり、そもそもエラーが出て動かなかったり、など)

そんなとき、ふと脳裏をよぎったのが、いつもお世話になっている「Markdown記法」でした。

私は記事の投稿に、有名プラグイン「Jetpack」で提供されている「Markdown記法による投稿機能」を使っているのですが、「Markdown記法では、リンクの解釈に言語は関係しない」 → 「それを参考にすればイケるのではないか!」 と思いつき、コードをのぞいて試してみたところ……できました!

そして、この$pattern部分の正規表現をいじる(簡単にclass名を変えるところから、正規表現を自前で用意する応用編まで)ことで、ブログカードに変換させるための任意の文字列を独自に設定することができます。

// 今回定義している正規表現
$pattern = '#<span class="bcard (i|e)link"><a href="((?:https?|ftp)(?:://.+?))" title="(.*?)"(?: target="_blank")?>(.*?)</a></span>#';
▼ 正規表現関連
PHP: preg_match_all – Manual
正規表現チェッカー PHP: preg_match() / JavaScript: match()

▼ Markdownのコードで参考にしたのは、sub _DoAnchors {} 部分。
Daring Fireball: Markdown

▼ 「Jetpack」で使えるMarkdown記法についてまとめた当サイトの記事
【WP】 Markdownで記事投稿: 「Jetpack」で使える記法一覧 | 日なたの縁台

コードの解説(2)

つづいて、$card_mathcesに格納された情報から、内部リンクについて、ブログカードの表示に必要な情報(タイトル、サムネイル画像、抜粋など)を取得します。

三項演算子(?!)を使い、すっきりと記述。この部分では、フリーのWPテーマ「Simplicity」を使っていたときに設定されていたカスタムフィールド値も、一部再利用しています。

//三項演算子(?!)
//式1がTRUEなら式2を、FALSEなら式3の値が$変数に代入される
$変数 = (式1) ? (式2) : (式3) 
▼ ページやや下の方に「三項演算子」の項目あり
PHP: 比較演算子 – Manual

コードの解説(3)

こちらでは、外部リンクに関する処理をしています。

外部リンクは、内部リンクと違い、サムネイル画像などあらたに取得が必要な情報がでてきます。
そこで利用するのが、サイトに設定されているOGP情報(The Open Graph protocol)です。

OGP情報へのアクセスには、以下のオープンソースコード(PHP関数)を使っています。

▼ opengraph.php の公式ページ(プログラム本体)

使い方は、以下のとおりです。

下準備として、上のリンクからopengraph.phpのコードをダウンロードまたはコピペしてopengraph.phpというファイル名で保存し、FTPソフトなどを使って使用しているWordPressのテーマフォルダにアップロードしておきます。

//functions.phpと同じフォルダにopengraph.phpをアップロードしておき、それを読み込む
require_once('opengraph.php');

//OGP情報をURLから取得する
$graph = OpenGraph::fetch($url);

if ($graph) : //OGP情報が設定されていたら
   /* 必要な情報を取得する */
endif;

元のopengraph.phpをForkして改善されている方も多くいらっしゃるので、そちらのコードを利用してみるのもありだと思います。
私は最近こちらの方のコードに変えてみました。
GitHub – AramZS/opengraph: Helper class for accessing the OpenGraph Protocol

ただし、コードを見ていただくと分かるように、このプログラムを利用しただけでは、外部サイトが提供している画像ファイルへの直リンクになってしまいます。なので、近いうちに、外部サイトのOGP画像を自分のサーバーに保存してから利用する形にしたいと思い、やり方を調べ中です。(WP_Filesystemを使うらしいことまでは、薄っすら把握)

【★追記:2018/08/05★】
できました。新しく関数をつくり、それに合わせて一箇所だけコードを変更しています。
詳細は以下の記事をご覧ください。
【WP】外部リンクをプラグインなしでブログカード化する方法: WP_Filesystem() を使います

コードの解説(4)

最後に、ここまでで取得したデータから、文字列結合演算子(.)を使って、ブログカード表示用のHTMLコードにゴリゴリまとめています。

classなどは、以下のような構造に分けています。この辺りの命名や構造はお好みで。

▼ ブログカードのclass名構造
オリジナルブログカードのclass設定

その後、preg_replace()で、該当部分のHTMLコードを、ブログカード用のコードに置き換えています。

コードの解説(5)

そして最後に、add_filter()(フィルターフック)を使って、データベースから記事の内容を取得し、それがスクリーン上に公開されるタイミングで関数を実行させることで、記事の書き換えを行わせています。

私も、WordPressを使いはじめてしばらくは、この「フィルターフック」のイメージがよくつかめなかったのですが、実際に使ってみることで、なんとな〜く分かってきたような気がします。

▼ フィルターフックについて
the_content | Hook | WordPress Developer Resources

▼ フィルターフックについては、以下の記事が、具体的かつ面白く、イメージがつきやすくて、オススメ(笑)
WordPressのフィルターフックで投稿を全て夢オチにする方法と、真面目な使い方 | NeGiMeMo.net

CSSファイル

あとは、CSSの設定です。現時点(2018/04)では、以下のようにしています。枠線をもう少し濃い色にしてもいいかな?と、思案中。

【追記: 2019/12/23】
現在使用しているブログカードのスタイル(CSS)を、別記事にまとめました。以下のコードは旧タイプのデザインです。
【WP】内部・外部リンク用ブログカードのデザインを更新しました

.blog-card {
    width: 600px;
    max-width: 100%;
    padding: 1em;
    margin: 1em 0;
    border: 1px solid #e0e0e0;
}

.blog-card a:link {
    text-decoration: none;
    color: inherit;
}

.blog-card:hover {
    background-color: #fafafa;
}

    .blog-card-thumbnail {
        position: relative;
        float: right;
        width: 100px;
        height: 100px;
    }

        .blog-card-thumbnail img {
            position: absolute;
            top: 50%;
            left: 50%;
            -webkit-transform: translate(-50%, -50%);
                    transform: translate(-50%, -50%);
            max-width: 100%;
            height: auto;
        }

    .blog-card-body {
        margin-right: 110px;
    }

        .blog-card-title {
            margin-top: 0;
            font-weight: bold;
        }

    .blog-card-favicon {
        float: left;
    }

        .blog-card-favicon img {
            vertical-align: middle;
            margin-right: .25em;
        }

    .blog-card-date {
        float: right;
    }

        .blog-card-body:after,
        .blog-card-footer:after {
            content: "";
            display: table;
            clear: both;
        }

完成形

以上の内容を実装すると、たとえば投稿画面で以下のようなHTMLコードを入力しておけば、

<span class="bcard ilink"><a href="https://cosybench.com/use-highlightjs-and-small-modification/" title="【WP】必要なページだけにhighlight.js を読み込ませるカスタマイズ方法 - 日なたの縁台">【WP】必要なページだけにhighlight.js を読み込ませるカスタマイズ方法 - 日なたの縁台</a></span>

このようなブログカードに変換されます。

まとめ

以上になります。

なるべく記事中に、ただの文字列になってしまう「URLの直書き」や「ショートコード」を使いたくない…!という動機から、オリジナルブログカードを実装することにしましたが、疲れました(身もふたもない、笑)

また実際に今回実装したブログカード変換用の文字列(<span class=bcard ilink>XXX</span>)を投稿画面に記述するときには、Google Chromeの拡張ツール「Create Link」を使っています。

ただ記事中でもふれましたが、外部サイトのリンクについては、現在の仕様では、OGP情報で設定されている画像への直リンクになってしまうので、ブログカード化しないようにしています。
外部サイトの画像を取得して、自分のサーバー、データベースへ保存するというやり方も出来るようですが、ちょっとまだうまく出来ていないので、それが完成した暁にはまた、記事を更新したいと思っています。

外部リンクのブログカード化にともなうアイキャッチ(OGP)画像への直リンク問題、解決しました。詳しくは「追記(1)」をご覧ください。

Googleで検索をかけてみても、なかなか、「自分でオリジナルの関数を書いて、URLの直書きやショートコードでないものから、ブログカードを表示させる」という方法を取っている記事を見つけられなかったので、まとめてみました。

かなりニーズは限られそうですが、どなたかのお役に立てば幸いです。(そしてお役に立てましたら、シェア or 引用していただけると嬉しいです)

追記(1): 外部リンクにも完全対応(2018/08/05)

「コードの解説(3)」部分にも追記しましたが、外部サイトのアイキャッチ(OGP)画像を自分のサーバーに保存してから利用するカスタマイズ、できました。これで、外部サイトのブログカード化も問題なくできるようになりました。

微修正が必要となるコードについての詳細は、以下の記事をご覧ください。

追記(2): ブログカードのスタイル(CSS)を更新(2019/12/23)

ブログカードのデザインを変更しました。
現在表示されているブログカードのCSSは、以下の記事にあらためてまとめました。classの構造も一部変更したので、ブログカードのHTMLを出力するoriginal_blog_card関数も一部修正して載せてあります。

追記(3): ブログカードの表示速度を改善(2020/10/08)

ここまでで紹介してきたコードでは、ページの読み込みに時間がかかっていたため、表示速度を上げるための対策を施しました。
大枠の流れは変わりませんが、コードを読み込ませるタイミングを調整したり、画像の表示にキャッシュを利用する機能などを追加しました。

最新のコードは▼の記事にすべて載せましたので、どうぞご覧ください。


  1. Embedlyは、会員登録すると、表示スタイルの選択肢が増えるそうです。