Edited at

WordPressをプラグインなしでAMP対応させる試み【その1:格闘編】

WordPressサイトのAMP対応といえば検索でまず引っかかるのはプラグインを使用する方法ですが、プラグインのデフォルト出力のままではいくら「一瞬でできる!」「5分でできる!」といっても売り物にはなりません。そこで、今回勉強がてらスクラッチでAMPページを作ってみました。


WordPress側で対応すること


1) エンドポイントの追加

これによりurlの最後にampがあるとテンプレの切り替えなどを行える。この場合、カスタム投稿タイプarticleのときはテンプレートをamp-single.phpにしている。


functions.php

  define( 'AMP_QUERY_VAR', apply_filters( 'amp_query_var', 'amp' ) );

add_rewrite_endpoint( AMP_QUERY_VAR, EP_PERMALINK );
add_filter( 'template_include', 'amp_page_template', 99 );
function amp_page_template( $template ) {
if( get_query_var( AMP_QUERY_VAR, false ) !== false ) {
if ( is_singular('article') ) {
$template = get_template_directory() . '/amp-single.php';
}
}
return $template;
}


2)headにlinkを追加する

オリジナルの記事の方には<link rel="amphtml" href="AMPページのURL">を付加する。

  echo '<link rel="amphtml" href="'.get_permalink( $id ).'/amp">'."\n";

次にampページ側には<link rel="canonical" href="オリジナルのURL">を付加する。

  echo '<link rel="canonical" href="'.get_permalink( $id ).'">';


3) シングルページテンプレートamp-single.phpの作業

AMPページにおける制限がいろいろあるのでそれに従ってテンプレートを作っていきます。今回はとりあえず動かすことが目的なので最小限です。場合によってはAMP用ヘッダ、AMP用フッタなどを切り分けることもありでしょう。また、SEO周りをプラグインにお任せしているなら、header出力も必要かと思います(個人的にはタイトルやdescriptionの細かな条件別けができないため、SEOプラグインは使っていません)。

AMPページでは既存のタグも独自属性を付けないものがあり、知らないとハマります(ハマりました)。その他ampページにおける注意点をまとめます。


  1. htmlタグにamp属性が必要 → <html lang="ja" amp>

  2. viewport設定が必要(あると思うけど) → <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">

  3. スタイルシートはhead内のみ。styleにamp-custom属性が必要 → <style amp-custom></style>(カスタムフォントのリンクは例外でOK)


  4. !important使用不可。後述のboilerplateコードが当てるスタイルを上書きできないので工夫が必要。


  5. インラインスタイル使用不可。Google様は絶対なのです。

  6. 使えないタグのスタイルを指定するとvalidationに通らない。

  7. 更新日やアイキャッチの設定はJSON-LDの構造化マークアップで行う


  8. imgタグは使用不可amp-imgに置き換える必要あり。ただしタグを置き換えるだけではダメ(後述)

  9. AMP boilerplateコードをhead内最後に配置:https://www.ampproject.org/ja/docs/fundamentals/spec/amp-boilerplate このコードがamp-imgの展開や独自仕様の適用を行う

以上を踏まえたテンプレートの骨格は以下の通りです:


amp-single.php

<!DOCTYPE html>

<html lang="ja" amp>
<head>
<meta charset="utf-8">
<?php
the_post();
$image_id = get_post_thumbnail_id();
$image = wp_get_attachment_image_src( $image_id, 'large');
$title = get_the_title();
echo ' <title>'.$title.'</title>'."\n";
echo ' <link rel="canonical" href="'.get_permalink( $id ).'">';
?>
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<style amp-custom>
ここにページ用のスタイルを記述
</style>
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "NewsArticle",
"mainEntityOfPage":
{
"@type": "WebPage",
"@id": "<?php echo get_the_permalink(); ?>"
},
"headline": "<?php echo $title;?>",
"datePublished": "<?php the_date('c'); ?>",
"dateModified": "<?php the_modified_date('c'); ?>",
"image": {
"@type": "ImageObject",
"url": "<?php echo $image[0];?>"
},
"author": {
"@type": "Organization",
"name": "書いた人"
},
"publisher": {
"@type": "Organization",
"name": "出版元",
"logo": {
"@type": "ImageObject",
"url": "ロゴ画像のURL"
}
}
}
</script>
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
<script async src="https://cdn.ampproject.org/v0.js"></script>
<script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>
</head>
<body>
<amp-analytics type="googleanalytics">
<script type="application/json">
{
"vars": {
"account": "UA-************"
},
"triggers": {
"trackPageview": {
"on": "visible",
"request": "pageview"
}
}
}
</script>
</amp-analytics>
<h1></h1>
<div class="wrapper">
<?php
the_content();
?>
</div>
</body>
</html>


AMPの検証

AMP化したページの検証は、URLの最後に#development=1を追加してページをリクエストします。検証結果はコンソールに出力されます。

36f5fd60094138f6704fd4cf492faa0d.png

問題がなければ「AMP validation successful.」と出力されます。


amp-imgとの戦い

しかしこのままではAMPのバリデーションに通りません。問題は画像の扱いです。amp-imgの仕様をみてみます。


1) widthとheightの指定が必須。

