• TOP
  • 記事一覧
  • 【WordPress】SVGを投稿記事のサムネイルに!自作プラグイン(未実装🤷‍♂️)「nonce(ナンス)トークン」wp_postmetaテーブルを利用

【WordPress】SVGを投稿記事のサムネイルに!自作プラグイン(未実装🤷‍♂️)「nonce(ナンス)トークン」wp_postmetaテーブルを利用

更新日:2025/02/14

試した内容:

  1. メタデータへのSVGコードの保存(成功)
  2. フックの優先度調整(1-99で試行)
  3. 初期化タイミングの調整(plugins_loaded, init, after_setup_theme)
  4. サムネイル表示フィルターの複数追加(post_thumbnail_html, get_post_thumbnail_html)
  5. デバッグログの追加と分析

考えられる問題点:

  1. テーマのサムネイル表示処理が標準的でない可能性
    • 独自のサムネイル表示関数を使用している可能性
    • フィルターをバイパスしている可能性
  2. フックのタイミング問題
    • サムネイル表示時に既にフィルターが実行済みの可能性
    • テーマの読み込みタイミングとの不整合
  3. テンプレート側の制約
    • CSSの競合
    • テンプレートが特定の画像形式を想定している

推奨される次のステップ:

  1. テーマの functions.php でサムネイル表示がどのように処理されているか確認
  2. テーマ側でサムネイルを表示している箇所のコードを確認
  3. get_the_post_thumbnail() の代わりに独自関数を使用することを検討

提案される検証方法:

  1. デフォルトテーマ(Twenty Twenty-Four など)での動作確認
  2. テーマの サムネイル表示部分に直接デバッグコードを追加
  3. フィルターが実際に呼び出されているか確認するためのテスト関数の追加

処理内容

プラグイン読み込み ~ クラス初期化の流れ

プラグイン読み込み get_instance()
(Singleton) __construct()
initフック登録

メタボックスの追加 ~ 投稿画面での入力

メタボックスは記事編集画面でSVGコード入力用のエリアのUIのことです

add_meta_boxes
でメタボックス追加 投稿編集画面
メタボックス
nonce生成
SVGコード入力

nonce は “Number used once“(一度だけ使われる数値)という意味で、フォーム送信時のCSRF対策改ざん防止のために使われる トークン(合言葉) です

「nonceを生成し、**隠しフィールド(input type=”hidden”)**として出力」

⬇️

第三者が不正なURLで投稿を勝手に更新したり、ボタン押下の偽装を行うといった攻撃を防ぐ

render_meta_box (wp_nonce_fieldでnonceを出力) 投稿画面フォーム 保存押下 save_post フック (wp_verify_nonceでnonce検証)

投稿保存時の処理

save_postフック Nonce検証
権限チェック update_post_meta
(_svg_code_thumbnail)

メタボックスで送信されたSVGコードは、記事のカラム(wp_posts内)に直接追加されるのではなく、wp_postmetaテーブルに「メタデータ」として格納される。

wp_postmeta テーブルとは

1つの投稿に対して複数の追加情報を柔軟に保存できる仕組みです

wp_posts ID: 123 post_title: “サンプル投稿” post_content: “本文…” wp_postmeta meta_id: 1 post_id: 123 meta_key: “_thumbnail_id” meta_value: “456” meta_id: 2 post_id: 123 meta_key: “price” meta_value: “1000” meta_id: 3 post_id: 123 meta_key: “color” meta_value: “red” post_id で wp_posts テーブルと紐付け

投稿IDをキーとして紐づけています。

+-----------+---------+----------------------+---------------+
| meta_id   | post_id | meta_key            | meta_value    |
+-----------+---------+----------------------+---------------+
| (自動ID)  | 123     | _svg_code_thumbnail | <svg>...</svg>|
+-----------+---------+----------------------+---------------+

デバッグすべきポイント

サムネイル置換とフロント表示

has_post_thumbnail
(SVGメタ存在を考慮) post_thumbnail_html SVGコード
を出力
<?php
/*
Plugin Name: SVG Code Thumbnail Fixed
Description: SVGコードを直接サムネイルとして使用(修正版10)
Version: 2.6
Author: Ida
*/

if (!defined('ABSPATH')) exit;

class SVG_Code_Thumbnail {
    private static $instance = null;
    private $nonce_action = 'svg_code_nonce_action';
    private $nonce_name = 'svg_code_nonce_field';
    private $meta_key = '_svg_code_thumbnail';
    private $debug = true;
    private $current_user;
    private $initialized = false;

