クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

ざっくりWordPress:PHP書式とテンプレートタグの識別-後編

WordPressをカスタマイズしようとした時に見慣れぬコード郡に戸惑った方(私もです)向け!「PHP書式」と「テンプレートタグ」が識別できるとコードが読みやすくなるよ、という記事です。

前編」の続きになります。 前編ではWordPressのファイル構成と、デフォルトテーマTwenty Seventeeenのindex.phpファイルを中心に「インクルードタグ」「WordPressループ」について解説しました。
「後編」では、その他の.phpファイルにあるテンプレートタグ、そしてプラグインのメソッドもざっくりと見ていきましょう!

目次:

前編はこちら
※参考:ざっくりWordPress:PHP書式とテンプレートタグの識別-前編

本稿はPHP書式が出てきます。「PHPって何?」という方はこちら
※参考:PHPの基本を理解するためにJSと比較する

content.phpのコード

前編で見たindex.phpは「WordPressループ」によって投稿一覧が表示さました。そして投稿の中身自体は別の.phpファイルがインクルードされていましたね。そのファイルはtemplate-parts/post/ディレクトリの中のcontent.phpです。

content.phpはこんなコードです。

<?php
/**
 * Template part for displaying posts
 *
 * @link https://codex.wordpress.org/Template_Hierarchy
 *
 * @package WordPress
 * @subpackage Twenty_Seventeen
 * @since 1.0
 * @version 1.2
 */

?>

<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
    <?php
    if ( is_sticky() && is_home() ) :
        echo twentyseventeen_get_svg( array( 'icon' => 'thumb-tack' ) );
    endif;
    ?>
    <header class="entry-header">
        <?php
        if ( 'post' === get_post_type() ) {
            echo '<div class="entry-meta">';
                if ( is_single() ) {
                    twentyseventeen_posted_on();
                } else {
                    echo twentyseventeen_time_link();
                    twentyseventeen_edit_link();
                };
            echo '</div><!-- .entry-meta -->';
        };

        if ( is_single() ) {
            the_title( '<h1 class="entry-title">', '</h1>' );
        } elseif ( is_front_page() && is_home() ) {
            the_title( '<h3 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h3>' );
        } else {
            the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
        }
        ?>
    </header><!-- .entry-header -->

    <?php if ( '' !== get_the_post_thumbnail() && ! is_single() ) : ?>
        <div class="post-thumbnail">
            <a href="<?php the_permalink(); ?>">
                <?php the_post_thumbnail( 'twentyseventeen-featured-image' ); ?>
            </a>
        </div><!-- .post-thumbnail -->
    <?php endif; ?>

    <div class="entry-content">
        <?php
        /* translators: %s: Name of current post */
        the_content( sprintf(
            __( 'Continue reading<span class="screen-reader-text"> "%s"</span>', 'twentyseventeen' ),
            get_the_title()
        ) );

        wp_link_pages( array(
            'before'      => '<div class="page-links">' . __( 'Pages:', 'twentyseventeen' ),
            'after'       => '</div>',
            'link_before' => '<span class="page-number">',
            'link_after'  => '</span>',
        ) );
        ?>
    </div><!-- .entry-content -->

    <?php
    if ( is_single() ) {
        twentyseventeen_entry_footer();
    }
    ?>

</article><!-- #post-## -->

うーむ、index.phpと同じくらいのボリュームがありますかねー。

前編と同じく「HTML要素」のみ抜き出してみます。

<!--HTMLのみ-->

<article>
    <header class="entry-header">
        <div class="entry-meta"></div>
        <h1 class="entry-title"></h1>
        <h3 class="entry-title"><a></a></h3>
        <h2 class="entry-title"><a></a></h2>
    </header><!-- .entry-header -->

        <div class="post-thumbnail">
            <a href="パーマリンク">
            </a>
        </div><!-- .post-thumbnail -->

    <div class="entry-content">
    </div><!-- .entry-content -->
