はじめに
Cocoon設定画面の項目は非常に多岐にわたります。
スキン制御などを定義する場合、従来はブラウザのデベロッパーツールを開き、膨大なHTMLの中から該当するキー(name属性)を一つひとつ探す必要があり、非常に手間がかかる作業でした。
そこで、Cocoon設定の各項目に対応するname属性を、ダッシュボード上でタブ・セクションごとに一覧表示できるようにしました。
これにより、カスタマイズの際にCocoon設定画面のHTMLを調べることなく、目的の設定項目とname属性をすぐに確認できます。
この記事では、この方法について説明します。
完成イメージ

実装手順
以下のコードをfunctions.phpに追加します。
// 管理画面メニューを追加
add_action('admin_menu', function () {
add_submenu_page(
THEME_SETTINGS_PAGE,
'Cocoon設定name属性一覧',
'Cocoon設定name属性一覧',
'manage_options',
'cocoon_setting_parse',
'render_cocoon_setting_parse'
);
}, 99);
// 管理画面用JS・CSSを追加
add_action('admin_enqueue_scripts', function (string $hook_suffix) {
if (($_GET['page'] ?? '') !== 'cocoon_setting_parse') {
return;
}
ob_start();
?>
.cocoon-setting-parse__tabs {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin: 20px 0 16px;
}
.cocoon-setting-parse__tab {
display: inline-block;
padding: 6px 12px;
border: 1px solid #ccc;
background: #fff;
color: #333;
font-size: 13px;
font-weight: 400;
cursor: pointer;
}
.cocoon-setting-parse__tab:hover {
background: #f0f0f0;
}
.cocoon-setting-parse__tab.is-active {
background: #2271b1;
border-color: #2271b1;
color: #fff;
font-weight: 400;
}
.cocoon-setting-parse__content {
display: none;
}
.cocoon-setting-parse__content.is-active {
display: block;
}
.cocoon-setting-parse__section {
margin: 20px 0 30px;
}
.cocoon-setting-parse__section h2 {
margin: 0;
padding: 8px 12px;
}
.cocoon-setting-parse__table {
table-layout: fixed;
width: 100%;
}
.cocoon-setting-parse__table th:nth-child(1),
.cocoon-setting-parse__table td:nth-child(1) {
width: 40%;
}
.cocoon-setting-parse__table td {
font-weight: normal;
}
.cocoon-setting-parse__name {
word-break: break-all;
}
<?php
wp_add_inline_style('wp-admin', ob_get_clean());
ob_start();
?>
jQuery(function($) {
$('.cocoon-setting-parse__tab').on('click', function () {
const $tab = $(this);
const target = $tab.data('target');
$('.cocoon-setting-parse__tab').removeClass('is-active');
$tab.addClass('is-active');
$('.cocoon-setting-parse__content').removeClass('is-active');
$(target).addClass('is-active');
});
});
<?php
wp_add_inline_script('jquery', ob_get_clean());
});
/**
* 解析対象の設定フォーム関数一覧とラベル引数位置を取得
*
* @return array<string, array{label_argument: int|null}>
*/
if (!function_exists('get_cocoon_setting_parse_form_functions')):
function get_cocoon_setting_parse_form_functions() {
return [
'generate_textbox_tag' => ['label_argument' => 2],
'generate_textarea_tag' => ['label_argument' => 2],
'generate_selectbox_tag' => ['label_argument' => 3],
'generate_checkbox_tag' => ['label_argument' => 2],
'generate_radiobox_tag' => ['label_argument' => null],
'generate_color_picker_tag' => ['label_argument' => 2],
'generate_range_tag' => ['label_argument' => null],
'generate_number_tag' => ['label_argument' => null],
'generate_upload_image_tag' => ['label_argument' => null],
'generate_upload_file_tag' => ['label_argument' => null],
'generate_code_editor_tag' => ['label_argument' => null],
];
}
endif;
/**
* Cocoonのsettings-tabs.phpのファイルパスを取得
*
* @return string ファイルが存在すればパス、存在しなければ空文字
*/
if (!function_exists('get_cocoon_setting_parse_tabs_file')):
function get_cocoon_setting_parse_tabs_file() {
$file = get_cocoon_template_directory() . '/lib/page-settings/settings-tabs.php';
if (is_file($file) && is_readable($file)) {
return $file;
}
return '';
}
endif;
/**
* Cocoon設定タブの配列を取得
*
* @return array<string, array> タブ情報の配列
*/
if (!function_exists('get_cocoon_setting_parse_tabs')):
function get_cocoon_setting_parse_tabs() {
$file = get_cocoon_setting_parse_tabs_file();
if (!$file) {
return [];
}
if (!function_exists('cocoon_get_settings_tabs')) {
require_once $file;
}
if (!function_exists('cocoon_get_settings_tabs')) {
return [];
}
$tabs = cocoon_get_settings_tabs();
return is_array($tabs) ? $tabs : [];
}
endif;
/**
* 指定されたファイルのPHPソースコードを取得
*
* @param string $file ファイルパス
* @return string ソースコード文字列
*/
if (!function_exists('get_cocoon_setting_parse_source')):
function get_cocoon_setting_parse_source(string $file) {
if (!is_file($file) || !is_readable($file)) {
return '';
}
$source = file_get_contents($file);
return is_string($source) ? $source : '';
}
endif;
/**
* PHPの式文字列から評価後の文字列値を抽出
*
* @param string $expression 式文字列
* @return string 評価後の文字列
*/
if (!function_exists('get_cocoon_setting_parse_expression_value')):
function get_cocoon_setting_parse_expression_value(string $expression) {
$expression = trim($expression);
if ($expression === '') {
return '';
}
// 文字列連結
$parts = preg_split('/\s*\.\s*/', $expression);
if (count($parts) > 1) {
$value = '';
foreach ($parts as $part) {
$part_value = get_cocoon_setting_parse_expression_value($part);
if ($part_value === '') {
return '';
}
$value .= $part_value;
}
return $value;
}
// 定数
if (preg_match('/^([A-Z_][A-Z0-9_]*)$/', $expression, $matches)) {
if (defined($matches[1])) {
return (string) constant($matches[1]);
}
return $matches[1];
}
// 文字列
if (preg_match('/^[\'"]((?:\\\\.|[^\'"])*)[\'"]$/s', $expression, $matches)) {
return stripcslashes($matches[1]);
}
// 翻訳関数
if (preg_match('/^(?:__|_e|esc_html__|esc_attr__|esc_html_e|esc_attr_e)\s*\(\s*[\'"]((?:\\\\.|[^\'"])*)[\'"]/s', $expression, $matches)) {
return stripcslashes($matches[1]);
}
// 式中の文字列
if (preg_match('/[\'"]((?:\\\\.|[^\'"])*)[\'"]/', $expression, $matches)) {
return stripcslashes($matches[1]);
}
return '';
}
endif;
/**
* 関数呼び出しの括弧から引数一覧を解析して抽出
*
* @param string $source ソースコード
* @param int $open_position 開始括弧の位置
* @return array<int, string> 引数の文字列配列
*/
if (!function_exists('get_cocoon_setting_parse_call_arguments')):
function get_cocoon_setting_parse_call_arguments(string $source, int $open_position) {
$length = strlen($source);
$depth = 1;
$quote = '';
$escape = false;
$argument = '';
$arguments = [];
for ($i = $open_position + 1; $i < $length; $i++) {
$char = $source[$i];
if ($quote !== '') {
$argument .= $char;
if ($escape) {
$escape = false;
} elseif ($char === '\\') {
$escape = true;
} elseif ($char === $quote) {
$quote = '';
}
continue;
}
if ($char === "'" || $char === '"') {
$quote = $char;
$argument .= $char;
continue;
}
if ($char === '(' || $char === '[' || $char === '{') {
$depth++;
$argument .= $char;
continue;
}
if ($char === ')' || $char === ']' || $char === '}') {
$depth--;
if ($depth === 0) {
if (trim($argument) !== '') {
$arguments[] = trim($argument);
}
break;
}
$argument .= $char;
continue;
}
if ($char === ',' && $depth === 1) {
$arguments[] = trim($argument);
$argument = '';
continue;
}
$argument .= $char;
}
return $arguments;
}
endif;
/**
* 関数引数または行ラベルから設定項目名を取得
*
* @param string $function_name 関数名
* @param array $arguments 引数配列
* @param string $row_label デフォルトの行ラベル
* @return string 設定項目名
*/
if (!function_exists('get_cocoon_setting_parse_item_label')):
function get_cocoon_setting_parse_item_label(string $function_name, array $arguments, string $row_label) {
$functions = get_cocoon_setting_parse_form_functions();
if (!isset($functions[$function_name])) {
return $row_label;
}
$label_argument = $functions[$function_name]['label_argument'];
if ($label_argument === null || !isset($arguments[$label_argument])) {
return $row_label;
}
$expression = trim($arguments[$label_argument]);
// 翻訳関数または文字列を取得
if (preg_match('/^(?:__|_e|esc_html__|esc_attr__|esc_html_e|esc_attr_e)\s*\(/', $expression) || preg_match('/^[\'"]((?:\\\\.|[^\'"])*)[\'"]$/s', $expression)) {
$label = get_cocoon_setting_parse_expression_value($expression);
if ($label !== '') {
return $label;
}
}
return $row_label;
}
endif;
/**
* フォームソースから各生成関数を解析して設定項目一覧を取得
*
* @param string $source 解析対象のソースコード
* @param string $row_label 行ラベル
* @return array<int, array{label: string, name: string}> 項目情報の配列
*/
if (!function_exists('get_cocoon_setting_parse_form_items')):
function get_cocoon_setting_parse_form_items(string $source, string $row_label) {
$items = [];
$names = [];
$functions = get_cocoon_setting_parse_form_functions();
preg_match_all('/\b(generate_[a-zA-Z0-9_]+_tag)\s*\(/', $source, $matches, PREG_OFFSET_CAPTURE);
foreach ($matches[1] as $match) {
$function_name = $match[0];
$function_position = $match[1];
if (!isset($functions[$function_name])) {
continue;
}
$open_position = strpos($source, '(', $function_position);
if ($open_position === false) {
continue;
}
$arguments = get_cocoon_setting_parse_call_arguments($source, $open_position);
if (!isset($arguments[0])) {
continue;
}
// name属性を取得
$name = get_cocoon_setting_parse_expression_value($arguments[0]);
if ($name === '' || $name === 'submit') {
continue;
}
// 同一の重複を除外
if (isset($names[$name])) {
continue;
}
$names[$name] = true;
// 設定項目名を取得
$label = get_cocoon_setting_parse_item_label($function_name, $arguments, $row_label);
$items[] = [
'label' => $label,
'name' => $name,
];
}
return $items;
}
endif;
/**
* テーブル行のソースコードからgenerate_label_tagのラベルを取得
*
* @param string $row_source 行のソースコード
* @return string ラベル文字列
*/
if (!function_exists('get_cocoon_setting_parse_row_label')):
function get_cocoon_setting_parse_row_label(string $row_source) {
if (!preg_match('/\bgenerate_label_tag\s*\(/', $row_source, $match, PREG_OFFSET_CAPTURE)) {
return '';
}
$function_position = $match[0][1];
$open_position = strpos($row_source, '(', $function_position);
if ($open_position === false) {
return '';
}
$arguments = get_cocoon_setting_parse_call_arguments($row_source, $open_position);
if (!isset($arguments[1])) {
return '';
}
return get_cocoon_setting_parse_expression_value($arguments[1]);
}
endif;
/**
* h2タグの要素内ソースコードからラベル文字を抽出
*
* @param string $source h2タグの内部ソースコード
* @return string 抽出されたラベル
*/
if (!function_exists('get_cocoon_setting_parse_h2_label')):
function get_cocoon_setting_parse_h2_label(string $source) {
$source = trim($source);
if ($source === '') {
return '';
}
// 翻訳関数から取得
if (preg_match('/(?:__|_e|esc_html__|esc_attr__|esc_html_e|esc_attr_e)\s*\(\s*[\'"]((?:\\\\.|[^\'"])*)[\'"]/s', $source, $matches)) {
return stripcslashes($matches[1]);
}
// PHPコードを除去
$source = preg_replace('/<\?(?:php)?[\s\S]*?\?>/i', '', $source);
return trim(wp_strip_all_tags($source));
}
endif;
/**
* ソースコード内のtrタグ単位で設定項目を解析して取得
*
* @param string $source 解析対象のソースコード
* @return array<int, array{label: string, name: string}> 項目情報の配列
*/
if (!function_exists('get_cocoon_setting_parse_rows')):
function get_cocoon_setting_parse_rows(string $source) {
$items = [];
preg_match_all('/<tr\b[^>]*>(.*?)<\/tr>/is', $source, $rows);
foreach ($rows[1] as $row_source) {
$row_label = get_cocoon_setting_parse_row_label($row_source);
$row_items = get_cocoon_setting_parse_form_items($row_source, $row_label);
foreach ($row_items as $item) {
$items[] = $item;
}
}
return $items;
}
endif;
/**
* ソースコード内のh2タグ単位でセクション分割し設定項目を取得
*
* @param string $source 解析対象のソースコード
* @return array<int, array{label: string, items: array}> セクション情報の配列
*/
if (!function_exists('get_cocoon_setting_parse_sections')):
function get_cocoon_setting_parse_sections(string $source) {
$sections = [];
preg_match_all('/<h2\b[^>]*>(.*?)<\/h2>/is', $source, $matches, PREG_OFFSET_CAPTURE);
$count = count($matches[0]);
// h2がない場合
if ($count === 0) {
$items = get_cocoon_setting_parse_rows($source);
if (!empty($items)) {
$sections[] = [
'label' => '',
'items' => $items,
];
}
return $sections;
}
// h2単位で取得
for ($i = 0; $i < $count; $i++) {
$h2_source = $matches[1][$i][0];
$h2_start = $matches[0][$i][1];
$h2_end = $h2_start + strlen($matches[0][$i][0]);
$next_h2_start = strlen($source);
if (isset($matches[0][$i + 1])) {
$next_h2_start = $matches[0][$i + 1][1];
}
$section_source = substr($source, $h2_end, $next_h2_start - $h2_end);
$h2_label = get_cocoon_setting_parse_h2_label($h2_source);
$items = get_cocoon_setting_parse_rows($section_source);
if (empty($items)) {
continue;
}
$sections[] = [
'label' => $h2_label,
'items' => $items,
];
}
return $sections;
}
endif;
/**
* 指定された設定フォームファイルを解析
*
* @param string $file フォームファイルのパス
* @return array セクション情報の配列
*/
if (!function_exists('parse_cocoon_setting_parse_form')):
function parse_cocoon_setting_parse_form(string $file) {
$source = get_cocoon_setting_parse_source($file);
if ($source === '') {
return [];
}
return get_cocoon_setting_parse_sections($source);
}
endif;
/**
* 管理画面ページを出力
*
* @param array $items
* @return string
*/
if (!function_exists('render_cocoon_setting_parse_table')):
function render_cocoon_setting_parse_table(array $items) {
if (empty($items)) {
return '';
}
ob_start();
?>
<table class="widefat striped cocoon-setting-parse__table">
<thead>
<tr>
<th>設定項目</th>
<th>name属性</th>
</tr>
</thead>
<tbody>
<?php foreach ($items as $item): ?>
<tr>
<td><?= esc_html($item['label']) ?></td>
<td class="cocoon-setting-parse__name"><?= esc_html($item['name']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php
return ob_get_clean();
}
endif;
/**
* 単一タブコンテンツのHTMLを生成
*
* @param string $tab_id タブID
* @param array $tab タブのデータ配列
* @param string $first_tab_id 先頭タブのID
* @param string $form_directory フォームディレクトリパス
* @return string 生成されたHTML文字列
*/
if (!function_exists('render_cocoon_setting_parse_tab_content')):
function render_cocoon_setting_parse_tab_content(string $tab_id, array $tab, string $first_tab_id, string $form_directory) {
$is_active = ($tab_id === $first_tab_id);
$active_class = $is_active ? ' is-active' : '';
$forms = (isset($tab['forms']) && is_array($tab['forms'])) ? $tab['forms'] : [];
$sections = [];
foreach ($forms as $form_file) {
$file = $form_directory . '/' . $form_file;
if (!is_file($file) || !is_readable($file)) {
continue;
}
$form_sections = parse_cocoon_setting_parse_form($file);
foreach ($form_sections as $section) {
if (!empty($section['items'])) {
$sections[] = $section;
}
}
}
ob_start();
?>
<div id="cocoon-setting-parse-<?= esc_attr($tab_id) ?>" class="cocoon-setting-parse__content<?= $active_class ?>">
<?php foreach ($sections as $section): ?>
<div class="cocoon-setting-parse__section">
<?php if ($section['label'] !== ''): ?>
<h2 class="hndle"><?= esc_html($section['label']) ?></h2>
<?php endif; ?>
<?= render_cocoon_setting_parse_table($section['items']) ?>
</div>
<?php endforeach; ?>
</div>
<?php
return ob_get_clean();
}
endif;
/**
* 管理画面の設定ページ全体を表示
*
* @return void
*/
if (!function_exists('render_cocoon_setting_parse')):
function render_cocoon_setting_parse() {
if (!current_user_can('manage_options')) {
wp_die('このページにアクセスする権限がありません。');
}
$tabs_file = get_cocoon_setting_parse_tabs_file();
if (!$tabs_file) {
echo '<div class="wrap"><h1>Cocoon設定name属性一覧</h1><div class="notice notice-error"><p>settings-tabs.phpを取得できません。</p></div></div>';
return;
}
$tabs = get_cocoon_setting_parse_tabs();
if (empty($tabs)) {
echo '<div class="wrap"><h1>Cocoon設定name属性一覧</h1><div class="notice notice-error"><p>Cocoon設定タブを取得できません。</p></div></div>';
return;
}
$first_tab_id = (string) array_key_first($tabs);
$form_directory = dirname($tabs_file);
ob_start();
?>
<div class="wrap">
<h1>Cocoon設定name属性一覧</h1>
<div class="cocoon-setting-parse__tabs">
<?php foreach ($tabs as $tab_id => $tab): ?>
<?php
$label = $tab['label'] ?? $tab_id;
$active_class = ($tab_id === $first_tab_id) ? ' is-active' : '';
?>
<button type="button" class="cocoon-setting-parse__tab<?= $active_class ?>" data-target="#cocoon-setting-parse-<?= esc_attr($tab_id) ?>">
<?= esc_html($label) ?>
</button>
<?php endforeach; ?>
</div>
<?php foreach ($tabs as $tab_id => $tab): ?>
<?= render_cocoon_setting_parse_tab_content((string)$tab_id, $tab, $first_tab_id, $form_directory) ?>
<?php endforeach; ?>
</div>
<?php
echo ob_get_clean();
}
endif;
さいごに
今回紹介した方法を使えば、ブラウザのデベロッパーツールを行ったり来たりするストレスから解放され、スキン制御やテーマカスタマイズのスピードが格段に向上します。
膨大な設定項目が一覧化されることで、開発時のタイピングミスを防ぎ、より正確なコードの実装が可能になります。