    public static function get_instance() {
        if (null === self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __construct() {
        $this->log_debug('プラグインインスタンス作成開始');

        if (!function_exists('wp_get_current_user')) {
            require_once(ABSPATH . 'wp-includes/pluggable.php');
        }
        
        $user = wp_get_current_user();
        $this->current_user = $user->ID ? $user->user_login : 'unknown';
        
        $this->log_debug('ユーザー情報取得', [
            'user' => $this->current_user,
            'time' => current_time('mysql')
        ]);

        // initフックで初期化
        add_action('init', [$this, 'initialize'], 0);
    }

    public function initialize() {
        if ($this->initialized) {
            return;
        }

        $this->initialized = true;
        $this->log_debug('プラグイン初期化開始');

        // メタボックス関連
        add_action('add_meta_boxes', [$this, 'add_meta_box']);
        add_action('save_post', [$this, 'save_svg'], 10, 2);

        // サムネイル関連
        add_filter('post_thumbnail_html', [$this, 'replace_thumbnail'], 10, 5);
        add_filter('has_post_thumbnail', [$this, 'check_thumbnail'], 10, 2);
        add_filter('wp_kses_allowed_html', [$this, 'allow_svg'], 10, 2);

        // スタイル
        if (is_admin()) {
            add_action('admin_head', [$this, 'admin_styles']);
        } else {
            add_action('wp_head', [$this, 'front_styles']);
        }

        $this->log_debug('プラグイン初期化完了');
    }

    private function log_debug($message, $data = null) {
        if ($this->debug) {
            $timestamp = current_time('mysql');
            $user = $this->current_user ?? 'unknown';
            error_log("[{$timestamp}] [{$user}] SVG_DEBUG: {$message}");
            if ($data !== null) {
                error_log("[{$timestamp}] [{$user}] SVG_DEBUG_DATA: " . print_r($data, true));
            }
        }
    }

    public function add_meta_box() {
        $this->log_debug('メタボックス追加開始');
        
        $post_types = ['post', 'page', 'recipe'];
        foreach ($post_types as $post_type) {
            add_meta_box(
                'svg_code_thumbnail',
                'SVGサムネイル',
                [$this, 'render_meta_box'],
                $post_type,
                'side',
                'high'
            );
        }
        
        $this->log_debug('メタボックス追加完了');
    }

    public function render_meta_box($post) {
        $this->log_debug('メタボックスレンダリング開始', ['post_id' => $post->ID]);
        
        wp_nonce_field($this->nonce_action, $this->nonce_name);
        
        $svg_code = get_post_meta($post->ID, $this->meta_key, true);
        ?>
        <div class="svg-code-editor">
            <p>
                <label for="svg_code_input">SVGコードを入力:</label>
                <textarea id="svg_code_input" 
                          name="svg_code_input" 
                          class="large-text code"
                          rows="8"
                          placeholder="ここにSVGコードを入力してください"><?php echo esc_textarea($svg_code); ?></textarea>
            </p>
            <?php if (!empty($svg_code)) : ?>
                <div class="svg-preview">
                    <p>プレビュー:</p>
                    <?php echo wp_kses($svg_code, $this->allow_svg([], 'post')); ?>
                </div>
                <p class="description">
                    最終更新: <?php echo get_the_modified_time('Y-m-d H:i:s', $post->ID); ?><br>
                    更新者: <?php echo get_the_modified_author(); ?>
                </p>
            <?php endif; ?>
        </div>
        <?php
        
        $this->log_debug('メタボックスレンダリング完了', ['post_id' => $post->ID]);
    }

    public function save_svg($post_id, $post) {
        try {
            $this->log_debug('保存処理開始', [
                'post_id' => $post_id,
                'post_type' => $post->post_type,
                'user' => $this->current_user
            ]);

            if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
                $this->log_debug('自動保存をスキップ');
                return;
            }

            if (!isset($_POST[$this->nonce_name])) {
                $this->log_debug('Nonceが見つかりません');
                return;
            }

            if (!wp_verify_nonce($_POST[$this->nonce_name], $this->nonce_action)) {
                $this->log_debug('Nonce検証失敗');
                return;
            }

            if (!current_user_can('edit_post', $post_id)) {
                $this->log_debug('権限不足');
                return;
            }

            if (isset($_POST['svg_code_input'])) {
                $svg_code = wp_unslash($_POST['svg_code_input']);
                $this->log_debug('SVGコード受信', ['length' => strlen($svg_code)]);

                if (!empty($svg_code)) {
                    $sanitized_svg = wp_kses($svg_code, $this->allow_svg([], 'post'));
                    $result = update_post_meta($post_id, $this->meta_key, $sanitized_svg);
                    
                    $this->log_debug('SVG保存結果', [
                        'success' => $result ? 'true' : 'false',
                        'meta_key' => $this->meta_key
                    ]);
                } else {
                    delete_post_meta($post_id, $this->meta_key);
                    $this->log_debug('空のSVGコード - メタ削除');
                }
            }
        } catch (Exception $e) {
            $this->log_debug('エラー発生', [
                'message' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
        }
    }

    public function replace_thumbnail($html, $post_id, $thumbnail_id, $size, $attr) {
        $svg_code = get_post_meta($post_id, $this->meta_key, true);
        
        if (!empty($svg_code)) {
            $this->log_debug('SVGサムネイル置換', [
                'post_id' => $post_id,
                'size' => $size
            ]);

            $width = 150;
            $height = 150;

            if (is_array($size)) {
                $width = $size[0];
                $height = $size[1];
            }

            return sprintf(
                '<div class="svg-thumbnail" style="width: %dpx; height: %dpx;">%s</div>',
                esc_attr($width),
                esc_attr($height),
                wp_kses($svg_code, $this->allow_svg([], 'post'))
            );
        }

        return $html;
    }

    public function check_thumbnail($has_thumbnail, $post_id) {
        $has_svg = !empty(get_post_meta($post_id, $this->meta_key, true));
        
        $this->log_debug('サムネイルチェック', [
            'post_id' => $post_id,
            'has_thumbnail' => $has_thumbnail,
            'has_svg' => $has_svg
        ]);
        
        return $has_thumbnail || $has_svg;
    }

    public function allow_svg($tags, $context) {
        return array_merge($tags, [
            'svg' => [
                'xmlns' => true,
                'viewbox' => true,
                'width' => true,
                'height' => true,
                'class' => true,
                'style' => true,
                'fill' => true,
                'preserveAspectRatio' => true
            ],
            'path' => [
                'd' => true,
                'fill' => true,
                'style' => true,
                'stroke' => true,
                'stroke-width' => true
            ],
            'circle' => [
                'cx' => true,
                'cy' => true,
                'r' => true,
                'fill' => true,
                'style' => true
            ],
            'rect' => [
                'x' => true,
                'y' => true,
                'width' => true,
                'height' => true,
                'fill' => true,
                'style' => true
            ],
            'g' => [
                'fill' => true,
                'transform' => true,
                'style' => true
            ]
        ]);
    }

    public function front_styles() {
        ?>
        <style>
            .svg-thumbnail {
                display: block;
                overflow: hidden;
                background: #f8f9fa;
                margin: 0 auto;
            }
            .svg-thumbnail svg {
                width: 100%;
                height: 100%;
                display: block;
                object-fit: contain;
            }
        </style>
        <?php
    }

    public function admin_styles() {
        ?>
        <style>
            .svg-code-editor {
                margin: 1em 0;
            }
            .svg-code-editor textarea {
                resize: vertical;
                font-family: monospace;
                margin-bottom: 1em;
            }
            .svg-preview {
                background: #f8f9fa;
                padding: 15px;
                margin: 1em 0;
                border: 1px solid #ddd;
                border-radius: 4px;
            }
            .svg-preview svg {
                max-width: 100%;
                height: auto;
                display: block;
                margin: 0 auto;
            }
            .description {
                color: #666;
                font-style: italic;
                margin-top: 0.5em;
            }
        </style>
        <?php
    }
}

// プラグインの初期化
function init_svg_thumbnail() {
    return SVG_Code_Thumbnail::get_instance();
}

add_action('plugins_loaded', 'init_svg_thumbnail', 0);
  1. 主な機能:
  • 投稿編集画面にSVGサムネイル入力用のメタボックスを追加
  • SVGコードの安全な保存と表示
  • プレビュー機能
  • 既存のサムネイル表示システムとの統合
  1. 使用方法:
  • プラグインを有効化
  • 投稿編集画面でSVGコードを入力
  • 通常のサムネイル表示箇所にSVGが表示される
  1. セキュリティ対策:
  • SVGコードのサニタイズ
  • 許可する要素と属性の制限
  • 適切なユーザー権限チェック
人気記事ランキング
話題のキーワードから探す