WordPressプラグインの開発は、functions.phpにコードを追加する延長線上にありますが、本格的なプラグインはOOP(オブジェクト指向)で設計すべきです。この記事では、保守性が高く拡張しやすいプラグインの作り方を解説します。
プラグインのディレクトリ構成
my-awesome-plugin/
├── my-awesome-plugin.php # メインファイル(エントリポイント)
├── includes/
│ ├── class-plugin.php # プラグインのメインクラス
│ ├── class-admin.php # 管理画面の処理
│ ├── class-api.php # REST APIエンドポイント
│ └── class-db.php # データベース操作
├── admin/
│ ├── views/ # 管理画面テンプレート
│ ├── css/
│ └── js/
├── public/
│ ├── css/
│ └── js/
├── languages/ # 翻訳ファイル
└── readme.txt # WordPress.org用
メインファイル
<?php
/**
* Plugin Name: My Awesome Plugin
* Description: プラグインの説明
* Version: 1.0.0
* Author: Your Name
* Text Domain: my-awesome-plugin
*/
if (!defined("ABSPATH")) exit;
define("MAP_VERSION", "1.0.0");
define("MAP_PLUGIN_DIR", plugin_dir_path(__FILE__));
define("MAP_PLUGIN_URL", plugin_dir_url(__FILE__));
require_once MAP_PLUGIN_DIR . "includes/class-plugin.php";
// プラグイン初期化
function map_init() {
return MyAwesomePluginPlugin::get_instance();
}
add_action("plugins_loaded", "map_init");
// アクティベーション・ディアクティベーション
register_activation_hook(__FILE__, ["MyAwesomePluginPlugin", "activate"]);
register_deactivation_hook(__FILE__, ["MyAwesomePluginPlugin", "deactivate"]);
プラグインのメインクラス(シングルトン)
<?php
namespace MyAwesomePlugin;
class Plugin {
private static $instance = null;
public static function get_instance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
$this->load_dependencies();
$this->init_hooks();
}
private function load_dependencies() {
require_once MAP_PLUGIN_DIR . "includes/class-admin.php";
require_once MAP_PLUGIN_DIR . "includes/class-api.php";
require_once MAP_PLUGIN_DIR . "includes/class-db.php";
}
private function init_hooks() {
// 管理画面
if (is_admin()) {
new Admin();
}
// REST API
add_action("rest_api_init", [new Api(), "register_routes"]);
// フロントエンド
add_action("wp_enqueue_scripts", [$this, "enqueue_public_assets"]);
}
public function enqueue_public_assets() {
wp_enqueue_style(
"map-public",
MAP_PLUGIN_URL . "public/css/style.css",
[],
MAP_VERSION
);
wp_enqueue_script(
"map-public",
MAP_PLUGIN_URL . "public/js/main.js",
[],
MAP_VERSION,
true
);
// JSにデータを渡す
wp_localize_script("map-public", "mapConfig", [
"ajaxUrl" => admin_url("admin-ajax.php"),
"apiUrl" => rest_url("my-plugin/v1/"),
"nonce" => wp_create_nonce("wp_rest"),
]);
}
public static function activate() {
Db::create_tables();
flush_rewrite_rules();
}
public static function deactivate() {
flush_rewrite_rules();
}
}
管理画面クラス
<?php
namespace MyAwesomePlugin;
class Admin {
public function __construct() {
add_action("admin_menu", [$this, "add_menu"]);
add_action("admin_init", [$this, "register_settings"]);
}
public function add_menu() {
add_options_page(
"My Plugin設定",
"My Plugin",
"manage_options",
"my-awesome-plugin",
[$this, "render_settings_page"]
);
}
public function register_settings() {
register_setting("map_settings", "map_api_key");
register_setting("map_settings", "map_enabled");
}
public function render_settings_page() {
include MAP_PLUGIN_DIR . "admin/views/settings.php";
}
}
データベースクラス
<?php
namespace MyAwesomePlugin;
class Db {
public static function create_tables() {
global $wpdb;
$table = $wpdb->prefix . "map_logs";
$charset = $wpdb->get_charset_collate();
$sql = "CREATE TABLE IF NOT EXISTS $table (
id bigint(20) NOT NULL AUTO_INCREMENT,
user_id bigint(20) DEFAULT NULL,
action varchar(50) NOT NULL,
data longtext,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY user_id (user_id),
KEY created_at (created_at)
) $charset;";
require_once ABSPATH . "wp-admin/includes/upgrade.php";
dbDelta($sql);
}
public static function insert_log($action, $data = null) {
global $wpdb;
return $wpdb->insert(
$wpdb->prefix . "map_logs",
[
"user_id" => get_current_user_id(),
"action" => $action,
"data" => is_array($data) ? json_encode($data) : $data,
],
["%d", "%s", "%s"]
);
}
}
セキュリティのベストプラクティス
- ABSPATH チェック: 全PHPファイルの先頭で
if (!defined("ABSPATH")) exit;を記述 - Nonceの検証: フォーム送信やAjaxリクエストでは必ずnonce検証を行う
- 権限チェック:
current_user_can()で適切な権限を確認 - サニタイズ: 入力値は
sanitize_text_field()、出力はesc_html()で処理 - SQLインジェクション対策:
$wpdb->prepare()を使用
まとめ
OOP設計でプラグインを作ることで、コードの見通しが良くなり、チーム開発やメンテナンスが格段に楽になります。最初は少し面倒に感じるかもしれませんが、一度フレームワークを作ってしまえば、新しいプラグインでも使い回せます。WordPress公式ディレクトリへの公開を目指す場合も、この設計であればレビューを通過しやすいでしょう。
コメントを残す