- 1. Sanitize(入力整形)とEscape(出力エスケープ)
- 2. ループ(The Loop)とWP_Query|投稿を表示する仕組み
- 3. テンプレート読み込み関数|ファイル分割で保守性を上げる
- 4. 投稿情報のテンプレタグ|タイトル・本文・日時・著者
この記事でわかること
- テーマ制作で日常的に使うWordPress関数を22カテゴリ・60関数以上を体系的に網羅
- 「知っている」から「安全に使える」へ:Sanitize/Escapeの正しい使い分け
- WP_Queryとメインクエリの違い、サブループ後のリセット処理を完全理解できる
- wp_head・カスタマイザー・コメント・ショートコードなど、解説書に載りにくい関数も収録
- コードのコピー&ペーストだけでなく「なぜそう書くか」の理由まで解説
WordPressのテーマ制作において、最も重要なのは「関数の名前を知っている」ことではありません。「どの関数を、どんな文脈で、どう安全に使うか」を理解することです。
関数の使い方があいまいなままコピー&ペーストを繰り返すと、セキュリティホールや予期しないバグを生む原因になります。この記事では、テーマ制作者が日常的に使う関数を目的・仕組み・実装例の3つの視点から解説します。
この記事はWordPress関数解説シリーズのPart1(テーマ制作編)です。プラグイン開発(設定画面・メニュー・CPT)はPart2、Ajax・REST API・セキュリティ設計はPart3で解説しています。
この記事の結論
テーマ制作で最も重要なのは「サニタイズは早め、エスケープは遅め」の2段構えと、WP_Queryサブループ後のwp_reset_postdata()です。この2つを省略するとXSS脆弱性や原因特定困難なバグが発生します。加えてenqueue APIによるCSS/JS管理、get_template_part()によるテンプレート分割、wp_head()/wp_footer()の必須記述を守れば、安全で保守しやすいテーマが作れます。
1. Sanitize(入力整形)とEscape(出力エスケープ)
結論:テーマ開発のセキュリティ事故のほとんどは、Sanitize/Escapeの省略から起きます。「サニタイズは早め、エスケープは遅め(出力直前)」の2段構えを習慣にするだけで、XSSやインジェクション攻撃の大半を防げます。
テーマ制作で最も事故が起きやすいのは、ユーザーやデータベースから取得した値をそのまま画面に出力してしまうことです。WordPressは多くのユーザーが様々な環境で使うCMSであり、悪意のある入力や予期しないデータが入ってくる可能性を常に考慮しなければなりません。
Sanitize(サニタイズ)とは
Sanitizeは入力時の整形処理です。フォームやURLパラメータから入ってきた値を、期待する形式に変換・制限します。たとえば、メールアドレスとして受け取った値に不正な文字が含まれていたら除去する処理がこれに該当します。
| 関数名 | 用途 | 主な動作 |
|---|---|---|
sanitize_text_field() |
テキスト入力 | HTMLタグ・改行を除去 |
sanitize_textarea_field() |
複数行テキスト | HTMLタグを除去・改行は保持 |
sanitize_email() |
メールアドレス | メール形式に整形 |
absint() |
正の整数 | 絶対値の整数に変換 |
sanitize_key() |
キー名・スラッグ | 英小文字・数字・_・-のみ |
Escape(エスケープ)とは
Escapeは出力時の無害化処理です。データベースから取得した値を画面に表示する直前に、出力先の文脈に合わせて安全な形式に変換します。これによりXSS(クロスサイトスクリプティング)攻撃を防ぎます。
| 関数名 | 使う場面 |
|---|---|
esc_html() |
HTML本文に出力するテキスト |
esc_attr() |
HTML属性値(class, id, data-* など) |
esc_url() |
href・src などのURL |
esc_js() |
インラインJavaScript内 |
実装例:タイトルを安全に表示する
|
1 2 3 4 5 6 7 |
<h1><?php echo esc_html( get_the_title() ); ?></h1> <!-- 属性値に出力する場合 --> <div data-title="<?php echo esc_attr( get_the_title() ); ?>">...</div> <!-- URLを出力する場合 --> <a href="<?php echo esc_url( get_permalink() ); ?>">続きを読む</a> |
2. ループ(The Loop)とWP_Query|投稿を表示する仕組み
結論:WP_Queryのサブループを使ったら必ずwp_reset_postdata()でリセットしてください。この一行を省略すると、タイトルや日付が別の投稿のものになるという、原因特定が極めて難しいバグが発生します。
WordPressにおける「ループ」とは、データベースから取得した投稿データを順番に処理して表示する仕組みです。記事一覧、アーカイブページ、検索結果など、複数の投稿を表示するあらゆる場面で使われます。
have_posts() / the_post()|基本のループ
have_posts()は表示すべき投稿が残っているかを真偽値で返し、the_post()は次の投稿データをグローバル変数$postにセットします。この2つを組み合わせたwhileループがWordPressテーマの基本構造です。
|
1 2 3 4 5 6 7 8 9 10 |
<?php if ( have_posts() ) : ?> <?php while ( have_posts() ) : the_post(); ?> <article class="post"> <h2><?php the_title(); ?></h2> <div class="excerpt"><?php the_excerpt(); ?></div> </article> <?php endwhile; ?> <?php else : ?> <p>記事が見つかりませんでした。</p> <?php endif; ?> |
WP_Query|カスタムループを作る
メインクエリとは別に、独自の条件で投稿を取得したい場合に使います。サイドバーへの「最新5件表示」や「関連記事リスト」などで活躍します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php $args = array( 'post_type' => 'post', 'posts_per_page' => 5, 'orderby' => 'date', 'order' => 'DESC', ); $recent_query = new WP_Query( $args ); if ( $recent_query->have_posts() ) : while ( $recent_query->have_posts() ) : $recent_query->the_post(); ?> <a href="<?php echo esc_url( get_permalink() ); ?>"> <?php echo esc_html( get_the_title() ); ?> </a> <?php endwhile; wp_reset_postdata(); // ← 必須:グローバル状態をリセット endif; ?> |
wp_reset_postdata()|忘れると起きること
WP_Queryでサブループを実行すると、グローバル変数$postがサブループの最後の投稿を指した状態になります。リセットを省略すると、その後のタイトルや日付の表示が意図しない投稿のものになるバグが発生します。原因が見えにくいため、リセット忘れは致命的です。
get_posts()|配列で投稿を取得
WP_Queryがループ処理に適しているのに対し、get_posts()は投稿オブジェクトの配列を直接取得したい場合に便利です。内部的にはWP_Queryを使用しています。
|
1 2 3 4 5 6 |
<?php $posts_array = get_posts( array( 'numberposts' => 3 ) ); foreach ( $posts_array as $p ) { echo esc_html( $p->post_title ) . '<br>'; } ?> |
pre_get_posts|メインクエリを書き換える
アーカイブの表示件数変更や特定カテゴリの除外など、メインクエリの条件を変えたい場合はこのフックを使います。新しいWP_Queryを作るより効率的です。
|
1 2 3 4 5 6 7 8 9 10 |
<?php add_action( 'pre_get_posts', function( $query ) { if ( is_admin() || ! $query->is_main_query() ) { return; // 管理画面とサブクエリには適用しない } if ( $query->is_category() ) { $query->set( 'posts_per_page', 12 ); } }); ?> |
重要:is_admin()とis_main_query()のチェックを必ず入れてください。これを怠ると管理画面の投稿一覧にも影響が出ます。
3. テンプレート読み込み関数|ファイル分割で保守性を上げる
結論:get_template_part()を使ったテンプレート分割は、テーマの保守コストを大幅に下げます。WordPress 5.5以降は第3引数で変数を渡せるため、パーツの再利用性がさらに向上しています。
get_header() / get_footer() / get_sidebar()
それぞれheader.php、footer.php、sidebar.phpを読み込みます。引数を渡すことでバリエーションのあるテンプレートも読み込めます。例:get_header( 'landing' )→header-landing.phpを読み込む。
|
1 2 3 4 5 6 7 8 |
<?php get_header(); ?> <main class="site-main"> <!-- メインコンテンツ --> </main> <?php get_sidebar(); ?> <?php get_footer(); ?> |
get_template_part()|UIパーツの再利用
記事カード、CTAブロック、著者情報など、複数箇所で使うUI部品を別ファイルに切り出すための関数です。第1引数がベース名、第2引数がバリエーション名で、parts/card-post.phpが存在しなければparts/card.phpにフォールバックします。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php // 呼び出し側:変数を渡す(WordPress 5.5以降) get_template_part( 'parts/card', 'post', array( 'show_date' => true, 'show_author' => false, ) ); ?> <?php // parts/card-post.php 側で受け取る $show_date = $args['show_date'] ?? true; $show_author = $args['show_author'] ?? true; ?> |
4. 投稿情報のテンプレタグ|タイトル・本文・日時・著者
結論:「the_」で始まる関数は直接出力、「get_the_」で始まる関数は値を返します。加工して出力する場合は必ずget_the_系+エスケープ関数を組み合わせてください。
| 関数 | 動作 | 使いどころ |
|---|---|---|
the_title() |
タイトルを直接出力 | h1・h2タグ内 |
get_the_title() |
タイトルを返す | 加工が必要な場合 |
the_content() |
本文を出力(フィルター適用済み) | single.phpなど |
the_excerpt() |
抜粋を出力 | 記事一覧・アーカイブ |
get_the_date() |
公開日を返す | timeタグのdatetime属性に |
get_the_modified_date() |
最終更新日を返す | 技術ブログのSEO対策 |
get_the_author_meta() |
著者情報を返す | 著者ボックス |
日付の実装例(SEO対応)
|
1 2 3 4 5 6 |
<time datetime="<?php echo esc_attr( get_the_date( 'c' ) ); ?>"> <?php echo esc_html( get_the_date() ); ?> </time> <!-- 最終更新日も表示(技術ブログでは鮮度が重要) --> <p>最終更新: <?php echo esc_html( get_the_modified_date() ); ?></p> |
著者情報の表示例
|
1 2 3 4 5 6 7 |
<p class="author"> 投稿者: <?php echo esc_html( get_the_author_meta( 'display_name' ) ); ?> </p> <!-- 著者ページへのリンク --> <a href="<?php echo esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ); ?>"> <?php echo esc_html( get_the_author_meta( 'display_name' ) ); ?>の記事一覧 </a> |
5. URL・リンク生成|ハードコードを絶対に避ける理由
結論:URLをハードコードすると、サイト移転やSSL化のたびに大量の修正が必要になります。WordPress関数で動的に生成する習慣をつけることで、環境変化に強いテーマを作れます。
get_permalink() / the_permalink()
投稿へのリンクを作成する基本関数です。パーマリンク設定に関係なく正しいURLを返します。
|
1 2 3 |
<a href="<?php echo esc_url( get_permalink() ); ?>"> <?php echo esc_html( get_the_title() ); ?> </a> |
home_url() / site_url() / admin_url()
| 関数 | 返すURL | 主な用途 |
|---|---|---|
home_url() |
サイトの表示URL | 通常のリンク生成に使う |
site_url() |
WordPressのインストールURL | サブディレクトリ構成で違いが出る |
admin_url() |
管理画面のURL | ログインリンクなど |
|
1 |
<a href="<?php echo esc_url( home_url( '/contact/' ) ); ?>">お問い合わせ</a> |
get_post_type_archive_link()
カスタム投稿タイプのアーカイブページへのリンクを生成します。
|
1 2 3 |
<a href="<?php echo esc_url( get_post_type_archive_link( 'book' ) ); ?>"> 書籍一覧 </a> |
6. アイキャッチ・画像関連|パフォーマンスと見た目を両立する
結論:the_post_thumbnail()を使えば、srcset属性が自動付与されてレスポンシブ対応が完了します。has_post_thumbnail()による存在確認を忘れると、alt属性が空のimgタグが出力されてSEO評価を下げる原因になります。
has_post_thumbnail() / the_post_thumbnail()
|
1 2 3 4 5 6 |
<?php if ( has_post_thumbnail() ) : ?> <figure class="post-thumbnail"> <?php the_post_thumbnail( 'large' ); ?> <!-- srcsetが自動付与されレスポンシブ対応済み --> </figure> <?php endif; ?> |
get_the_post_thumbnail_url()|背景画像として使う
|
1 2 3 4 5 6 |
<?php $thumb_url = get_the_post_thumbnail_url( get_the_ID(), 'large' ); if ( $thumb_url ) : ?> <div class="hero" style="background-image: url(<?php echo esc_url( $thumb_url ); ?>);"></div> <?php endif; ?> |
wp_get_attachment_image()|メディアIDから画像タグを生成
アイキャッチ以外の画像(カスタムフィールドに保存した画像IDなど)を出力する際に使います。srcsetやalt属性も自動付与されます。
|
1 2 3 4 5 6 7 8 9 10 11 |
<?php $image_id = get_post_meta( get_the_ID(), 'gallery_image', true ); if ( $image_id ) { echo wp_get_attachment_image( $image_id, 'medium', false, array( 'class' => 'gallery-img', 'loading' => 'lazy' ) ); } ?> |
add_image_size()|カスタムサイズを登録する
|
1 2 3 4 5 6 |
<?php add_action( 'after_setup_theme', function() { add_image_size( 'card-thumbnail', 640, 360, true ); // 16:9でトリミング add_image_size( 'hero-wide', 1920, 600, true ); }); ?> |
set_post_thumbnail_size()|デフォルトサムネイルサイズを変更
thumbnailサイズのデフォルト(150×150)を変更したい場合に使います。add_image_size()と異なり、既存のthumbnailサイズそのものを上書きします。
|
1 2 3 4 5 |
<?php add_action( 'after_setup_theme', function() { set_post_thumbnail_size( 400, 300, true ); // 400x300でクロップ }); ?> |
7. カテゴリ・タグ・タクソノミー表示
結論:カスタムタクソノミーを使うならget_the_terms()を覚えてください。WP_Errorが返ることがあるため、is_wp_error()チェックをセットで書く癖をつけることが安全な実装の基本です。
get_the_category()|カテゴリを取得
|
1 2 3 4 5 6 |
<?php $categories = get_the_category(); if ( $categories ) { echo '<span class="category">' . esc_html( $categories[0]->name ) . '</span>'; } ?> |
get_the_terms()|カスタムタクソノミー対応
|
1 2 3 4 5 6 7 8 9 |
<?php $terms = get_the_terms( get_the_ID(), 'genre' ); if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) { foreach ( $terms as $term ) { $link = get_term_link( $term ); echo '<a href="' . esc_url( $link ) . '" class="tag">' . esc_html( $term->name ) . '</a>'; } } ?> |
get_term_link()|タームのアーカイブURLを取得
上記の例でも使っていますが、タームオブジェクト・ID・スラッグのいずれかを引数に渡してアーカイブURLを取得します。WP_Errorが返る可能性があるため、チェックが必要です。
|
1 2 3 4 5 6 7 |
<?php $term = get_term_by( 'slug', 'fiction', 'genre' ); if ( $term && ! is_wp_error( $term ) ) { $link = get_term_link( $term ); echo '<a href="' . esc_url( $link ) . '">フィクション一覧</a>'; } ?> |
8. ページネーションと前後リンク
結論:the_posts_pagination()はWordPress標準のページネーション関数で、アクセシビリティ対応のHTMLを自動生成します。自前でページネーションを組む前にまずこの関数で要件を満たせるか確認してください。
the_posts_pagination()|アーカイブ用
|
1 2 3 4 5 6 7 |
<?php the_posts_pagination( array( 'mid_size' => 2, 'prev_text' => '« 前へ', 'next_text' => '次へ »', ) ); ?> |
paginate_links()|ページリンクを自由にカスタマイズ
the_posts_pagination()では不十分なカスタマイズが必要な場合、paginate_links()を直接使います。出力するHTMLを自分でラップできます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php global $wp_query; $paginatd = paginate_links( array( 'total' => $wp_query->max_num_pages, 'current' => max( 1, get_query_var( 'paged' ) ), 'format' => '?paged=%#%', 'prev_text' => '前へ', 'next_text' => '次へ', 'type' => 'array', // 配列で返す ) ); if ( $paginatd ) { echo '<nav class="pagination"><ul>'; foreach ( $paginatd as $link ) { echo '<li>' . $link . '</li>'; } echo '</ul></nav>'; } ?> |
previous_post_link() / next_post_link()|個別記事用
|
1 2 3 4 |
<nav class="post-navigation"> <div class="nav-prev"><?php previous_post_link( '%link', '« %title' ); ?></div> <div class="nav-next"><?php next_post_link( '%link', '%title »' ); ?></div> </nav> |
9. メニュー・ウィジェット・サイドバー
結論:register_nav_menus()でメニュー位置を登録してwp_nav_menu()で表示するパターンは、管理画面からメニューを編集できる柔軟な構造を生み出します。テーマの導入直後にまず設定すべき基本機能です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?php // functions.php:メニュー位置を登録 add_action( 'after_setup_theme', function() { register_nav_menus( array( 'primary' => 'メインメニュー', 'footer' => 'フッターメニュー', ) ); }); // テンプレートファイル:メニューを表示 wp_nav_menu( array( 'theme_location' => 'primary', 'container' => 'nav', 'menu_class' => 'main-navigation', 'fallback_cb' => false, // メニュー未登録時に何も出力しない ) ); ?> |
register_sidebar() / dynamic_sidebar()
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?php add_action( 'widgets_init', function() { register_sidebar( array( 'name' => 'サイドバー', 'id' => 'sidebar-1', 'before_widget' => '<div class="widget">', 'after_widget' => '</div>', 'before_title' => '<h3 class="widget-title">', 'after_title' => '</h3>', ) ); }); // sidebar.php if ( is_active_sidebar( 'sidebar-1' ) ) { dynamic_sidebar( 'sidebar-1' ); } ?> |
10. body_class / post_class|CSS設計の土台
結論:body_class()をbodyタグに使うだけで、ページ種別ごとのCSSセレクタが自動生成されます。「.single .sidebar { display: none; }」のような記事詳細ページだけのスタイルが、JavaScriptなしで簡潔に書けるようになります。
|
1 2 3 4 5 6 7 |
<!-- header.php --> <body <?php body_class(); ?>> <!-- ループ内 --> <article <?php post_class(); ?>> <!-- 投稿内容 --> </article> |
独自クラスを追加したい場合は引数に渡します。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
<!-- 独自クラスを追加する --> <body <?php body_class( 'my-custom-class' ); ?>> <!-- フィルターで追加する方法(functions.php) --> <?php add_filter( 'body_class', function( $classes ) { if ( is_singular( 'post' ) ) { $classes[] = 'article-page'; } return $classes; }); ?> |
11. 条件分岐(Conditional Tags)基本編
結論:is_front_page()とis_home()の違いを把握することが、WordPress条件分岐の第一歩です。「フロントページにブログ投稿インデックスを表示」と設定している場合、両方がtrueになります。応用編はSection21で解説します。
| 関数 | 判定内容 |
|---|---|
is_front_page() |
フロントページか |
is_home() |
ブログ投稿インデックスか |
is_single() |
個別投稿ページか |
is_page() |
固定ページか |
is_category() |
カテゴリアーカイブか |
is_search() |
検索結果ページか |
is_404() |
404ページか |
is_user_logged_in() |
ログイン済みか |
|
1 2 3 4 5 6 7 |
<?php if ( is_front_page() ) { // トップページ用の処理 } elseif ( is_single() ) { // 個別投稿用の処理 } ?> |
12. スクリプト・スタイルのenqueue|直書きが危険な理由
結論:CSSやJavaScriptをheader.phpやfooter.phpに直書きするのは避けてください。enqueue APIを使うことで、プラグインとの依存関係管理、バージョンキャッシュ対策、読み込み順序の制御が自動化されます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php add_action( 'wp_enqueue_scripts', function() { // CSSの読み込み wp_enqueue_style( 'theme-style', // ハンドル名 get_stylesheet_uri(), // URL array(), // 依存関係 '1.0.0' // バージョン(キャッシュ対策) ); // JavaScriptの読み込み(フッターに出力) wp_enqueue_script( 'theme-script', get_stylesheet_directory_uri() . '/assets/js/app.js', array( 'jquery' ), // jQuery依存 '1.0.0', true // フッターに出力 ); }); ?> |
wp_add_inline_style() / wp_add_inline_script()
登録済みスタイルやスクリプトに、少量のインラインコードを追記したい場合に使います。<style>タグや<script>タグを直書きする代わりに使うことで、適切なタイミングで出力されます。
|
1 2 3 4 5 6 7 8 9 10 |
<?php add_action( 'wp_enqueue_scripts', function() { wp_enqueue_style( 'theme-style', get_stylesheet_uri() ); // テーマカラーをCSSカスタムプロパティで渡す例 $primary_color = get_theme_mod( 'primary_color', '#0066cc' ); $custom_css = ":root { --color-primary: " . sanitize_hex_color( $primary_color ) . "; }"; wp_add_inline_style( 'theme-style', $custom_css ); }); ?> |
13. 翻訳(i18n)関数|テーマ配布を見据えた書き方
結論:テーマを配布する予定がある場合、テキストを翻訳関数でラップする習慣を最初からつけてください。後から対応しようとすると膨大な修正コストがかかります。
| 関数 | 動作 |
|---|---|
__() |
翻訳して返す |
_e() |
翻訳して出力 |
esc_html__() |
翻訳+エスケープして返す |
esc_html_e() |
翻訳+エスケープして出力 |
_n() |
件数に応じて単数・複数形を切り替えて返す |
|
1 2 3 4 5 6 7 8 9 10 |
<h2><?php esc_html_e( '最新の投稿', 'mytheme' ); ?></h2> <!-- 件数で単複切り替え --> <?php $count = get_comments_number(); printf( esc_html( _n( '%s件のコメント', '%s件のコメント', $count, 'mytheme' ) ), number_format_i18n( $count ) ); ?> |
14. add_theme_support()|テーマの基本機能を有効化する
結論:add_theme_support()はテーマのafter_setup_themeフックで呼び出します。post-thumbnailsの宣言なしにthe_post_thumbnail()を使っても画像は表示されません。機能を使う前に必ず宣言してください。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?php add_action( 'after_setup_theme', function() { // アイキャッチ画像を有効化(必須宣言) add_theme_support( 'post-thumbnails' ); // タイトルタグの自動生成(header.phpにtitleタグを書かなくてよくなる) add_theme_support( 'title-tag' ); // カスタムロゴを有効化 add_theme_support( 'custom-logo', array( 'width' => 200, 'height' => 80, 'flex-width' => true, 'flex-height' => true, ) ); // HTML5マークアップのサポート add_theme_support( 'html5', array( 'search-form', 'comment-form', 'comment-list', 'gallery', 'caption', ) ); // ブロックエディタのスタイルをフロントとエディタで共有 add_theme_support( 'editor-styles' ); add_editor_style( 'assets/css/editor.css' ); // ウィドスクリーンアライン(alignwide / alignfull)を有効化 add_theme_support( 'align-wide' ); }); ?> |
15. テーマ必須の出力関数|wp_head / wp_footer / bloginfo
結論:wp_head()とwp_footer()の省略は、プラグインの多くが動作しなくなる最大の原因です。「なぜ書くか」を理解せずコピーしているテーマが多いですが、この2つはWordPressエコシステム全体のフックポイントです。
wp_head()|</head>の直前に必須
wp_head()は</head>タグの直前に書きます。プラグインがCSSやメタタグを挿入するためのフックがここに集まっています。これを省略するとSEOプラグインのメタタグが出力されなくなります。
|
1 2 3 4 5 6 |
<!-- header.php --> <head> <meta charset="<?php bloginfo( 'charset' ); ?>"> <meta name="viewport" content="width=device-width, initial-scale=1"> <?php wp_head(); ?> <!-- ← この位置が重要。絶対に省略しない --> </head> |
wp_footer()|</body>の直前に必須
wp_footer()は</body>タグの直前に書きます。wp_enqueue_script()でフッターに登録したJavaScriptはすべてここで出力されます。省略するとjQueryを含む多くのスクリプトが動作しなくなります。
|
1 2 3 4 |
<!-- footer.php --> <?php wp_footer(); ?> <!-- ← 絶対に省略しない --> </body> </html> |
language_attributes()|htmlタグの言語属性
WordPressの設定言語に応じたlang属性とdir属性を自動出力します。アクセシビリティとSEOの両方に影響します。
|
1 2 3 |
<!DOCTYPE html> <html <?php language_attributes(); ?>> <!-- 出力例:html lang="ja" --> |
bloginfo() / get_bloginfo()
サイト名、説明文、文字セット、WordPressのバージョンなど、管理画面の「一般設定」に対応する情報を取得します。bloginfo()は直接出力、get_bloginfo()は値を返します。
| 引数 | 返す情報 |
|---|---|
'name' |
サイト名(一般設定のサイトのタイトル) |
'description' |
キャッチフレーズ |
'charset' |
文字コード(通常 UTF-8) |
'url' |
サイトURL(home_url()と同等) |
'template_url' |
テーマディレクトリURL |
|
1 2 3 4 5 6 7 8 9 |
<!-- サイト名をクリッカブルロゴとして表示 --> <a href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home"> <?php bloginfo( 'name' ); ?> </a> <!-- キャッチフレーズの出力 --> <p class="site-description"> <?php echo esc_html( get_bloginfo( 'description' ) ); ?> </p> |
16. カスタムロゴ|has_custom_logo / the_custom_logo
結論:カスタムロゴを実装するには、add_theme_support(‘custom-logo’)の宣言が前提です。その上でhas_custom_logo()で存在確認し、the_custom_logo()で出力する2段構えが基本パターンです。サイト名テキストをフォールバックとして残すことが重要です。
has_custom_logo() / the_custom_logo()
the_custom_logo()は<a>タグで囲まれた<img>タグを出力します。alt属性にはサイト名が自動でセットされるため、アクセシビリティ対応も同時に完了します。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
<div class="site-branding"> <?php if ( has_custom_logo() ) : ?> <?php the_custom_logo(); ?> <?php else : ?> <!-- カスタムロゴ未設定時はサイト名テキストで代替 --> <p class="site-title"> <a href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home"> <?php bloginfo( 'name' ); ?> </a> </p> <?php endif; ?> </div> |
get_custom_logo()|ロゴHTMLを文字列で取得
ロゴを他のHTMLと組み合わせたい場合はget_custom_logo()で文字列として取得します。なお、出力時はWordPressが生成するHTMLなのでechoで直接出力できます(エスケープ不要)。
|
1 2 3 4 5 6 |
<?php $logo_html = get_custom_logo(); if ( $logo_html ) { echo $logo_html; // WordPressが生成する安全なHTMLのため直接出力可 } ?> |
17. テーマカスタマイザー|get_theme_mod / set_theme_mod
結論:テーマの色やレイアウトなど「ユーザーが変更したいデザイン設定」はget_theme_mod()で管理します。カスタマイザーに登録すると管理画面からリアルタイムプレビューで変更でき、ユーザー体験が大幅に向上します。
get_theme_mod() / set_theme_mod()
get_theme_mod()はテーマモッドの値を取得します。第2引数にデフォルト値を指定できるため、カスタマイザーで未設定の場合でもフォールバックが効きます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php // 値を取得(デフォルト値付き) $primary_color = get_theme_mod( 'primary_color', '#0066cc' ); $show_sidebar = get_theme_mod( 'show_sidebar', true ); // 使用例:インラインCSSとして出力 $custom_css = ' :root { --color-primary: ' . sanitize_hex_color( $primary_color ) . '; } '; wp_add_inline_style( 'theme-style', $custom_css ); ?> |
customize_register|カスタマイザーに設定を追加
customize_registerアクションフックを使って、カスタマイザーにセクション・コントロール・設定を登録します。設定→コントロールを1セットとして追加します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<?php add_action( 'customize_register', function( WP_Customize_Manager $wp_customize ) { // セクションを追加 $wp_customize->add_section( 'theme_colors', array( 'title' => 'テーマカラー設定', 'priority' => 30, ) ); // 設定を登録(保存されるデータの定義) $wp_customize->add_setting( 'primary_color', array( 'default' => '#0066cc', 'sanitize_callback' => 'sanitize_hex_color', // 入力値をサニタイズ 'transport' => 'postMessage', // リアルタイムプレビュー ) ); // コントロールを追加(UIの定義) $wp_customize->add_control( new WP_Customize_Color_Control( $wp_customize, 'primary_color', array( 'label' => 'メインカラー', 'section' => 'theme_colors', ) ) ); // テキストコントロールの例 $wp_customize->add_setting( 'footer_text', array( 'default' => '', 'sanitize_callback' => 'sanitize_text_field', ) ); $wp_customize->add_control( 'footer_text', array( 'label' => 'フッターテキスト', 'section' => 'title_tagline', // 既存の「サイト基本情報」セクションに追加 'type' => 'text', ) ); }); ?> |
18. ショートコード|add_shortcode / do_shortcode
結論:ショートコードはテーマ独自の再利用可能なUIコンポーネントを投稿本文内で使えるようにする仕組みです。ただし、ブロックエディタが普及した現在、新規実装はブロックの代わりにショートコードを使うか慎重に判断してください。既存テーマとの互換性維持には有効です。
add_shortcode( $tag, $callback )
functions.phpでショートコードを登録します。コールバック関数は必ず文字列を返す(echoではない)のが重要なポイントです。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<?php // シンプルなショートコード(属性なし) add_shortcode( 'site_name', function() { return esc_html( get_bloginfo( 'name' ) ); } ); // 使い方:[site_name] // 属性付きショートコード add_shortcode( 'button', function( $atts, $content = null ) { // デフォルト値とユーザー指定値をマージ $atts = shortcode_atts( array( 'url' => '#', 'color' => 'primary', 'size' => 'medium', ), $atts, 'button' ); // 出力は文字列で返す(echo不可) return sprintf( '<a href="%s" class="btn btn-%s btn-%s">%s</a>', esc_url( $atts['url'] ), esc_attr( $atts['color'] ), esc_attr( $atts['size'] ), esc_html( $content ) ); } ); // 使い方:[button url="/contact/" color="primary"]お問い合わせ[/button] ?> |
do_shortcode()|PHPからショートコードを実行
テンプレートファイルなど、PHP内からショートコードを実行したい場合に使います。
|
1 2 3 4 5 6 7 8 |
<?php // テンプレート内でショートコードを実行 echo do_shortcode( '[button url="/contact/"]お問い合わせ[/button]' ); // ウィジェットやテキストエリアの内容にショートコードを処理させる $content = get_post_meta( get_the_ID(), 'custom_content', true ); echo do_shortcode( wp_kses_post( $content ) ); ?> |
shortcode_atts()|属性のデフォルト値を安全に処理
ショートコードの属性は必ずshortcode_atts()を通してください。ユーザーが渡した不明な属性を除去し、デフォルト値とマージする安全な処理が行われます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php $atts = shortcode_atts( array( // デフォルト値(許可する属性の定義) 'id' => '', 'class' => '', 'title' => '新着情報', 'count' => 5, ), $atts, // ユーザーが渡した属性 'my_widget' // ショートコード名(フィルターに使われる) ); // 取り出し $count = absint( $atts['count'] ); $title = sanitize_text_field( $atts['title'] ); ?> |
19. コメント機能|comments_template / comment_form
結論:コメント機能はcomments_template()の1行で完結します。comments.phpに実装を任せることで、テンプレートの責務分離が実現します。comment_form()は引数でラベルや必須項目を細かく制御できるため、UI要件に合わせてカスタマイズしてください。
comments_template()|single.phpに1行追加するだけ
個別投稿テンプレート(single.php)に記述します。comments.phpを自動で読み込み、コメント一覧とフォームを表示します。
|
1 2 3 4 5 6 7 |
<!-- single.php --> <?php // コメントが許可されている場合のみ表示 if ( comments_open() || get_comments_number() ) { comments_template(); } ?> |
comments_open() / get_comments_number()
| 関数 | 用途 |
|---|---|
comments_open() |
コメントが有効かどうかを判定(true/false) |
get_comments_number() |
コメント件数を整数で返す |
have_comments() |
コメントが存在するかを判定 |
|
1 2 3 4 5 6 7 8 9 10 11 |
<!-- コメント件数を表示 --> <?php $comment_count = get_comments_number(); if ( $comment_count ) { printf( '<a href="%s">%s</a>', esc_url( get_comments_link() ), esc_html( sprintf( _n( '%d件のコメント', '%d件のコメント', $comment_count, 'mytheme' ), $comment_count ) ) ); } ?> |
wp_list_comments()|コメント一覧を出力
コメント一覧をWordPress標準のHTML構造で出力します。comments.php内で使います。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<!-- comments.php --> <div id="comments" class="comments-area"> <?php if ( have_comments() ) : ?> <h2 class="comments-title"> <?php echo esc_html( sprintf( _n( '%d件のコメント', '%d件のコメント', get_comments_number(), 'mytheme' ), get_comments_number() ) ); ?> </h2> <ol class="comment-list"> <?php wp_list_comments( array( 'style' => 'ol', 'short_ping' => true, 'avatar_size'=> 48, ) ); ?> </ol> <!-- ページネーション --> <?php the_comments_navigation(); ?> <?php endif; ?> <?php if ( comments_open() ) : ?> <?php comment_form(); ?> <?php elseif ( ! comments_open() && get_comments_number() ) : ?> <p class="no-comments"><?php esc_html_e( 'コメントは受け付けていません。', 'mytheme' ); ?></p> <?php endif; ?> </div> |
comment_form()|コメントフォームを出力
引数でフォームの各部分をカスタマイズできます。よく使うオプションを示します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php comment_form( array( 'title_reply' => 'コメントを書く', 'label_submit' => '送信する', 'comment_notes_before' => '<p class="comment-notes">メールアドレスは公開されません。</p>', 'fields' => array( 'author' => '<p><label for="author">名前<span class="required">*</span></label>' . '<input id="author" name="author" type="text" required></p>', 'email' => '<p><label for="email">メールアドレス<span class="required">*</span></label>' . '<input id="email" name="email" type="email" required></p>', 'url' => '', // ウェブサイトフィールドを削除 ), ) ); ?> |
20. テキスト・日付処理|wp_trim_words / wp_kses / date_i18n
結論:wp_kses_post()は「投稿本文として許可されているHTMLタグのみ通す」フィルターです。外部から取得したHTMLやユーザー入力をそのままechoする前に必ず通してください。wp_trim_words()はthe_excerpt()よりも柔軟な文字数制御が必要な場面で使います。
wp_trim_words()|指定語数で文章を切り詰める
抜粋を手動で生成したい場合や、カスタムフィールドの内容を一定の長さに収めたい場合に使います。
|
1 2 3 4 5 6 7 |
<?php $content = get_post_field( 'post_content', get_the_ID() ); // 80語(日本語の場合は文字)で切り詰め、末尾に「...」を付ける $trimmed = wp_trim_words( $content, 80, '...' ); echo esc_html( $trimmed ); ?> |
wp_kses() / wp_kses_post()|許可タグ以外のHTMLを除去
wp_kses_post()は投稿本文と同じ権限セット(p, a, strong, emなど)を通すフィルターです。外部APIから取得したHTMLや、テーマオプションに保存されたHTMLを出力する際の標準的なサニタイズ手段です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php // 投稿本文と同じ許可タグでフィルタリング $html_content = get_theme_mod( 'footer_description' ); echo wp_kses_post( $html_content ); // 許可タグを自分で指定する場合(aタグとstrongのみ許可) $allowed = array( 'a' => array( 'href' => true, 'title' => true ), 'strong' => array(), 'em' => array(), ); echo wp_kses( $user_submitted_html, $allowed ); ?> |
wp_strip_all_tags()|すべてのHTMLタグを除去
HTMLを完全にテキストに変換します。OGP用のdescriptionなど、タグが不要な場面で使います。
|
1 2 3 4 5 |
<?php $description = wp_strip_all_tags( get_the_content() ); $description = wp_trim_words( $description, 80 ); // OGPメタタグなどに使用 ?> |
date_i18n()|ロケール対応の日付フォーマット
PHPのdate()のWordPress版です。WordPress設定のタイムゾーンと言語に対応した日付を返します。日本語環境では月・曜日が日本語で出力されます。
|
1 2 3 4 5 6 7 |
<?php // 「2025年3月15日(土)」のように出力 echo esc_html( date_i18n( 'Y年n月j日(D)' ) ); // 投稿日を指定フォーマットで echo esc_html( date_i18n( 'Y年n月j日', get_the_time( 'U' ) ) ); ?> |
human_time_diff()|相対時間を表示
「3時間前」「2日前」のような相対時間を表示します。SNS風のUIや最新記事一覧でよく使われます。
|
1 2 3 4 5 6 |
<?php $post_time = get_the_time( 'U' ); $diff = human_time_diff( $post_time, current_time( 'timestamp' ) ); echo esc_html( $diff . '前' ); // 出力例:「2時間前」「3日前」 ?> |
number_format_i18n()|数値のロケール対応フォーマット
閲覧数、コメント件数などの数値をロケールに合わせたフォーマットで出力します。
|
1 2 3 4 5 |
<?php $view_count = 12345; echo esc_html( number_format_i18n( $view_count ) ); // 日本語環境では「12,345」と出力 ?> |
21. 条件分岐 応用編|is_singular / is_archive / is_sticky
結論:is_singular()はis_single()・is_page()・is_attachment()をまとめて判定できる便利な関数です。カスタム投稿タイプの個別ページを含めて「1件の投稿が表示されているか」を判定したい場面で活躍します。
| 関数 | 判定内容 | 備考 |
|---|---|---|
is_singular() |
個別投稿・固定・添付ファイルのいずれか | 投稿タイプ名を引数に絞り込み可能 |
is_archive() |
アーカイブページ全般 | カテゴリ・タグ・日付・著者を含む |
is_tax() |
カスタムタクソノミーのアーカイブ | タクソノミー名を引数で指定可能 |
is_tag() |
タグアーカイブ | – |
is_author() |
著者アーカイブ | – |
is_date() |
日付アーカイブ | is_year() / is_month() / is_day()も使える |
is_sticky() |
先頭固定表示の投稿か | ループ内で使用 |
is_paged() |
ページ分割された2ページ目以降か | canonicalタグ制御などに使う |
has_blocks() |
ブロックエディタで作成された投稿か | クラシック/ブロック切り替えに使う |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?php // カスタム投稿タイプ「book」の個別ページだけで処理 if ( is_singular( 'book' ) ) { // 書籍詳細専用の処理 } // カスタムタクソノミー「genre」のアーカイブ if ( is_tax( 'genre' ) ) { $term = get_queried_object(); echo esc_html( $term->description ); } // 先頭固定投稿にバッジを表示 if ( is_sticky() ) { echo '<span class="sticky-badge">おすすめ</span>'; } // ブロックエディタ製コンテンツにクラスを付ける if ( has_blocks() ) { // ブロック専用CSSを読み込むなど } ?> |
get_queried_object()|現在のページの主要オブジェクトを取得
カテゴリアーカイブならWP_Term、個別投稿ならWP_Post、著者アーカイブならWP_Userを返します。「今どのページにいるか」に応じた動的な処理に使います。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php // アーカイブタイトル・説明文の汎用表示 if ( is_archive() ) { $obj = get_queried_object(); if ( $obj instanceof WP_Term ) { echo esc_html( $obj->name ); echo esc_html( $obj->description ); } } // 標準関数を使う方法(より推奨) echo esc_html( get_the_archive_title() ); echo wp_kses_post( get_the_archive_description() ); ?> |
22. テーマファイルのパス・読み込み|locate_template / load_theme_textdomain
結論:get_template_directory_uri()とget_stylesheet_directory_uri()の違いは、子テーマ使用時に顕著になります。子テーマ側のファイルを優先したい場合はstylesheet系を使い、親テーマのファイルを確実に参照したい場合はtemplate系を使ってください。
get_template_directory() / get_stylesheet_directory()
| 関数 | 返す値 | 子テーマ時の挙動 |
|---|---|---|
get_template_directory() |
親テーマのフルパス | 常に親テーマを参照 |
get_stylesheet_directory() |
現在有効なテーマのフルパス | 子テーマがあれば子テーマを返す |
get_template_directory_uri() |
親テーマのURL | CSS・JS読み込みに使う |
get_stylesheet_directory_uri() |
現在有効なテーマのURL | 子テーマのCSSに使う |
get_stylesheet_uri() |
現在有効なテーマのstyle.cssのURL | wp_enqueue_style()によく使う |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php // CSS読み込み例 add_action( 'wp_enqueue_scripts', function() { // 子テーマのstyle.cssを読み込む(子テーマ時は子テーマ優先) wp_enqueue_style( 'theme-style', get_stylesheet_uri() ); // 親テーマのjsを確実に読み込む wp_enqueue_script( 'theme-js', get_template_directory_uri() . '/assets/js/main.js', array(), '1.0.0', true ); }); ?> |
locate_template()|テンプレートファイルを検索する
子テーマ→親テーマの順でファイルを探し、見つかったパスを返します。テーマのオーバーライドが効くファイル探索が必要な場面で使います。
|
1 2 3 4 5 6 7 8 9 |
<?php // ファイルのパスを取得(読み込みはしない) $template = locate_template( array( 'parts/card-product.php', 'parts/card.php' ) ); // → 子テーマに parts/card-product.php があればそのパスを返す // なければ親テーマの parts/card.php を返す // ファイルを探してそのまま読み込む(第2引数をtrueに) locate_template( 'parts/card.php', true, false ); ?> |
load_theme_textdomain()|テーマの翻訳ファイルを読み込む
テーマの翻訳(.poファイル)を有効にするために必要な設定です。配布テーマでは必須で、after_setup_themeアクション内で呼び出します。
|
1 2 3 4 5 6 |
<?php add_action( 'after_setup_theme', function() { // /wp-content/themes/mytheme/languages/ にある翻訳を読み込む load_theme_textdomain( 'mytheme', get_template_directory() . '/languages' ); } ); ?> |
get_search_form()|検索フォームを出力
テーマにsearchform.phpがある場合はそのHTMLを、なければWordPress標準のフォームHTMLを出力します。
|
1 2 3 4 5 6 7 |
<?php // ヘッダーや検索ページに追加 get_search_form(); // PHPの変数として取得したい場合 $search_form = get_search_form( array( 'echo' => false ) ); ?> |
get_search_query()|検索キーワードを安全に取得
検索結果ページで現在の検索語を取得します。内部的にエスケープ処理が行われているため、esc_attr()と組み合わせるだけで安全に出力できます。
|
1 2 3 4 |
<!-- search.php や searchform.php での使用 --> <h1> 「<?php echo esc_html( get_search_query() ); ?>」の検索結果 </h1> |
まとめ:テーマ制作で守るべき5つの原則
テーマ制作の品質は「エスケープの徹底」「ループのリセット」「テンプレート分割」「enqueue API使用」「wp_head/wp_footerの必須記述」の5原則で決まります。
この記事では、テーマ制作者が使うWordPress関数を22カテゴリ・60関数以上にわたって解説しました。実務で役立てられるよう、最重要ポイントを整理します。
| 原則 | 要点 |
|---|---|
| ✅ エスケープの徹底 | 出力時は必ずesc_html・esc_url・esc_attr・wp_kses_postを使う |
| ✅ ループのリセット | WP_Queryサブループ後は必ずwp_reset_postdata() |
| ✅ テンプレート分割 | get_template_part()でUIパーツを再利用可能にする |
| ✅ enqueue API使用 | CSS・JSの直書きを避け依存関係を正しく管理する |
| ✅ wp_head/wp_footerを省略しない | この2行を省略するとプラグインの大半が動作しなくなる |
次回のPart2では、プラグイン開発における設定画面(Settings API)・管理メニュー・メタボックス・カスタム投稿タイプ・権限管理について詳しく解説します。









コメント