</article>
  • articleの中にheder.post-thumbnail.entry-contentがあります。
  • headerの中にはメタ情報(日付、カテゴリなど).entry-metaとタイトルh1h3があります。タイトルが複数あるのはPHPのif文で分岐するためです。
  • .post-thumbnailはサムネイルです。中にaリンクがありますね。
  • .entry-contentは本文です。

PHP書式」を加えるとこんな感じです。前編と同様に「テンプレートタグ」はざっくりした日本語に置き換えています。

<!--HTMLとPHP-->

<?php 情報 ?>

<article id="post-<?php ID名 ?>" <?php class名 ?>>
    <header class="entry-header">
        <?php
        if (条件) {
            メタ情報(`<div class="entry-meta"></div>`投稿日、カテゴリなど)の表示設定
        };

        if (条件) {
            タイトル'<h1 class="entry-title"></h1>'
        } elseif (条件) {
            タイトル'<h3 class="entry-title"><a></a></h3>'
        } else {
            タイトル'<h2 class="entry-title"><a></a></h2>'
        }
        ?>
    </header><!-- .entry-header -->

    <?php if (条件)  ?>
        <div class="post-thumbnail">
            <a href="パーマリンク ?>">
                <?php サムネイル ?>
            </a>
        </div><!-- .post-thumbnail -->
    <?php endif; ?>

    <div class="entry-content">
        <?php
            コンテンツ設定(sprintf(`<span class="screen-reader-text"></span>`);
                タイトル設定
            ) );

            投稿が複数ページの時表示(array(
                'キー'      =>'<div class="page-links">' ,
                'キー'       =>'</div>',
                'キー' =>'<span class="page-number">',
                'キー'  =>'</span>',
            ) );
        ?>
    </div><!-- .entry-content -->

    <?php
    if ( 条件 ) {
        フッター設定
    }
    ?>

</article>
  • 冒頭のPHPにはテーマの情報がコメントで書かれています。
  • headerは「メタ情報」の設定に. entry-metaが埋め込まれ、「タイトル」h1h3がif文の分岐で表示されます。
  • .post-thumbnailには「サムネイル」の設定が入ります。
  • .entry-contentに「コンテンツ」の表示設定が入ります。
     sprintfはPHPの「組み込み関数」でフォーマット化された文字列を返します。中にはspan要素が入っています。
  • その下には投稿が複数ページに渡るときのページネーションが配列array()で入っています。
  • 最後にif文の中でフッター表示の設定があります。

投稿のテンプレートタグ

content.phpには「投稿」の表示によく使われるテンプレートタグが見受けられます。下記のようなものです。

※条件分岐タグ: is_で始まります。

  • is_home();:「トップページなら」を意味します。
  • is_front_page():こちらも「トップページなら」を意味します。
  • is_single():「投稿ページなら」を意味します。

※取得タグ: get_で始まります。

  • get_post_type():投稿のタイプを取得。if文の条件式( 'post' === get_post_type() )は「投稿ページなら」を意味します。
  • get_the_post_thumbnail():投稿サムネールを取得。

※表示タグ: the_で始まります。

  • the_title(): タイトルを表示。
  • the_post_thumbnail():サムネイルを表示。
  • the_content():本文を表示。

その他のテンプレートタグはこちら
※参考:テンプレートタグ - WordPress Codex 日本語版
(ただし、twentyseventeen_get_svg()などテーマの中に定義された独自の関数もあり、テーマを変更すると反映されません)

header.php、footer.phpの必須タグ

前編」のindex.phpにインクルードされていた header.phpsidebar.phpfooter.php などのファイルも基本的には同じやり方です。
HTML構造」→「PHP書式」→「テンプレートタグ」の順番で全体像から細部に向かって見ていけばコードが理解しやすくなります。ぜひ読んでみてください。

ここではheader.phpfooter.php必須で入るテンプレートタグを紹介します。