2) レスポンシブにするにはlayout="responsive"属性を付与する。この場合、widthとheightはただの画像比率を示すものになる。幅は100vwになる(!)。

3) srcsetに対応する。


1)と3)についてはWordPressからサイズとsrcsetに対応して出力されているのでまあよし。

2)の動作を見てみると、amp-imgはAMPのJSにより処理され、画像比率をキープしながら縦方向をpaddiingで確保するi-amphtml-sizer要素と、画像自体のimg要素に展開されてるのがわかります。imgはposition: absolute; left:0;top:0;bottom:0;right:0;指定です。これが後述の問題1の原因です。


問題1 figcaption が消える

上にも書いたように、amp-imgが展開されると、imgをabsoluteを使って配置するため、figcaptionがブロック内に飲み込まれその高さ分画像が縦に伸びるのです。そこで、figure>figcaptionの構成を分解して画像と並列に並び替えます。


問題2 絵文字が大変なことに

今回の場合、本文中に多くの絵文字がつかわれており、インライン画像として展開されるためamp-imgに置換すると大変なことに。。。そこで今回はやむなく削除で。

絵文字はPCでコピペしていたため画像に置換されていました。iOSネイティブでのコピペを行うことで回避。


問題3 layout="responsive"設定した画像がはみ出す!

layout="responsive"設定のamp-imgの展開をみてみると要素にwidth: 100vwがついている場合があるので、amp-img { max-width: 100%; } でなんとかやりすごす。これ、ついてないこともあるのです。

これら+その他の事情をふまえてコンテンツの書き出し前の処理を追加しました。


// 表示するコンテンツ取得
$content = apply_filters( 'the_content', get_post_field('post_content', $id) );

// インラインスタイル削除
$content = preg_replace('/ +style=["][^"]*?["]/i', '', $content);
$content = preg_replace('/ +style=[\'][^\']*?[\']/i', '', $content);

// figure>img+figcaptionな構成をdivラッパーとキャプション部に分割
$content = preg_replace('/<figure.*?><img(.*?>)<figcaption .*?>(.*?)<\\/figcaption><\\/figure>/',
'<div class="amp-img-wrapper with-caption"><amp-img layout="responsive"\\1</div><div class="amp-caption">\\2</div>',
$content);

// img単体で配置されているものをdivラッパーに入れる(Classic Editor Plugin対応版)
$content = preg_replace('/<img(.*?)\/?>/i',
'<div class="amp-img-wrapper"><amp-img layout="responsive"\1></div>',
$content);

echo $content;


その他の問題

画像がかなりのネックになるので、サイトのロゴなどはすべてスタイルシート背景で実装。丁寧にやればamp-imgでもできそうですがエネルギー切れ。


Googleへの登録とか

さてここまでで、既存URL/ampというURLでAMPページを実装できたはずです。では、Google様へどうお知らせするの?となりますが、AMPの偉い人のtwitter(ソース失念)によると、linkタグを設定していればGoogle側で勝手にクロールしてAMPページを登録してくれるそうです。AMPページ登録状況は、Search Consoleの「検索での見え方」の子項目「Accelerated Mobile Pages」で確認できます(新型Search Consoleのときは「ステータス」内の「AMP」)。


更新:問題発生

AMPページを公開後3日、Search Consoleよりメール:

7f403fe745e25b8ffdfa78a924ea5eb3.png

そんなアホなと、amp-imgにheight指定が抜けてるとのことで、該当記事を見てみると。。。

<amp-img layout="responsive" class="CToWUd" src="https://mail.google.com/mail/e/1f374" alt="🍴" />

こんな絵文字が生きてました。これGmail経由でもらった原稿をコピー&ペーストで配置したもののようで、その絵文字というかインライン画像が消しきれてなかった模様。

ところで2018年3月現在、Search Consoleは新旧版が併存していますが、新しい方はすべてのエラーを正しく表示していません(「重要ではない問題」が表示されない)。そのため、エラー確認は旧バージョンを使ったほうが今の所はよいかもです。

この問題のあるページ以外はSP検索時にAMPページがヒットすることを確認しましたが、一部はまだ既存ページがヒットするようで、もう少し時間がかかりそうです。

なお、一旦AMPページがインデックスされると通常のクロールのタイミングでGoogleが保持するキャッシュも更新されているのを確認しました。なので、ページの修正などは数時間程度で反映されるハズです。


更新2:キャプションの無い画像の比率がおかしい

figureに入れられていないimg単体の画像比率がおかしいので、ラッパーで囲うよう変更。以下のような処理を行います。

処理前

<figure>

<img ....>
<figcaption>きゃぷしょん</figcaption>
</figure>

<p>段落</p>

<img ....>

処理後

<div class="amp-img-wrapper with-caption">

<amp-img layout="responsive" ...>
</div>
<div class="amp-caption">きゃぷしょん</div>

<p>段落</p>

<div class="amp-img-wrapper">
<amp-img layout="responsive" ...>
</div>


WP 4.9.8更新

おそらくClassic Editorプラグインの仕様ですが、<img>タグがこれまでのXHTML風閉じ方からHTML風に変更されています。画像タグの処理コードもそれに合わせて変更しました。


画像などの処理を改良した第2話に続きます。