きっかけ:PDFのサムネイルが欲しい
WordPressのメディアライブラリではPDFがすべて同じアイコンで表示され、中身の区別がつきません。既存プラグインも条件に合わなかったので、自分で作ることにしました。 私のサイトでは、PDFファイルを頻繁にアップロードします。カタログ、マニュアル、レポート…。 でも、WordPressのメディアライブラリを開くと、PDFはすべて同じアイコン。
開発環境の準備
Local by Flywheelでローカル環境を構築し、名前空間とオートローダーを使ったクラス設計でプラグインの基本構造を組みました。 まずはローカル環境の構築から。私はLocal by Flywheelを使っています。|
1 2 3 4 5 |
必要なもの: - Local by Flywheel(ローカルWordPress環境) - Cursor(エディタ) - - PHP 7.4以上 - - ImageMagick(PDF変換用) |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
rapls-pdf-image-creator/ ├── rapls-pdf-image-creator.php # メインファイル ├── includes/ │ ├── Plugin.php # シングルトンのエントリーポイント │ ├── Generator.php # PDF→画像変換のコア │ ├── Settings.php # 設定値の管理 │ ├── Admin.php # 管理画面 │ ├── MediaLibrary.php # メディアライブラリ連携 │ └── Engine/ │ └── ImagickEngine.php # ImageMagick実装 ├── admin/ │ ├── views/ # 設定画面のテンプレート │ ├── css/ # 管理画面用CSS │ └── js/ # 管理画面用JS └── languages/ # 翻訳ファイル |
最初の壁:PDF変換エンジン
PDF→画像変換にはImageMagickとGhostScriptの2つの方法がありますが、後述の審査でexec()が使用禁止となり、最終的にImageMagick一本に絞ることになりました。 PDFを画像に変換する方法は主に2つあります:- ImageMagick(Imagick PHP拡張) – PHPから直接呼び出せる
- GhostScript – コマンドラインツール、exec()で実行
機能実装:こだわったポイント
PDFアップロード時の自動サムネイル生成、アイキャッチ画像としての設定、既存PDFの一括処理の3つにこだわりました。1. アップロード時の自動生成
PDFをアップロードしたら、何もしなくてもサムネイルが生成される。これが一番こだわったポイントです。add_attachmentフックを使って、PDFがアップロードされたタイミングを検知:
|
1 2 3 4 5 6 |
add_action('add_attachment', function($attachmentId) { $mime = get_post_mime_type($attachmentId); if ($mime === 'application/pdf') { // サムネイル生成処理 } }); |

2. アイキャッチ画像として設定
生成したサムネイルをPDFの「アイキャッチ画像」として設定できるようにしました。 これでget_post_thumbnail_id()がPDF添付ファイルでも使えるようになります。テーマ開発者にとっては嬉しい機能のはず。
3. 既存PDFの一括処理
プラグインを有効化する前にアップロードしたPDFはどうする? 「一括生成」機能を実装しました。AJAXで1件ずつ処理して、プログレスバーで進捗を表示。
WordPress.org審査:地獄の始まり
審査ではexec()使用禁止、プレフィックス不足、プラグイン名の一般性、error_log()のパス露出など7項目を指摘されました。セキュリティ系の指摘が最も厳しく、GhostScriptエンジンの全削除を余儀なくされました。 ※以下のメール文面は、個人情報(メールアドレス等)を伏せたうえで、要点のみ抜粋しています。メールのやり取り(時系列の要点)
- 自動プレレビュー(AI):名前/スラッグが一般的すぎる、所有権(メールドメイン)確認、プレフィックス、ディレクトリアセット同梱、Deprecated 指摘などが提示される
- 私の対応:プラグイン名を Rapls PDF Image Creator に変更し、スラッグも
rapls-pdf-image-creatorへ。所有権についても「raplsworks は自分のサイト」である旨を明確化 - 人のレビュー:Ghostscript エンジンで
exec()を使っている点が危険として指摘される(ホスト側でブロックされる/悪用され得る) - 再提出(v1.0.5):
exec()/shell_exec()を完全削除し、Ghostscript エンジンを廃止。Imagick のみに一本化 - 再提出(v1.0.6):Plugin Check フィードバックを踏まえ、翻訳ロード等を整理。加えて「外部通信なし・追跡なし・nonce/権限チェック・適切なサニタイズ」など、審査チームが確認しやすい形で補足
メールで実際に来た主な指摘(要点)
- 名前/スラッグが一般的すぎる:既存プラグインと混同されやすいので、ブランド/識別子を含めて区別する
- 所有権の確認:プラグインの関連URLと、提出アカウントのメールドメインが一致しない場合は説明が必要(第三者なりすまし防止のため)
exec()/shell_exec()は危険:リモートコード実行につながり得る/多くのホストで無効化されているため、削除が求められる- プレフィックス不足:関数・グローバル・保存データ(option/meta/transient など)に衝突回避のプレフィックス(4文字以上)が必要
- ディレクトリ用アセットは ZIP に入れない:バナー/アイコン/スクショ等は承認後に SVN の assets へアップロード(ZIP同梱は避ける)
- Deprecated 関数:将来的に壊れる可能性があるため、代替手段へ置き換える
私が再提出コメントで書いたこと(コピペできる”短い型”)
返信は長文にせず、レビュアーが「確認すべき点」をすぐ拾えるように、次の順で短くまとめました。|
1 2 3 4 5 6 7 8 9 |
Thank you for reviewing my submission. - Removed all exec()/shell_exec() usage and the Ghostscript-based engine. - The plugin now uses Imagick only (no OS command execution). - No external API calls / tracking / remote assets (no CDN). - Admin/AJAX actions include capability checks and nonce verification. - Output is sanitized appropriately; logging runs only when WP_DEBUG is enabled. Thank you again for your guidance. |
Your plugin has been reviewed and we have concerns…却下。 ここから、審査との長い戦いが始まりました。
指摘1: プラグイン名が一般的すぎる
最初は「PDF Image Creator」という名前でした。プラグイン名にユニークなプレフィックスを付けてください。「Rapls PDF Image Creator」に変更。Raplsは私のブランド名です。
指摘2: exec()は使用禁止
これが一番痛かった。 GhostScriptエンジンはexec()関数でコマンドを実行していました。
exec()、shell_exec()、system()などのシェル実行関数は、セキュリティ上の理由から使用できません。せっかく実装したGhostScript対応を、泣く泣く削除。ImageMagick一本に絞りました。
指摘3: プレフィックスの統一
関数名、定数、オプション名…すべてに一貫したプレフィックスが必要でした。|
1 2 3 4 5 6 7 |
// 修正前 function get_pdf_thumbnail() { ... } define('PLUGIN_VERSION', '1.0.0'); // 修正後 function rapls_pic_get_thumbnail() { ... } define('RAPLS_PIC_VERSION', '1.0.0'); |
指摘4: flush_rewrite_rules()の削除
有効化・無効化時にflush_rewrite_rules()を呼んでいましたが、これも指摘を受けました。
このプラグインはカスタム投稿タイプやリライトルールを登録していません。不要な処理は削除してください。確かに、意味もなく入れていました。反省。
指摘5: error_log()のファイルパス露出
デバッグ用に入れていたerror_log()が問題に。
|
1 2 3 4 5 6 7 |
// 修正前(危険) error_log('Error in ' . __FILE__ . ': ' . $e->getMessage()); // 修正後(安全) if (defined('WP_DEBUG') && WP_DEBUG) { error_log('Rapls PDF Image Creator: ' . $e->getMessage()); } |
指摘6: load_plugin_textdomain()は不要
翻訳の読み込み処理を入れていましたが…WordPress 4.6以降、WordPress.orgでホストされるプラグインは翻訳が自動的に読み込まれます。load_plugin_textdomain()は非推奨です。知らなかった…。削除しました。
指摘7: 日本語翻訳のスタイル
日本語翻訳ファイルも審査対象でした。 WordPress日本語チームの翻訳スタイルガイドに従う必要があります。|
1 2 3 4 5 |
// 修正前 "PDFファイルからサムネイルを生成" // 修正後(半角英字の前後にスペース) "PDF ファイルからサムネイルを生成" |
最終的なコード品質
審査を通過するために、入力のサニタイズ・出力のエスケープ・nonce検証を全箇所に徹底し、WordPress Coding Standardsに準拠したコードに仕上げました。 審査を通過するために、以下の点を徹底しました:セキュリティ
- すべての入力値をサニタイズ(
sanitize_text_field()、absint()など) - すべての出力をエスケープ(
esc_html()、esc_attr()、esc_url()) - AJAX処理には必ずnonce検証と権限チェック
- カスタムHTMLには
wp_kses_post()でサニタイズ
コーディング規約
- WordPress Coding Standardsに準拠
- PHPDoc形式のコメント
- 適切な名前空間とクラス設計
互換性
- PHP 7.4以上で動作(
readonlyプロパティやmatch式は使わない) - WordPress 5.0以上で動作
ついに承認!
何度もの修正を経て、ついに承認メールが届きました。exec削除、プレフィックス統一、セキュリティ対策の徹底が承認の決め手でした。
Congratulations, your plugin hosting request for Rapls PDF Image Creator has been approved.正直、泣きそうになりました。
公開後の反省と学び
審査を通じてセキュリティ意識とコーディング規約の理解が大幅に向上しました。一方で、最初からガイドラインを読んでおけばGhostScript対応の無駄な工数を避けられたのが最大の反省点です。良かったこと
- 審査プロセスを通じて、セキュリティ意識が大幅に向上した
- WordPressのコーディング規約を深く理解できた
- 「動けばいい」から「正しく動く」へ意識が変わった
反省点
- 最初からWordPress.orgのガイドラインを読んでおくべきだった
- GhostScript対応に時間をかけすぎた(結局削除)
- 翻訳スタイルガイドの存在を知らなかった
これからプラグインを開発する人へ
- まずガイドラインを読む – Plugin Guidelinesは必読
- Plugin Check プラグインを使う – 提出前に自動チェックできる
- exec()系は諦める – 外部コマンド実行は審査に通らない
- プレフィックスは最初から統一 – 後から変更するのは大変
- セキュリティは妥協しない – サニタイズ・エスケープ・nonce検証
プラグインのダウンロード
Rapls PDF Image Creatorは、WordPress公式ディレクトリから無料でダウンロードできます。
開発者向け:テンプレート関数
テーマ開発者向けにテンプレート関数とショートコード4種類を用意しています。get_post_thumbnail_id()でPDFのサムネイルを取得できます。 テーマ開発者の方向けに、テンプレート関数も用意しています:|
1 2 3 4 5 6 7 |
<?php // サムネイルがあるか確認 if (rapls_pic_has_thumbnail($pdf_id)) { // サムネイル画像を表示 echo rapls_pic_get_thumbnail_image($pdf_id, 'medium'); } ?> |







コメント