PHPのcurl_multiを使って非同期でカウント数を取得するSNSのシェアボタンを自作してみた

先日、CURLというPHPの関数を使ってシェアカウント付のSNSボタンを自作しましたが、スピードがいまいちだったので、他に高速表示できる方法がないか探してみたところ、cURL関数に非同期で値を取得できるcurl_multiという関数があることを知りました。これを使えば複数のcURLハンドルを非同期で実行できるようです。

こんなSNSをボタンを作る

前回、CURLで自作したSNSボタンと同じボタンをcurl_multiを使って作ります。

これを表示するソース

<?php
//カウント数を取得したい対象URL
$targeturl = 'https://www.apple.com/jp/';
$targeturl = urlencode($targeturl);

//現在のページで取得したいとき
//$url = 'https://'.$_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];

// 並列通信用マルチハンドルを用
$mh = curl_multi_init();

// APIごとにCurl Handleを作る
// Twitter
$ch_twitter = curl_init("http://jsoon.digitiminimi.com/twitter/count.json?url=$targeturl");
curl_setopt($ch_twitter, CURLOPT_RETURNTRANSFER, TRUE);
curl_multi_add_handle($mh, $ch_twitter);

// Facebook
$ch_facebook = curl_init("https://graph.facebook.com/?id=$targeturl");
curl_setopt($ch_facebook, CURLOPT_RETURNTRANSFER, TRUE);
curl_multi_add_handle($mh, $ch_facebook);

// Hatena
$ch_hatena = curl_init("https://b.hatena.ne.jp/entry.count?url=$targeturl");
curl_setopt($ch_hatena, CURLOPT_RETURNTRANSFER, TRUE);
curl_multi_add_handle($mh, $ch_hatena);

// Pocket
$ch_pocket = curl_init("https://widgets.getpocket.com/v1/button?v=1&count=horizontal&url=$targeturl&src=$targeturl");curl_setopt($ch_pocket, CURLOPT_RETURNTRANSFER, TRUE);
curl_multi_add_handle($mh, $ch_pocket);

// 複数の通信を非同期で同時実行
do { curl_multi_exec($mh, $running); } while ( $running );

// APIから戻ってきた値を変数に入れる
$json_twitter  = curl_multi_getcontent($ch_twitter);
$json_facebook = curl_multi_getcontent($ch_facebook);
$count_hatena = curl_multi_getcontent($ch_hatena);
$html_pocket = curl_multi_getcontent($ch_pocket);

//cURLハンドルセットを閉じる
curl_multi_remove_handle($mh, $ch_twitter)
curl_close($ch_twitter);
curl_multi_remove_handle($mh, $ch_facebook);
curl_close($ch_facebook);
curl_multi_remove_handle($mh, $ch_hatena);
curl_close($ch_hatena);
curl_multi_remove_handle($mh, $ch_pocket);
curl_close($ch_pocket);
curl_multi_close($mh);

/*デバッグ用
var_dump($json_twitter)."\n";
var_dump($json_facebook)."\n"
var_dump($count_hatena)."\n";
var_dump($html_pocket)."\n";
*/

//Twitterのカウント数を取り出す
$count_twtter = json_decode($json_twitter, true );
$count_twtter = num_format(strval($count_twtter['count']));

//facebookのカウント数を取り出す
$count_facebook = json_decode($json_facebook, true );
$count_facebook = num_format($count_facebook['share']['share_count']);

//Hatenaのカウント数を取り出す
$count_hatena = num_format($count_hatena);

//Pocketのカウント数を取り出す
//HTML→DOM→XML→JSON→配列に変換
$dom_pocket = new DOMDocument('1.0', 'UTF-8');
$dom_pocket->preserveWhiteSpace = false;
$dom_pocket->loadHTML($html_pocket);
$xmlString_pocket = $dom_pocket->saveXML();
$xmlObject_pocket = simplexml_load_string($xmlString_pocket);
$count_pocket = json_decode(json_encode($xmlObject_pocket), true);
$count_pocket = num_format(strval($count_pocket['body']['div']['a']['span']['em']))
?>

<!DOCTYPE html>
  <html lang="ja" itemscope itemtype="http://schema.org/WebPage">
  <head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1"><br>  <meta http-equiv="x-dns-prefetch-control" content="on"><br>  <link rel="dns-prefetch" href="//cdn.jsdelivr.net">
