OXY NOTES

SNSのカウントをキャッシュするWordPressのプラグインを作成しました

Step by Step Social Count CacheというWordPressのプラグインを作りました

簡単に言えば、SNSのカウントをキャッシュするプラグインです。

投稿の最終更新日から「1日」「1日~1週間」「1週間以降」の3つの段階で、キャッシュの有効期限を設定することができます。

条件が揃えば、1度キャッシュしたカウントは平均2msで表示が可能になり、実に1000倍も高速に表示することができます

プラグインの公式ページ」からダウンロードするか、プラグインの検索ページで「Step by Step Social Count Cache」と検索していただければ表示されます。

この解説はバージョン1.7で有効です。

1.4以前のバージョンからアップデートした場合、1度プラグインを停止し、再度有効にしてください。

プラグインの特徴と設定方法

カウントをキャッシュするSNSは5種類

具体的にはTwitterFacebookはてなブックマークPocketfeedly5種類です。

(Twitterのカウントはcount.jsoon APIを利用しています。カウントを有効にするには、事前にwidgetoon.js & count.jsoonでサイト登録をする必要があります。未登録だと「-1」と表示されます。)

v1.7更新時追記:Google+は今後カウントを表示しない方針のようです(2017/10/1日現在カウントは取得できません)。そのため今後カウントが復活しない限り、カウント・表示は行いません。

取得するSNSはオプションページで変更することができます。

表示の有無にかかわらず、チェックの入っているSNSのカウントを取得してキャッシュします。実行速度に直結するため、必要のないSNSのチェックは外してください。

キャッシュ期間は3段階

投稿の最終更新日から、「1日」「1日~1週間」「1週間以降」の3つの段階で、キャッシュの有効期限を設定することができます。

それぞれのキャッシュ期間はオプションページで分単位で調整することができます。

FacebookのApp Tokenが必要

Facebookでいいねをカウントするには、App Tokenが必要です。2015年8月現在、最新のAPI 2.4は「Facebookの解説ページ」を参考にApp Tokenを取得してください。
App Tokenはオプションページで設定できるようにしました。

Tokenの値が間違っている場合、設定ページで「FacebookのApp Tokenが誤っています。値を確認してください。」というエラーメッセージが表示されます。

追記

tokenの取得方法の質問を頂いたので解説します。
まずFacebookのDeveloperページでアプリを登録します。アプリのページでApp IDと、App Secretをメモします。

続いてWordPressのfunctions.phpに以下のように記述してアクセスしてみてください。

$app_id = "0000000000000000"; // App ID
$app_secret = "00000000000000000"; // App Secret

$token_url = "https://graph.facebook.com/oauth/access_token?" .
"client_id=" . $app_id .
"&client_secret=" . $app_secret .
"&grant_type=client_credentials";

$access_token = wp_remote_get( $token_url );

var_dump($access_token);

すると「access_token=000000000000|4OsdafgagvadwxVkXdafnHguU(サンプル)」といった値が出力されます。「access_token=」以降がApp Tokenです。

もちろん普通に「https://graph.facebook.com/oauth/access_token?client_id=$app_id&client_secret=$app_secret&grant_type=client_credentials」で$app_id$app_secretを置き換えてブラウザでアクセスしても取得できます。

feedlyでカウントするRSSは自由に設定可能

WordPressは自動でRSS、RSS2、Atomのフィードが書きだされます。一般によく使われているのはRSS2のFeedで、URLは「http://example.com/?feed=rss2」です。
カスタムのパーマリンクを設定している場合は静的URLで「http://example.com/feed/」等になります。

このプラグインでは上記の2つのパターンには対応しますが「パーマリンクは静的だけど、Feedは動的なURLを使いたい。」または「独自に作成したFeedを使用したい。」といった需要もあるようなのでFeedを指定できるようにしました。

キャッシュのプリロードが可能

通常カウントのキャッシュは、ページを読み込んだ時点で作成されます。

6つのSNS全てのカウントを取得すると1~2秒かかります。一覧ページで10ページ表示したとして、初回閲覧時に20秒もかかる計算になります。

そこで「バックグランドで先回りしてカウントをキャッシュしよう」というのがプリロード機能です。

プリロードはWordPressの擬似cronを使っています。投稿のステータスが公開になっているページを、5分間に5ページずつ取得します。
かなりゆったりとしたペースですが、同時接続数の厳しいレンタルサーバ等でも余裕を持って使えるように設定しました。
全ページを網羅すると自動で停止するので、サーバに負荷をかけ続けることもありません。

注意点として、WordPressのcronを停止していると動作しません。
また、プラグインディレクトリにファイルを保存する関係で、プラグインディレクトリに対する書き込み権限が必要です。

プリロードでは1度キャッシュしたページのカウントは再取得しません。キャッシュを全て削除して再取得したい場合はプラグインの設定画面で1度プラグインを停止して、有効にした後にプリロードを実行してください。

