子テーマでテンプレート上書きすればアップデートしても大丈夫っていうけど本当にそう言い切って良いのか?
この記事は Habakiri Advent Calendar 2015 4日目の記事です。Habakiri とは少しそれますが、Habakiri が子テーマ前提のテーマということで、今回は子テーマに関する記事です。
そもそも子テーマとは
Codex の子テーマのページには、下記のような説明があります。
子テーマは、親テーマと呼ばれる別のテーマの機能とスタイルを継承したテーマです。既存のテーマを変更する方法として、子テーマが推奨されています。
あるテーマを親とした子テーマを作ることで、基本は親テーマを使うけど部分的に上書きする、ということが可能になります。
それがどういうメリットがあるのかと言うと、例えば「TwentyFifteen のヘッダーに住所を表示するようにしたい」というときに、直接 TwentyFifteen のheader.php
を書き換えるとします。WordPress のテーマ・プラグインはアップデートするとそのプラグイン・テーマの中身が丸ごと新バージョンに置き換わってしまうため、せっかく編集したheader.php
もTwentyFifteen をアップデートしたときに消えてしまう、ということになります。
そこで TwentyFifteen を親とした子テーマを作成し、その子テーマの中にheader.php
を作成すれば、TwentyFifteen のheader.php
よりも子テーマのheader.php
が優先して表示されるため、アップデートによって TwentyFifteen のheader.php
が最新版に置き換わっても大丈夫、ということになります。
そんな素敵な子テーマなので、ググッてみると子テーマを手放しで参照するような記事がたくさんあります。そこで、本当に子テーマを使っていれば何でも大丈夫と言えるのか?ということについて書きたいと思います。
子テーマで上書きしていれば親テーマのアップデートの影響を受けることは無い?
子テーマについて調べていると、次のような記述をしばしば見かけます。
「子テーマは親テーマのアップデートの影響を受けない」
確かに子テーマでテンプレートを上書きしていれば、親テーマでそのテンプレートがアップデートされた場合でも子テーマのテンプレートはそのまま残ります。そういう意味では親テーマのアップデートの影響を受けないということもできますが、主語が大きいというか、必ずしも影響を受けないとは言い切れない場合もあります。例えば次のような場合です。
親テーマの content.php
<?php while ( have_posts() ) : the_post(); ?> <div id="post-<?php the_ID(); ?>" class="<?php post_class(); ?>"> <h1 class="entry-title"><?php the_title(); ?></h1> <div class="entry-content"> <?php the_content(); ?> </div> </div> <?php endwhile; ?>
ここで、子テーマで content.php を上書きしてみたいと思います。
子テーマの content.php
<?php while ( have_posts() ) : the_post(); ?> <div id="post-<?php the_ID(); ?>" class="<?php post_class(); ?>"> <h1 class="entry-title"><?php the_title(); ?></h1> <div class="entry-content"> <p> ここから本文が始まります </p> <?php the_content(); ?> </div> </div> <?php endwhile; ?>
<?php the_content(); ?>
の前に「ここから本文が始まります」という一文を加えました。ここまでは何の問題も無いように見えます。
さて、ここで親テーマのアップデートで、次のように変更された場合はどうでしょう?
更新された親テーマの content.php
<?php while ( have_posts() ) : the_post(); ?> <div id="post-<?php the_ID(); ?>" class="<?php post_class(); ?>"> <h1 class="entry-title post-title"><?php the_title(); ?></h1> <div class="entry-content"> <?php the_content(); ?> </div> </div> <?php endwhile; ?>
どこがアップデートされたかわかるでしょうか?アップデート前は<h1 class="entry-title">
となっていたところが、アップデート後は<h1 class="entry-title post-title">
となっており、post-title
というクラスが追加されています。
子テーマのcontent.php
にはpost-title
というクラスの記述がありません。このアップデートで、これまで.entry-title
で見出しのデザインが行われていたのが、.post-title
でデザインされるように変更されたとしたらどうでしょう?子テーマには.post-title
の記述がないため見出しのデザインが抜け落ちてしまいます。content.php
が上書きされていないにもかかわらず親テーマのアップデートの影響を受けてしまうのです。
なるべくこういったことが起こらないように、テーマ作者はよく注意してアップデートを行う必要があるのですが、はじめからどのようなことが考えられるか入念に検討・確認してからテーマを作ることは難しいですし、途中で後方互換性がないアップデートを行いたくなってしまうことは少なくありません。
では後方互換性がないアップデートを行わなければ良いかというと、それはそれでそのテーマの進化を止めてしまうことになるというか、放置するだけになって、結局アップデートが続くテーマをアップデートしないで使うのと同じ状況になってしまうため良くありません。テーマを公開するようになって、この部分でモヤモヤすることがものすごく多く、他のテーマ開発者の方にお会いする機会があれば毎回質問しているのですが、ほんと皆さんどうしているのでしょう…。返答記事をこっそりと期待しています^^;
親テーマをアップデートしても子テーマで変更した部分は必ず残るので安心?
前述したように子テーマで親テーマのテンプレートをコピーしてカスタマイズすれば、親テーマをアップデートしてもそのカスタマイズが消えることはありません。そういう意味では「必ず残るので安心」なのですが、別の意味では「安心」と言えない場合もあります。
例えば親テーマにセキュリティホールがあった場合です。下記のような場合です(例がすごく雑ですが…)。
親テーマの content.php
<?php while ( have_posts() ) : the_post(); ?> <div id="post-<?php the_ID(); ?>" class="<?php post_class(); ?>"> <h1 class="entry-title"><?php the_title(); ?></h1> <h2><?php echo get_post_meta( get_the_ID(), 'lead', true ); ?></h2> <div class="entry-content"> <?php the_content(); ?> </div> </div> <?php endwhile; ?>
.entry-content
の前にリード文を表示するためにlead
というメタデータを表示しています。ここで、子テーマで content.php を上書きします。
子テーマの content.php
<?php while ( have_posts() ) : the_post(); ?> <div id="post-<?php the_ID(); ?>" class="<?php post_class(); ?>"> <h1 class="entry-title"><?php the_title(); ?></h1> <div class="entry-content"> <h2><?php echo get_post_meta( get_the_ID(), 'lead', true ); ?></h2> <p> ここから本文が始まります </p> <?php the_content(); ?> </div> </div> <?php endwhile; ?>
<?php the_content(); ?>
の前に「ここから本文が始まります」という一文を加えました。ここで親テーマのアップデートで、次のように変更された場合はどうでしょう?
更新された親テーマの content.php
<?php while ( have_posts() ) : the_post(); ?> <div id="post-<?php the_ID(); ?>" class="<?php post_class(); ?>"> <h1 class="entry-title"><?php the_title(); ?></h1> <?php echo esc_html( get_post_meta( get_the_ID(), 'lead', true ) ); ?> <div class="entry-content"> <?php the_content(); ?> </div> </div> <?php endwhile; ?>
アップデート前はリード文を表示するget_post_meta( get_the_ID(), 'lead', true )
の部分がエスケープ処理されていませんでした。これはセキュリティ的に問題になる場合があるため、テーマ作者がアップデートでエスケープするようにesc_html( get_post_meta( get_the_ID(), 'lead', true ) )
という風にアップデートを行っています。これで親テーマはセキュリティに関するアップデートが適用されるわけですが、子テーマではアップデートが適用される前のcontent.php
を使用しているため、セキュリティ的には引き続き「安心」とは言えない状況が続いてしまっています。
つまり、子テーマで上書きしていれば親テーマをアップデートしてもそのカスタマイズした部分が残ることは保証されているけど、同テンプレート内のカスタマイズしていない部分も必ず残ってしまう、ということです。
公式ディレクトリに掲載されているテーマの場合はレビュワーさんがチェックをしてくれているので、こういったセキュリティ面の不具合はあまり無いかもしれませんが、絶対に無いと言い切れるわけでは無いのでこの点はちゃんと認識して注意する必要があると思っています。
ほとんどのテンプレートを子テーマで上書きしている子テーマ
ほぼ全てのテンプレートを子テーマで上書きしている場合、上で書いたことがほぼ全てのテンプレートで起こることになりますし、そもそも親テーマをアップデートしてもその変更は子テーマには全く反映されないということになってしまうので、そういう場合はいっそフォークして別のテーマとして作れば良いんじゃね?と思うようなものに出会うこともありますね…。
ではどうすれば良いのか?
いろいろ書きましたが、要するに子テーマにさえしておけば何の問題も無いしオールオッケー♪というわけではなく、問題がでることもあるのでプラグインをアップデートするときと同じようにテスト環境でアップデートしてみて、問題がないか確認してから本番でアップデートしたほうが良い、ということです。
また、テンプレートを丸ごと上書きしないといけない作りになっているからこういったことが起こりやすいわけで、テーマ開発者としては、子テーマを作るときになるべくテンプレートを丸ごと上書きしないですむ設計にしなければいけないと考えています。このあたりは昨日の記事をご参照いただければ。
この投稿へのトラックバック
-
-
[…] HabakiriAdvent Carender 12/4 子テーマでテンプレート上書きすればアップデートしても大丈夫っていうけど本当にそう言い切って良いのか? | Habakiri […]
-
-
-
[…] これは思った通り機能してくれました。Habakiri は例えば記事の前後とか、タイトルの前後とか、ヘッダーの前後とか、いろいろなところにアクションフックが用意してあります。なので「ここに何かを足したいんですよ」みたいなときにテンプレートを上書きせずにその何かを追加することができます。もしこういうフックがないテーマを親とした場合、一部分だけ何かを追加したいだけだとしてもまるっとテンプレートを上書きしなければいけなくなります。いわゆる「子テーマ上書き問題」です(僕が勝手に言っているだけ。誰か流行らしてください)。最近「子テーマはいいぞ」みたいな声をよく聞きますが、この問題をみなさんがどう考えているのかというのは常々気になっていまして、機会があればディスカッションしたり考えを聞いてみたいなぁと思っています。 […]
-
-
-
[…] abakiri作者さんのページに、「子テーマでテンプレート上書きすればアップデートしても大丈夫っていうけど本当にそう言い切って良いのか?」ってエントリーが上がっているので、ご参 […]
-
- トラックバック URL
この投稿へのコメント