<style>
  .sns-counter {<
    width: 100%;
  }
  .sns-counter li {
  display: inline-block;
  border-radius: 2px;
  width: 80px;
  padding: 3px;
  margin-left: 3px;
  }
  .sns-counter li span {
  display: inline-block;
  text-align: center;
  width: 70%;
  font-size: 12px;
  font-weight: bold;
  color: #fff;
  }
 .sns-counter li i {
 display: inline-block;
  text-align: center;
  width: 30%;
  color: #fff;
  font-size: 14px;
  position: relative;
  top: 1px;
  }
 .sns-counter .btn-tw {
  background-color: #55ACEE;
  }
 .sns-counter .btn-fb {
  background-color: #3A5795;
  }
 .sns-counter .btn-hb {
  background-color: #008CE1;
  }
 .sns-counter .btn-pk {
  background-color: #ef4357;
  }
 .sns-counter .btn-line {
  background-color: #1dcd00;
  }
  .sns-counter .btn-total {
  background-color: #000;
  }
 .btn-hb:hover,.btn-tw:hover,.btn-fb:hover,.btn-pk:hover,.btn-line:hover,.btn-total:hover {
  background-color: #ccc;
  }<
  i.fa-hatena:before {
  content: "B!";
  font-family: Verdana;
  font-weight: bold;
  }
</style>
</head>
<body>
<div>
<ul class="sns-counter" title="<?php echo $targeturl; ?>"><!--
  --><li class="btn-tw">
  <a href="//twitter.com/intent/tweet?url=<?php echo $targeturl; ?>" target="_blank">
  <i class="fab fa-twitter"></i><span><?php echo $count_twtter; ?></span>
  </a><br>  </li><!--
  --><li class="btn-fb">
  <a href="//www.facebook.com/sharer/sharer.php?u=<?php echo $targeturl; ?>" onclick="window.open('https://www.facebook.com/sharer/sharer.php?u=<?php echo $targeturl; ?>', 'new', 'width=500,height=300');return false;" target="_blank">
  <i class="fab fa-facebook-f"></i><span><?php echo $count_facebook; ?></span>
  </a>
  </li><!--
  --><li class="btn-hb">
  <a href="//b.hatena.ne.jp/entry/<?php echo $targeturl; ?>" target="_blank">
  <i class="fa fa-hatena"></i><span><?php echo $count_hatena; ?></span>
  </a>
  </li><!--
  --><li class="btn-pk">
  <a href="//getpocket.com/edit?url=<?php echo $targeturl; ?>" target="_blank">
  <i class="fab fa-get-pocket"></i><span><?php echo $count_pocket; ?></span>
  </a>
  </li><!--
  --><li class="btn-line">
  <a href="//social-plugins.line.me/lineit/share?url=<?php echo $targeturl; ?>" target="_blank">
  <i class="fab fa-line"></i><span>LINE</span>
  </a>
  </li>
</ul>
</div>
<script>
window.addEventListener("load",function(){
  const css=[ "//cdn.jsdelivr.net/npm/@fortawesome/[email protected]/css/all.min.css" ];
  for(i in css){
   let html=document.createElement("link");
   html.rel="stylesheet";html.href=css[i];
   html.async=true;document.head.appendChild(html);
  }
});
</script>
</body>
</html>

たった0.5秒だけど速い!

前回は平均2秒だったのが今回1.5秒になりました。

期待していたのは1秒以下だったのですが、curl_multiを使った効果はありました。非同期なのはAPIから結果を取得しに行く部分で、表示は非同期ではありません。よってブログのページにこのボタンを埋め込むとこの1.5秒分ほどページ全体の表示を遅くさせる可能性があります。それでも純正のシェアボタンを使うよりは速いと思います。

表示も非同期にしたいときはPHPでカウント数を呼び出す部分をJavascriptから実行させることができれば可能かもしれません。jQueryでできるらしいので今度試してみたいと思います。

2019/01/17追記

jQueryから非同期でPHPを実行する方法をやってみました。

参考サイト

Hugo で構築されています。
テーマ Stack は Jimmy によって設計されています。