まずはheader.phpから。

<!--header.php-->
<?php wp_head(); ?>
</head>

wp_head()head要素の閉じタグの直前に入れます。

次はfooter.phpです。

<!--footer.php-->
<?php wp_footer(); ?>
</body>

wp_footer()は、body要素の閉じタグの直前に入れます。

いずれもWordPress本体やプラグインの設定(.jsファイル、.cssファイルなど)を読み込む関数です。入れ忘れるとプラグインが動かない、プラグインのCSSが反映されない、などの問題が起こる可能性があります。

functions.phpのテーマ独自タグ

テーマフォルダの中にもう一つ大事なファイルがあります。functions.phpです。
functionsという名前の通りここには「関数たち」が入っています。

PHPの基本を理解するためにJSと比較する」でPHPの「ユーザー定義関数」はJSと同じくfunctionを頭に付けると書きました。

しかし「前編」のindex.phpにも今回のcontent.phpにもfunctionの文字が見当たらないですよね。そこに呼び出された関数は別のファイルに定義されているということです。

先ほど「twentyseventeen_get_svg()はテーマの中に定義された独自の関数」と書きました。その正体こそがfunctions.phpの中の「関数たち」です。

function.phpを見るとたくさんの関数や変数が定義されています。例えば下記のようなものです。twentyseventeen_excerpt_more()というテーマ独自関数の定義部分です。

<?php 
//functions.phpの中の関数

function twentyseventeen_excerpt_more( $link ) {
    if ( is_admin() ) {
        return $link;
    }

    $link = sprintf( '<p class="link-more"><a href="%1$s" class="more-link">%2$s</a></p>',
        esc_url( get_permalink( get_the_ID() ) ),
        /* translators: %s: Name of current post */
        sprintf( __( 'Continue reading<span class="screen-reader-text"> "%s"</span>', 'twentyseventeen' ), get_the_title( get_the_ID() ) )
    );
    return ' &hellip; ' . $link;
}
add_filter( 'excerpt_more', 'twentyseventeen_excerpt_more' );
  • if文の条件にあるis_admin()is_で始まることからもわかるように「条件分岐タグ」です。「管理画面であれば」を意味します。変数$linkを返すようです。
  • その下に変数$linkの定義があります。sprintf ()は先ほど出てきたPHPの組み込み関数ですね。P要素やa要素の文字列を返します。
  • その下のもう一つsprintf ()があり中にはspan要素が入っています。
  • 最後の関数add_filter ()は「フィルターフック」と言ってJSでいうイベント(○○した時に関数を実行)のような役割です。イベントや実行に関わる関数は引数の中で指定されています。

JSのイベントについてはこちら
参考:【JSの基本-後編】書ける前に読む!HTML、CSS、JSの書式-5

なお、テーマ独自の関数を直接編集すると、テーマをバージョンアップした時に編集が無効になってしまう可能性があります。
WordPressはテーマ同士に親子関係(親テーマ、子テーマ)を作ることができます。「子テーマ」はバージョンアップの影響を受けません。*1
そのため、テーマ独自の関数を編集したい場合はthemesフォルダに「子テーマ」を作って、そこのfunctions.phpを編集します。*2
(「自作テーマ」の場合はこの限りではありません。自作のユーザー定義関数もどんどん書き加えて構いません)

プラグインのメソッド

最後に、プラグインのコードも見てみましょう。
前編」で、プラグインのファイルはpluginsフォルダの中にあると書きました。フォルダの中はこのようになっています。f:id:idr_zz:20180112002521p:plain

akismetwp-multibyte-patchなどはWordPressに最初から入っている「デフォルトプラグイン」です。他のプラグインをインストールするとこのフォルダに追加されます。
なお、pluginsフォルダの中の関数も直接は編集しません。「子テーマ」のfunctions.phpで編集します。先ほどのテーマ独自の関数と同様の理由です。*3

