WordPressでツイートURLが埋め込みに置換されない不具合に対処する

ツイートURLが埋め込みに置換されない不具合に対処

WordPressではTwitterのURLを貼り付ければ
自動的にツイートが取得されページに埋め込まれます。

この機能が何らかの理由で処理されずURLがそのまま表示されてしまう()場合に
埋め込みツイートに置換するコードを考えてみました。

(※テーマでCocoonを使用して外部ブログカードを有効にしている場合はブログカード化してしまう)

この記事は上記現象が発生した方の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. 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&mdash; 有吉弘行 (@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のみが対象です。ツイート以外(リスト等)は対象外です。
  2. 取得したデータを元にURLを埋め込み用の記述に置換します。

    <blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">バルス!</p>&mdash; 有吉弘行 (@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日

  3. TwitterのJavaScript(widgets.js)が埋め込みに変換してくれます。

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>

Tweetを表示

実際はwidgets.jsによって次のようにツイートに変換されます。

読み込めなかった時に表示するテキストの変更

次のコードの部分で変更できます。[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を文字列で表示

ブログカード化されたツイートのスクリーンショット

ツイート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 Codex日本語版によると

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のコードも参考にさせていただきました。

もし改善すべき記述方法があれば教えていただけると助かります。
ここまで読んでくださりありがとうございました!

タイトルとURLをコピーしました