APCもしくはAPCuが有効な場合にクエリをキャッシュ

PHPのユーザーキャッシュ機能であるAPCもしくはAPCuを利用して高速化しています。

キャッシュしたカウントをデータベースから取得して表示するのに6msかかります。APCが有効な場合は2msで表示できるようになります。通常の方法でカウントを取得すると、遅い場合は2秒程度かかるので、実に1000倍の速度でカウントを表示することが可能です。


プラグインの使い方

カウントを表示する方法

投稿のキャッシュを全て取得して書き出す方法。
返り値は変数なので、それぞれのSNS名を添え字にしてカウントを出力します。

複数取得する場合はパフォーマンス的にこちらをお勧めします。

<?php
	$socal_count = sbs_get_all();
	echo $socal_count["all"]; // 全てのSNSの合計を返す
	echo $socal_count["twitter"];
	echo $socal_count["facebook"];
	echo $socal_count["google"];
	echo $socal_count["hatena"];
	echo $socal_count["pocket"];
	echo $socal_count["feedly"];
?>

もしくは個別に取得して書き出す方法。

<?php
	echo sbs_get_twitter();
	echo sbs_get_facebook();
	echo sbs_get_google();
	echo sbs_get_hatena();
	echo sbs_get_pocket();
	echo sbs_get_feedly();
?>

カウントの数値を返すだけなので、パラメータ等はありません。

デザインされたボタンを表示する方法

以下の2つのタイプを作成しました。

バルーンタイプ

カウントを上に表示するタイプです。
このサイトでも記事の下に表示しています。

<?php
    $args = array( "hatena", "twitter", "google", "facebook", "pocket", "feedly" );
    sbs_balloon_style( $args );
?>

パラメータ

$args

“hatena”, “twitter”, “google”, “facebook”, “pocket”, “feedly”のいずれかを配列で指定します。指定した順序によって表示順が変更になります。

表示する要素は順序を変えたり

必要な要素だけ選んだり

といったカスタマイズが可能です。

スクエアタイプ

モノトーンの四角でカウントを表示するタイプです。
このタイプもバルーンタイプと同じように要素を並べ替えたり、必要な要素だけ表示する事ができます。

<?php
    $args = array( "hatena", "twitter", "google", "facebook", "pocket", "feedly" );
    sbs_square_style( $args );
?>

パラメータ

$args

“hatena”, “twitter”, “google”, “facebook”, “pocket”, “feedly”のいずれかを配列で指定します。指定した順序によって表示順が変更になります。

カウントの多い投稿のIDを取得する方法

SNSでカウントの多い順に投稿を表示したい」という需要もあるようなので、追加しました。
このブログでも試験的に導入してみました。動作サンプルはサイドバーにある「SNSで人気の投稿」というタグをご覧ください。

<?php
	sbs_get_pp_all( $page, $post_type );
?>

パラメータ

$page

取得する投稿の数を指定する。初期値(省略時):10ページ。

$post_type

取得するポストタイプを指定する。初期値(省略時):post。

戻り値

全てのSNSのカウントを合計して、カウントが多い順に投稿のIDを配列で返す。

デフォルトの使い方

投稿タイプがpostの投稿を、カウントが多い順に10ページ表示する例。

<?php
	$page = 10; // 表示するページ数
	$post_type = 'post'; // 表示するポストタイプ
	$args = array(
		'post_type' => $post_type,
		'posts_per_page' => $page,
		'orderby' => 'post__in',
		'post__in' => sbs_get_pp_all( $page, $post_type ),
	);
?>

<?php $wp_query = new WP_Query( $args ); ?>
<?php while ( $wp_query->have_posts() ) : $wp_query->the_post(); ?>

<!-- ここにタイトルやサムネイルなどを指定する -->
<?php the_title(); ?>

<?php endwhile; ?>
<?php wp_reset_query(); ?><!-- 忘れずにリセットする必要がある -->

サブループの記述方法は、様々ありますがメインクエリを元に戻すことを忘れないようにしてください。
一旦メインクエリを「$temp = $wp_query」等で保存しておいて、wp_reset_query()の代わりに「$wp_query = $temp」で戻すこともできます。

<?php
	$page = 10;
	$post_type = 'post';
	$args = array(
		'post_type' => $post_type,
		'posts_per_page' => $page,
		'orderby' => 'post__in',
		'post__in' => sbs_get_pp_all( $page, $post_type ),
	);
	$temp = $wp_query // 一旦クエリを保存
?>

<?php $wp_query = new WP_Query( $args ); ?>
<?php while ( $wp_query->have_posts() ) : $wp_query->the_post(); ?>

<?php the_title(); ?>

<?php endwhile; ?>
<?php $wp_query = $temp; ?><!-- ここで戻す -->

さらに詳しく知りたい方は「WordPressの新・旧ループからカスタムクエリ・アーカイブまで徹底解説」をご覧ください。

以下のタグを使えば特定のSNSのみのカウント順に投稿IDを取得できます。