プラグインの中にも独自の関数(メソッド)が定義されてきます。「生JSとライブラリとフレームワーク」で触れたjQueryプラグインのメソッドとよく似た位置付けです。プラグインを削除すると「そんなメソッドはないよ」と認識されません。

図にするとこんな感じです。だんだんマトリョーシカみたいになってきた…。

f:id:idr_zz:20180112202235j:plain

※参考:【卒jQueryへの道】生JSとライブラリとフレームワークの理解

下記は私がいつもお世話になっているプラグイン「Smart Custom Fields」のコードです。
投稿にコンテンツ以外の独自の項目「カスタムフィールド」を追加できます。

※参考:Smart Custom Fields

このコードをcontent.phpの任意の場所に挿入するとその場所にカスタムフィールドが表示されます。こちらもテーマのバージョンアップの影響を受けないようにcontent.phpを「子テーマ」に複製をしてから編集します。

<?php 
$repeat_group = SCF::get( 'profile' );
foreach ( $repeat_group as $fields ) { ?>
<dl class="profile"> 
    <dt>name</dt>
    <dd><?php echo $fields['name']; ?></dd>
    <dt>job</dt>
    <dd><?php echo $fields['job']; ?></dd>
    <dt>details</dt>
    <dd><?php echo $fields['details']; ?></dd>
</dl> 
 <?php } ?>
  • $repeat_group に入る関数SCF::get()はSmart Custom Fields独自の「メソッド」です。このプラグインをアンインストールすると認識しません。
     profileという名前のグループを取得します。
  • foreachwhileforと同じく「反復」の制御構造で、「配列」の数がある分だけ繰り返します。プラグインの中に定義された$repeat_groupの数がある分$fieldsを反復処理します。
  • dd要素の中の$fields['フィールド名']は「PHPの基本」で触れた「連想配列」なので、 'name'、 'job'、 'details' という名前のキーから値を呼び出して表示することがわかります。

最後に

WordPressのコードを初めて見たとき「なんじゃこりゃあ!」と感じたものです。
先人のコードをコピペしてもそれが何を意味しているのかわからず、どこをどういじっていいのやら改変するにもおっかなビックリ。コードが「文字の塊」に見えてしまっているんですよね。

.phpファイルは「HTMLタグ」の中に「PHP書式」が埋め込まれている。さらにその中にWordPressの「テンプレートタグ」やプラグインの「メソッド」が呼び出されている。*4 という感じにコードを3分割、4分割と分類していくと見渡しが良くなり、だんだん「ここは触っていいんだなー」「ここは触っちゃいけなさそう」と判断力が付いてきます。

そしてWordPressをとっかかりにPHPコードに慣れていくと他のWebサービス(例えばEC-CUBEとかね)にも取り組みやすくなるかと思います。*5
サーバサイドの世界に踏み込めるとできることが一気に拡がるのでなんだかワクワクしてきますね!それではまた。

※参考:データベース言語SQL文についての記事も書きました!
SQL文はLocal by FlywheelのAdminerから慣れよう(WordPressのDB操作:その1) - クモのようにコツコツと


※参考:サーバサイドの基本まとめました!
【WordPress、PHP、SQL文】Webデザイナーでもわかるサーバサイドの基本まとめ!(6記事) - クモのようにコツコツと idr-zz.hatenablog.com

※参考:【WordPress、PHP、SQL文】サーバサイドの基本まとめ
qiita.com

*1:子テーマは親テーマより後に読み込まれるため、親テーマのコードを上書きします。

*2:その他の.php、.css、.jsファイルも同じです。

*3:テーマやプラグインがバージョンアップするのは機能の追加のほか、セキュリティの強化もありますので、できるだけ最新バージョンにしていきましょう。

*4:呼び名は違いますが要は「関数」なんですよね

*5:簡単なものなら自分で開発してみたりしちゃったりしちゃったりして…。