【解決】WordPressのコンテンツ埋め込み(oEmbed)が1日で動かなくなる現象

WordPressロゴTwitterカードやYouTubeの動画など、WordPressの記事内にコンテンツ埋め込み(oEmbed)をする仕組みがありますが、これが約1日で普通のURLの文字列になってしまう、つまり記事作成時にはoEmbedが動くけど、翌日、動かなくなる事象に遭遇しました。

ちなみに、下記がYouTubeをoEmbedで挿入した例です。これが、1日後にURLの文字列になってしまいます。

ネットで調べましたが、該当の不具合に対する情報が探せなく、、、困ったときは、WordPressのソースを見れば、ということで、見たら1発でわかりました!

		$attr['discover'] = apply_filters( 'embed_oembed_discover', true );

		// Use oEmbed to get the HTML.
		$html = wp_oembed_get( $url, $attr );

		if ( $post_ID ) {
			if ( $html ) {
				update_post_meta( $post_ID, $cachekey, $html );
				update_post_meta( $post_ID, $cachekey_time, time() );
			} elseif ( ! $cache ) {
				update_post_meta( $post_ID, $cachekey, '{{unknown}}' );
			}

該当する箇所は、上記の309行目で、
なんと、oEmbedの規格に従い関数wp_oembed_getで取得したHTML(←これが埋め込まれる)をpost_metaにキャッシュしてるではありませんか!

しかも、これは、同一のメソッド内「public function shortcode」の上部で、「DAY_IN_SECONDS」(=1日)間はキャッシュされ、それが再利用されるというものです。

なんらプログラムに問題がないように思われますが、実は、utf8なMySQLに、4バイトのutf8の入ったデータをupdate_post_metaで保存できないんですよ。なので、今回の事象は、wp_oembed_getで取得した埋め込まれるHTML内に4バイトのutf8、つまり絵文字が含まれてると発動します(特にTwitterカードとかでは絵文字が使われる率が高いので高確率で発動します)。初回は、なんとかこのキャッシュが迂回されてoEmbedが動いているように見えますが、「DAY_IN_SECONDS」(=1日)後に、保存されてるはずのキャッシュが保存されてなく、oEmbedが動作しなくなるといった具合です。

という訳で、問題の起こったWordPressサイトを調べると、MySQLは「utf8」「utf8mb4」どちらも使えるバージョンですが、たしかにWordPressで使っているMySQLのデータベースが「utf8mb4」ではなく「utf8」だったんですよ。これをMySQLのコマンドで「utf8mb4」に変更したら直りました。

たしかWordPress4.3あたりから、絵文字対応とかいって、データベースが「utf8mb4」が標準になったんですが、このサイトはそれ以前からバージョンアップ、バージョンアップでやってきたWordPressだったんですね。

たしかに今でもWordPressは、MySQLの文字コード「utf8」で動きますが、いろいろな問題を引き起こしますので、「utf8mb4」が必須と考えといた方がよいかもです。