2026年10月10日
2026年10月10日
WordPressでショートコードを作成する方法【実用例付き】
はじめに
ショートコードは[my_shortcode attr="value"]という形式でWordPressの投稿や固定ページに動的コンテンツを埋め込める機能です。Gutenbergへの移行が進んでいますが、テキストエディターや既存コンテンツでまだ活躍しています。
症状・原因
ショートコードが必要なケース:
- 投稿の任意の位置に動的な情報(価格表・地図・フォーム)を埋め込みたい
- 非技術者でも再利用可能なコンテンツを挿入させたい
- ウィジェットやテキストブロックから動的コンテンツを呼び出したい
- 複雑なHTMLを隠蔽してシンプルなコードで表現したい
解決手順
ステップ1:基本的なショートコードを作成する
// functions.php またはカスタムプラグイン
// シンプルなショートコード(属性なし)
add_shortcode('current_year', function(): string {
return date('Y');
});
// 使用例: [current_year] → 2024
// 属性付きショートコード
add_shortcode('button', 'button_shortcode');
function button_shortcode(array|string $atts, ?string $content = null): string {
// デフォルト値とユーザー指定値をマージ
$atts = shortcode_atts([
'url' => '#',
'color' => 'primary', // primary / secondary / danger
'size' => 'medium', // small / medium / large
'target' => '_self',
'class' => '',
], $atts, 'button');
// セキュリティ: URLをエスケープ
$url = esc_url($atts['url']);
$target = in_array($atts['target'], ['_self', '_blank'], true) ? $atts['target'] : '_self';
$class = 'btn btn-' . sanitize_html_class($atts['color'])
. ' btn-' . sanitize_html_class($atts['size']);
if ($atts['class']) {
$class .= ' ' . sanitize_html_class($atts['class']);
}
$label = $content ? esc_html(strip_tags($content)) : 'クリック';
$rel = ($target === '_blank') ? ' rel="noopener noreferrer"' : '';
return sprintf(
'<a href="%s" class="%s" target="%s"%s>%s</a>',
$url,
esc_attr($class),
esc_attr($target),
$rel,
$label
);
}
// 使用例: [button url="https://example.com" color="primary" target="_blank"]詳しく見る[/button]
ステップ2:コンテンツを囲む形式(ラッピング型)を作成する
// アラートボックスショートコード
add_shortcode('alert', 'alert_shortcode');
function alert_shortcode(array|string $atts, ?string $content = null): string {
$atts = shortcode_atts([
'type' => 'info', // info / success / warning / error
'title' => '',
], $atts, 'alert');
$type = in_array($atts['type'], ['info', 'success', 'warning', 'error'], true)
? $atts['type'] : 'info';
$icons = ['info' => 'ℹ️', 'success' => '✅', 'warning' => '⚠️', 'error' => '❌'];
$icon = $icons[$type];
$title_html = '';
if ($atts['title']) {
$title_html = '<strong class="alert-title">' . esc_html($atts['title']) . '</strong><br>';
}
// コンテンツ内のショートコードを処理
$content = do_shortcode($content);
return sprintf(
'<div class="alert alert-%s" role="alert">%s %s%s</div>',
esc_attr($type),
$icon,
$title_html,
wp_kses_post($content)
);
}
// 使用例:
// [alert type="warning" title="注意"]この操作は取り消せません[/alert]
ステップ3:データベースから動的データを表示するショートコード
// 最新の投稿一覧を表示するショートコード
add_shortcode('recent_posts', 'recent_posts_shortcode');
function recent_posts_shortcode(array|string $atts): string {
$atts = shortcode_atts([
'count' => 5,
'category' => '',
'thumbnail' => 'false',
], $atts, 'recent_posts');
// キャッシュキーを生成
$cache_key = 'sc_recent_posts_' . md5(serialize($atts));
$output = get_transient($cache_key);
if ($output === false) {
$args = [
'post_type' => 'post',
'posts_per_page' => absint($atts['count']),
'post_status' => 'publish',
'no_found_rows' => true,
];
if ($atts['category']) {
$args['category_name'] = sanitize_text_field($atts['category']);
}
$posts = get_posts($args);
$show_thumb = filter_var($atts['thumbnail'], FILTER_VALIDATE_BOOLEAN);
ob_start();
echo '<ul class="shortcode-recent-posts">';
foreach ($posts as $post) {
echo '<li>';
if ($show_thumb && has_post_thumbnail($post)) {
echo '<a href="' . esc_url(get_permalink($post)) . '">';
echo get_the_post_thumbnail($post, 'thumbnail', ['loading' => 'lazy']);
echo '</a>';
}
echo '<a href="' . esc_url(get_permalink($post)) . '">'
. esc_html(get_the_title($post)) . '</a>';
echo '<time datetime="' . esc_attr($post->post_date) . '">'
. esc_html(get_the_date('', $post)) . '</time>';
echo '</li>';
}
echo '</ul>';
$output = ob_get_clean();
// 5分間キャッシュ
set_transient($cache_key, $output, 5 * MINUTE_IN_SECONDS);
}
return $output;
}
// 使用例: [recent_posts count="3" category="news" thumbnail="true"]
ステップ4:ショートコードのセキュリティを強化する
// フォームショートコード(非ログインユーザー向け)
add_shortcode('contact_mini', 'contact_mini_shortcode');
function contact_mini_shortcode(array|string $atts): string {
// 既にログイン済みなら別のメッセージを表示
if (is_user_logged_in()) {
return '<p class="notice">ログイン中のユーザーはこのフォームを使用できません。</p>';
}
$atts = shortcode_atts(['title' => 'お問い合わせ'], $atts, 'contact_mini');
$nonce = wp_create_nonce('contact_mini_nonce');
return sprintf('
<form class="shortcode-contact-form" method="post">
<h3>%s</h3>
<input type="hidden" name="contact_mini_nonce" value="%s">
<input type="hidden" name="action" value="contact_mini_submit">
<label>お名前<input type="text" name="contact_name" required></label>
<label>メール<input type="email" name="contact_email" required></label>
<label>メッセージ<textarea name="contact_message" rows="4" required></textarea></label>
<button type="submit">送信する</button>
</form>',
esc_html($atts['title']),
esc_attr($nonce)
);
}
// フォーム処理(init アクション)
add_action('init', 'handle_contact_mini_submit');
function handle_contact_mini_submit(): void {
if (!isset($_POST['action']) || $_POST['action'] !== 'contact_mini_submit') return;
if (!wp_verify_nonce($_POST['contact_mini_nonce'] ?? '', 'contact_mini_nonce')) {
wp_die('不正なリクエストです');
}
$name = sanitize_text_field($_POST['contact_name'] ?? '');
$email = sanitize_email($_POST['contact_email'] ?? '');
$message = sanitize_textarea_field($_POST['contact_message'] ?? '');
wp_mail(get_option('admin_email'), "お問い合わせ: {$name}", "{$email}\n\n{$message}");
wp_safe_redirect(add_query_arg('sent', '1', wp_get_referer()));
exit;
}
ステップ5:ショートコードをデバッグ・管理する
# WP-CLIでショートコードを実行(確認用)
wp eval "echo do_shortcode('[recent_posts count=3]');"
# 登録されているショートコード一覧を確認
wp eval "global \$shortcode_tags; echo implode(', ', array_keys(\$shortcode_tags));"
# 特定の投稿に含まれるショートコードを確認
wp post get 123 --field=post_content | grep -oE '\[[a-z_]+[^\]]*\]'
注意事項
- ショートコードのコールバックは必ず文字列を
returnしてください(echoは使わない) - ユーザー入力値は必ず
shortcode_atts()でフィルタリングし、出力時はesc_html()等でエスケープしてください - Gutenbergブロックエディターでは
do_shortcode()が実行されないことがあります
まとめ
ショートコードの作成は、①add_shortcode()での登録、②shortcode_atts()でデフォルト値設定と属性マージ、③出力値の適切なエスケープ(esc_html/esc_url/wp_kses_post)、④動的データはTransientでキャッシュ、⑤フォームはnonce検証の流れで実装します。