WordPressではTwitterのURLを貼り付ければ
自動的にツイートが取得されページに埋め込まれます。
この機能が何らかの理由で処理されずURLがそのまま表示されてしまう(※)場合に
埋め込みツイートに置換するコードを考えてみました。
(※テーマでCocoonを使用して外部ブログカードを有効にしている場合はブログカード化してしまう)
functions.phpにコードを追記する
functions.phpは編集前に必ずバックアップを取って保存してください。編集後エラーが出た場合バックアップファイルを元に復元してください。
以下のコードを子テーマのfunctions.phpにコピペするだけです。
動作の詳細が知りたい方は解説等ご覧ください。
//Twitterアドレスを置換する function change_twitter_url( $content ){ //jsonデータが読み取れなかった時に表示するhtml $tweet = '<blockquote class="twitter-tweet" data-width="550" data-dnt="true"><a href="[url]">Tweetを表示</a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>'; //ツイートURLの行を取得 $res = preg_match_all( '/^(<p>)?(https?:\/\/twitter\.com\/\w{1,15}\/status(es)?\/\d+)(<br *\/?>|<\/p>)?$/im', $content,$m); //オプション設定 $options = array( CURLOPT_SSL_VERIFYPEER => false, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5 //タイムアウト(秒) ); //マッチしたURLをループして置換 foreach ($m[0] as $match) { //ツイートURL $url = strip_tags($match); //json取得 $json_url = 'https://publish.twitter.com/oembed?maxwidth=550&dnt=true&url='. urlencode($url); $ch = curl_init($json_url); curl_setopt_array($ch, $options); $json = curl_exec($ch); $arr = json_decode($json, true); curl_close($ch); if ($arr === NULL) { //json取得失敗 $html = preg_replace('/\[url\]/', $url, $tweet); } else { //json取得成功 $html = html_entity_decode($arr['html']); } //本文中のURLを置換 $content = preg_replace('{^'.preg_quote($match, '{}').'}im', $html , $content, 1); } return $content; } add_filter('the_content','change_twitter_url',10);
JSONデータを取得しない簡易版
上記コードはWordPressが置換しなかったURLが存在するページを表示する度にJSONデータを取得します。
もしかすると実行対象やPV数が多かった場合に処理が重くなるかもしれません。
javascriptがオンならどうせwidgets.jsが置換してくれるので、ツイート内容が引用されなくても問題ない!という場合は以下のコードを使用すればJSONデータを取得しません。
//Twitterアドレスを置換する(簡易版) function change_twitter_url( $content ){ //表示するhtml $tweet= '<blockquote class="twitter-tweet" data-width="550" data-dnt="true"><a href="[url]">Tweetを表示</a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>'; //ツイートURLの行を取得 $res = preg_match_all( '/^(<p>)?(https?:\/\/twitter\.com\/\w{1,15}\/status(es)?\/\d+)(<br *\/?>|<\/p>)?$/im', $content,$m); //マッチしたURLをループして置換 foreach ($m[0] as $match) { //ツイートURL $url = strip_tags($match); //[url]を置換 $html = preg_replace('/\[url\]/', $url, $tweet); //本文中のURLを置換 $content = preg_replace('{^'.preg_quote($match, '{}').'}im', $html , $content, 1); } //置換後の本文を返す return $content; } add_filter('the_content','change_twitter_url',10);
修正履歴
2019-01-07:
- JSONの取得方法を変更(file_get_contents→curl/タイムアウトの設定等)
- 2つ目以降のツイートでJSON取得失敗時に1つ前の取得成功ツイートが表示される不備を修正
- マッチするツイートURLをWordPressの処理に近づけました。
修正前
//Twitterアドレスを置換する function change_twitter_url( $content ){ //jsonデータが読み取れなかった時に表示するhtml $html= '<blockquote class="twitter-tweet" data-width="550" data-dnt="true"><a href="[url]">Tweetを表示</a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>'; //ツイートURLの行を取得 $res = preg_match_all( '/^(<p>)?(https?:\/\/twitter\.com\/\w{1,15}\/status(es)?\/\d+)(?!.*<br *\/?>).*?(<\/p>)?/im', $content,$m); //マッチしたURLをループして置換 foreach ($m[0] as $match) { //ツイートURL $url = strip_tags($match); //json取得 $json_url = 'https://publish.twitter.com/oembed?maxwidth=550&dnt=true&url='. urlencode($url); $json = file_get_contents($json_url); $json = mb_convert_encoding($json, 'UTF8', 'ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN'); $arr = json_decode($json, true); if ($arr === NULL) { //json取得失敗 $html = preg_replace('/\[url\]/', $url, $html); } else { //json取得成功 $html = html_entity_decode($arr['html']); } //本文中のURLを置換 $content = preg_replace('{^'.preg_quote($match, '{}').'}im', $html , $content, 1); } //置換後の本文を返す return $content; } add_filter('the_content','change_twitter_url',10);
修正後
//Twitterアドレスを置換する function change_twitter_url( $content ){ //jsonデータが読み取れなかった時に表示するhtml $tweet = '<blockquote class="twitter-tweet" data-width="550" data-dnt="true"><a href="[url]">Tweetを表示</a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>'; //ツイートURLの行を取得 $res = preg_match_all( '/^(<p>)?(https?:\/\/twitter\.com\/\w{1,15}\/status(es)?\/\d+)(<br *\/?>|<\/p>)?$/im', $content,$m); //オプション設定 $options = array( CURLOPT_SSL_VERIFYPEER => false, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5 //タイムアウト(秒) ); //マッチしたURLをループして置換 foreach ($m[0] as $match) { //ツイートURL $url = strip_tags($match); //json取得 $json_url = 'https://publish.twitter.com/oembed?maxwidth=550&dnt=true&url='. urlencode($url); $ch = curl_init($json_url); curl_setopt_array($ch, $options); $json = curl_exec($ch); $arr = json_decode($json, true); curl_close($ch); if ($arr === NULL) { //json取得失敗 $html = preg_replace('/\[url\]/', $url, $tweet); } else { //json取得成功 $html = html_entity_decode($arr['html']); } //本文中のURLを置換 $content = preg_replace('{^'.preg_quote($match, '{}').'}im', $html , $content, 1); } return $content; } add_filter('the_content','change_twitter_url',10);
2018-12-18 02:49(修正): JSON取得失敗時のHTMLにも引数追加で付加される独自データ属性を追加
修正前
$html= '<blockquote class="twitter-tweet"><a href="[url]">Tweetを表示</a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>';
修正後
$html= '<blockquote class="twitter-tweet" data-width="550" data-dnt="true"><a href="[url]">Tweetを表示</a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>';
2018-12-18 01:08(修正): 横幅を制御する引数等を追加しました。
修正前
$json_url = 'https://publish.twitter.com/oembed?url='. urlencode($url);
修正後
$json_url = 'https://publish.twitter.com/oembed?maxwidth=550&dnt=true&url='. urlencode($url);
2018-12-17 23:01(修正): ハイパーリンクのURLも置換する設定になっていたので修正しました。
修正前
$res = preg_match_all( '/^(<p>)?(<a[^>]+?>)?(https?:\/\/twitter\.com\/\w{1,15}\/status(es)?\/\d+)(<\/a>)?(?!.*<br *\/?>).*?(<\/p>)?/im', $content,$m);
修正後
$res = preg_match_all( '/^(<p>)?(https?:\/\/twitter\.com\/\w{1,15}\/status(es)?\/\d+)(?!.*<br *\/?>).*?(<\/p>)?/im', $content,$m);
コードの動作解説
本文が表示される時に置換が実行されるのでデータベースが書き換えられる事はありません。
動作の流れとしては次のようになっています。
1行のツイートURLがあればそれを元にツイート詳細のJSONデータを取得します。
https://twitter.com/ariyoshihiroiki/status/1074322424257114118
{"url":"https:\/\/twitter.com\/ariyoshihiroiki\/status\/1074322424257114118","author_name":"有吉弘行","author_url":"https:\/\/twitter.com\/ariyoshihiroiki","html":"\u003Cblockquote class=\"twitter-tweet\"\u003E\u003Cp lang=\"ja\" dir=\"ltr\"\u003Eバルス!\u003C\/p\u003E— 有吉弘行 (@ariyoshihiroiki) \u003Ca href=\"https:\/\/twitter.com\/ariyoshihiroiki\/status\/1074322424257114118?ref_src=twsrc%5Etfw\"\u003EDecember 16, 2018\u003C\/a\u003E\u003C\/blockquote\u003E\n\u003Cscript async src=\"https:\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"\u003E\u003C\/script\u003E\n","width":550,"height":null,"type":"rich","cache_age":"3153600000","provider_name":"Twitter","provider_url":"https:\/\/twitter.com","version":"1.0"}
WordPressがTwitterURLを埋め込みに置換した後に実行されるので、
置換がされなかったURLのみが対象です。ツイート以外(リスト等)は対象外です。取得したデータを元にURLを埋め込み用の記述に置換します。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">バルス!</p>— 有吉弘行 (@ariyoshihiroiki) <a href="https://twitter.com/ariyoshihiroiki/status/1074322424257114118?ref_src=twsrc%5Etfw">2018年12月16日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
バルス!
— 有吉弘行 (@ariyoshihiroiki) 2018年12月16日
TwitterのJavaScript(widgets.js)が埋め込みに変換してくれます。
バルス!
— 有吉弘行 (@ariyoshihiroiki) 2018年12月16日
widgets.jsの動作について
まず、ツイートがどのように埋め込まれるか。
[このツイートをサイトに埋め込む]のコードを見ると引用タグに囲まれたツイート内容とwidgets.jsファイルを読み込むコードである事がわかります。
widgets.jsはclass="twitter-tweet"
が付与された<blockquote>
タグ内にある<a>
タグのURLを取得して埋め込みが表示される仕組みなので、引用にツイートURLのハイパーリンクさえあれば勝手に埋め込みに変換してくれます。
ツイートの詳細が読み込めなかった時(JSONデータ取得失敗)
JSONデータが取得出来なかった場合にもツイートを表示出来るようにリンクのみ貼られる仕様です。
前述の通りツイートURLさえ貼られていれば作動するので、仕様変更がない限りは問題ないと思います。
<blockquote class="twitter-tweet" data-width="550" data-dnt="true"><a href="https://twitter.com/ariyoshihiroiki/status/1074322424257114118">Tweetを表示</a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
読み込めなかった時に表示するテキストの変更
次のコードの部分で変更できます。[url]
はツイートURLに置換されます。
JavaScriptがオフの場合やwidgets.jsが作動しなかった場合に表示されます。
$tweet = '<blockquote class="twitter-tweet" data-width="550" data-dnt="true"><a href="[url]">Tweetを表示</a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>';
<blockquote class="twitter-tweet"></blockquote>
内に<a href="[url]"></a>
が存在しないとwidgets.jsは作動しません。注意点など
<p>
タグを除去するカスタマイズを行っていると動作しないかもしれません。- このコードはWordPressがoEmbed埋め込みを実行した後に実行されます。実行順は
add_filter('the_content','change_twitter_url',10);
この数値を変える事で変更出来ます(動作は保証出来ません) - このコードでブログが重くなる場合JSONを取得しない簡易版をお試しください。
検証方法
不具合を再現出来ないため、下記コードをfunctions.phpに追記しoEmbed機能を無効にしたテスト環境で検証しました。
remove_filter('the_content', array($wp_embed, 'autoembed'), 8);
参考 WordPressのoEmbed完全キャンセルしちゃいます? | 日記の間 | あかつきのお宿
Cocoonでブログカードに置換されるのを避けURLを文字列で表示
今回と同様の不具合が発生しCocoon(テーマ)で外部ブログカードがオンの場合、該当ツイートがブログカード化されます。※上記コードでブログカード化される問題も解決します。
- JSONデータが取得出来なかった時、URLを文字列のまま表示したい。
という場合はコードを次のように修正するとURLの前に!
が追記され文字列として表示されます。
//$html = preg_replace('/\[url\]/', $url, $tweet); $html = preg_replace('/('.preg_quote($url,'/').')/im', '!$1', $match, 1);
https://twitter.com/ariyoshihiroiki/status/1074322424257114118
参考 URLをブログカード化せずに文字列として表示させる方法 | Cocoon
コードの実行順について
WordPressの置換の後、且つCocoonのブログカード置換の前に実行されます。
実行順は次のコードの数値で調整でき小さい数ほど早く実行されます。
Cocoonは優先度11だったのでそれより前の10を指定しています。
add_filter('the_content','change_twitter_url',10);
不具合の原因は不明
WordPress が URL を使ってメディアを埋め込むことができなかった場合、URL のハイパーリンクが表示されます。
とあるためハイパーリンクになっていなければそもそもWordPressの置換処理が行われていないように見えます。
Twitter DevelopersをみてもAPI制限はないようですが何かエラーがあって取得できなかった可能性も無きにしもあらず…。
埋め込みデータのキャッシュ
一度取得した埋め込みデータのキャッシュに問題がある場合データベースからキャッシュを削除すれば解決するかもしれません。
置換されない原因が不明なので一時的に解決しても再発する可能性もありますが…。
【番外】CocoonでツイートURLを埋め込みにせずブログカード化する
テーマでCocoonを利用しておりツイートURLを埋め込みにせずブログカード化したい場合は一行URLをハイパーリンクにすればブログカードとして表示されます。
自分で見た目をカスタマイズしたい場合など便利かもしれません。
<a href="https://twitter.com/ariyoshihiroiki/status/1074322424257114118">https://twitter.com/ariyoshihiroiki/status/1074322424257114118</a>
https://twitter.com/ariyoshihiroiki/status/1074322424257114118
※サンプルはキャプチャ画像です
参考・あとがき
コード作成にあたって参考にしたサイト。
Cocoonのコードも参考にさせていただきました。
- oEmbed APIの使い方まとめ!URLから埋め込みHTMLタグを作ろう!
- APIなどにfile_get_contents()を使うのはオススメしない理由と代替案 – Qiita
- PHPでJSONデータの取得の仕方 – Qiita
- GET statuses/oembed — Twitter Developers
- GitHub – yhira/cocoon
もし改善すべき記述方法があれば教えていただけると助かります。
ここまで読んでくださりありがとうございました!
テスト環境では置換されるので恐らく問題ないと思います。
詳しい流れはこちらの投稿になります。