<?php
	sbs_get_pp_twitter( $page, $post_type );
	sbs_get_pp_facebook( $page, $post_type );
	sbs_get_pp_google( $page, $post_type );
	sbs_get_pp_hatena( $page, $post_type );
	sbs_get_pp_pocket( $page, $post_type );
?>

バルーン・スクエアタイプのタグはカスタマイズ可能

設定画面でsbs_balloon_style() もしくは sbs_square_style() のタグをカスタマイズすることができます。

PHPのコードは使えないので、「ページのURL」等を指定するには以下のカスタマイズ用のタグを利用してください。

[[url]] ページのURL
[[site_title]] サイト名
[[title]] ページのタイトル
[[count]] それぞれのSNSのカウント数

例)twitterの表示を「Tweet」から「List」に変更

<li class="twitter_count"><div class="bubble"><a href="https://twitter.com/intent/tweet?original_referer=[[url]]&text=[[title]]%20%7C%20[[site_title]]&tw_p=tweetbutton&url=[[url]]&via=[[site_title]]" class="count" target="_blank">Tweet</a></div><a class="bgimage" href="https://twitter.com/intent/tweet?original_referer=[[url]]&text=[[title]]%20%7C%20[[site_title]]&tw_p=tweetbutton&url=[[url]]&via=[[site_title]]" class="count" target="_blank"></a></li>
<li class="twitter_count"><div class="bubble"><a href="https://twitter.com/intent/tweet?original_referer=[[url]]&text=[[title]]%20%7C%20[[site_title]]&tw_p=tweetbutton&url=[[url]]&via=[[site_title]]" class="count" target="_blank">List</a></div><a class="bgimage" href="https://twitter.com/intent/tweet?original_referer=[[url]]&text=[[title]]%20%7C%20[[site_title]]&tw_p=tweetbutton&url=[[url]]&via=[[site_title]]" class="count" target="_blank"></a></li>

バルーンタイプ、スクエアタイプ共に別々のカスタマイズが可能です。

空欄にして設定を保存するとデフォルトの設定がセットされます。


デバッグモードの実装

バージョン1.6より隠し機能としてデバッグモードを実装しました。
sbs-social-count-cache.php1092行目あたり

	$debug_mode = false;

となっているところを

	$debug_mode = ture;

としてください。

するとソース上で以下のようにキャッシュ関連の情報が表示されます。

<!--
SBS Social Count Cacheデバックモード
投稿の最終更新日から:1週間以上経過
サーバのapc cache:有効
apc cache:見つかった
サイトのローカルタイム:2017-04-29 15:24:12
投稿の最終更新日時:2016-05-13 17:23:28
キャッシュの取得時間:2017-04-26 09:26:04
キャッシュの有効期限:2017-05-03 09:26:04
キャッシュの残り時間:324112秒
キャッシュ:期限内
-->

この情報を元にキャッシュが正しくされているか調べることができます。
また、正しく反映されない場合にも原因の追求にご利用ください。


プラグイン作成後記

SNSでの拡散は、ブログだけでなく、ニュースやコーポレートサイトでも欠かせないものになりました。
しかし設置するサイト数も増え、SNSの種類も増えたため、表示が遅くなってしまいました。

サーバの動作を1ms秒単位で調整している身としては、その重さが看過できないものになってきました。
そこでPHPでカウントだけを取得したり、JavaScriptで非同期に取得する方法も試しましたが、納得のできる速度は出ませんでした。

Nginxのリバースプロキシで長めにキャッシュすることでお茶を濁していましたが、これにも問題がありました。
投稿してすぐに拡散された記事の場合、反響を反映するまで時間がかかりました。

キャッシュを長くすると投稿から間もない反響を反映できないキャッシュを短くすると満足な速度が出ない、そんなジレンマを抱えていました。

そこで、記事を作成してからの経過時間でキャッシュ期間を変更するという仕組みを思いつきました。

個人的に運用してみて経過も良好だったので「練習がてらプラグインを作成してみよう」というのがきっかけです。
実際に公開するにあたり、タグが被らないように前置詞を付けたり、アンインストール時に作成したデータベースやオプションを削除したりと、個人用では必要のなかった調整に、なかなか骨が折れました。
プラグインの作成より、公開するための作業のほうがずっと大変でしたw

もしよかったら動作テストを兼ねて利用してみてください。
フィードバックもいただけたら嬉しいです。

純日本語プラグイン

日本人による、日本のブログ向けのプラグインなので「プラグインページ」の表記を日本語にしました。

作者の拙い英語で日本人も外国人も戸惑わせるくらいなら、きっぱりと日本向けにしたほうが混乱も少なかろうという判断です。

考えてみればCodexやプラグインの国際化が進んだ昨今、なぜプラグインページだけが英語のみなのか少し疑問です。


最後に、初めてのプラグインで不具合が何度も発生したにもかかわらず、根気強く協力していただいたキャリコさん、ありがとうございました。