从零开始搭插件骨架:我把目录结构、入口文件和本地环境揉进了一份「最小可运行模板」
刚开始写插件那会儿,我的目录是乱的——index.php、main.php、plugin.php 全塞根目录,WordPress 后台能认出来,但自动加载一塌糊涂,调试时找文件像翻垃圾堆。后来逼自己定了一套规矩,现在新项目都是复制粘贴改个名字就能跑。
我的目录长这样,不多不少:
my-plugin/ ├── my-plugin.php ← 唯一入口,只做一件事:启动 ├── composer.json ← 自动加载 + 依赖管理 ├── src/ │ ├── Plugin.php ← 核心类,单例,管生命周期 │ ├── Admin/ ← 后台相关:菜单、设置页、AJAX │ ├── Frontend/ ← 前台:短代码、脚本注入、模板覆盖 │ ├── Core/ ← 跨层工具:数据库抽象、通用钩子、常量 │ └── Integrations/ ← 第三方对接:WooCommerce、REST、块编辑器 ├── assets/ │ ├── css/ │ ├── js/ │ └── images/ ├── languages/ ├── tests/ ← PHPUnit + Brain\Monkey └── .vscode/ ← launch.json 预配置,新人clone即用
关键认知转变:入口文件不是"主程序",是"施工指示牌"。以前我把业务逻辑全堆 my-plugin.php,现在它只干四件事——
<?php
/**
* Plugin Name: My Plugin
* ...
*/
// 1. 防直接访问
if (!defined('ABSPATH')) exit;
// 2. 定义常量(路径、版本,不用硬编码)
define('MY_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('MY_PLUGIN_URL', plugin_dir_url(__FILE__));
define('MY_PLUGIN_VERSION', '1.0.0');
// 3. Composer 自动加载(没有 Composer 就用 spl_autoload_register 手写)
require_once MY_PLUGIN_DIR . 'vendor/autoload.php';
// 4. 启动,且只启动一次
add_action('plugins_loaded', function () {
\MyPlugin\Plugin::instance()->boot();
});
Plugin::boot() 里面才做具体的事:注册服务容器、加载文本域、判断前台/后台分别初始化哪个模块。这样 plugins_loaded 之前,我的代码完全"静音",不会跟别的插件抢钩子。
本地环境我用的是"轻量三件套",不是全套 Docker:
1. wp-env(@wordpress/env)—— 项目里丢个 .wp-env.json:
{
"core": "WordPress/WordPress#6.5",
"plugins": [ "." ],
"port": 2000,
"testsPort": 2001,
"config": {
"WP_DEBUG": true,
"WP_DEBUG_LOG": true,
"SCRIPT_DEBUG": true
}
}
跑 npx wp-env start,当前插件自动挂载进容器,改代码宿主机实时同步。调试日志直接落 wp-content/debug.log,tail -f 盯着看。
2. Xdebug 走 VS Code 的 launch.json,但端口改成 9003,pathMappings 配到容器里的 /var/www/html/wp-content/plugins/my-plugin。断点打在 src/Admin/Settings.php 里,保存设置页表单时直接停住,比 var_dump 后刷新页面快十倍。
3. 一个偷懒的调试入口:我在 tests/ 旁边放了 _playground.php,不在版本控制里,纯本地用——
<?php // 直接 php _playground.php 跑,绕过 WordPress 全局依赖 require __DIR__ . '/../vendor/autoload.php'; $service = new \MyPlugin\Core\SomeService(); $result = $service->calculate(['foo' => 'bar']); var_dump($result);
想快速验证一个类的逻辑,不用开浏览器、不用等 WordPress 加载,CLI 里秒出结果。写顺手了,有些单元测试都是先在 _playground.php 里跑通再搬过去。
踩过的一个坑: 用 wp-env 时,插件在宿主机改完代码,容器里偶尔不生效。不是缓存,是文件挂载的 inode 问题。解决方式是改完 touch 一下入口文件,或者给 wp-env start 加 --update 强制刷新挂载点。这毛病耗了我半小时,后来写进项目 README 的"本地开发注意事项"里。
你们本地调插件是全套 Docker 还是也走这种轻量路线?有没有更顺手的"单文件快速验证"技巧?

