从"Hello World"到能下断点:我搭第一个插件目录时走过的结构弯路
【总结】本文作者分享了从初学插件开发时将所有代码塞进单个文件导致调试困难,到逐步建立规范目录结构的经验总结。关键实践包括:坚持入口文件(my-plugin.php)只做声明不写业务逻辑,
刚开始写插件那会儿,我把所有东西塞进一个文件,my-plugin.php 里既有头部声明又有业务逻辑还有样式输出,本地调试全靠 var_dump 刷页面。后来项目稍微大一点,找一段代码要翻几百行,断点根本没法打——因为执行流在 HTML 输出和 PHP 逻辑之间横跳,Xdebug 停下来的地方永远不是我想看的那一行。
这篇记录我后来固定下来的目录骨架,以及怎么让本地环境真正"可调试"。
一、目录结构:从"一坨"到"有层有级"
我现在新开插件必定先建这几个目录,哪怕第一个版本只有两百行代码:
my-plugin/
├── my-plugin.php # 唯一入口,只做"挂号"和"分流"
├── bootstrap.php # 容器、常量、自动加载注册
├── src/
│ ├── Admin/ # 后台相关:页面、Ajax、菜单注册
│ ├── Public/ # 前台相关:短码、小工具、脚本
│ ├── Core/ # 跨层通用:数据库、钩子调度器、工具函数
│ └── Contracts/ # 接口定义,解耦用
├── assets/
│ ├── css/
│ ├── js/
│ └── images/
├── languages/ # 从第一天就留好位置
├── tests/ # 单元测试,后面会说怎么接进本地
└── vendor/ # composer 依赖(如果有)
关键约定:入口文件里不写任何业务逻辑。我的 my-plugin.php 通常长这样——
bootstrap.php 负责把自动加载挂好、把主类实例化,然后让主类自己去决定什么时候注册钩子。这样入口文件永远干净,调试时我一眼就知道"程序从哪进"。
二、自动加载:别用 require 堆路径
早期我手写一堆 require_once,改个目录结构就报错。现在直接用 composer 的 PSR-4,composer.json 里加一段:
{
"autoload": {
"psr-4": {
"MyPlugin\\": "src/"
}
}
}
本地开发时 composer dump-autoload 随时刷新映射。生产打包再考虑优化成 classmap,开发阶段 PSR-4 最省事。
三、本地调试环境:不是"能跑 WordPress"就行
我试过几种方案,最后固定用 Docker + Xdebug + VS Code,因为能精确控制 PHP 版本和扩展,且断点能穿透到插件代码里。
我的 docker-compose.yml 核心片段(PHP 服务部分):
services:
wordpress:
image: wordpress:6.4-php8.2-apache
volumes:
- ./wordpress:/var/www/html
- ./my-plugin:/var/www/html/wp-content/plugins/my-plugin # 插件挂载为卷
- ./php.ini:/usr/local/etc/php/conf.d/xdebug.ini # 覆盖配置
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: root
WORDPRESS_DB_PASSWORD: secret
ports:
- "8080:80"
xdebug.ini 内容(Xdebug 3 写法):
zend_extension=xdebug.so
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=host.docker.internal
xdebug.client_port=9003
xdebug.log=/tmp/xdebug.log
VS Code 的 .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9003,
"pathMappings": {
"/var/www/html/wp-content/plugins/my-plugin": "${workspaceFolder}"
}
}
]
}
这里有个坑:pathMappings 必须精确映射到插件目录,不能只映射到 /var/www/html,否则断点会飘到 WordPress 核心文件里,或者干脆不生效。我为此浪费过两小时。
四、让错误"炸"在屏幕上:开发期的错误级别
插件入口或者 bootstrap.php 里,开发环境我会强制打开错误显示:
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
error_reporting( E_ALL );
ini_set( 'display_errors', '1' );
}
配合 wp-config.php 里的 define( 'WP_DEBUG', true ); 和 define( 'WP_DEBUG_LOG', true );,错误同时进文件。但注意:生产环境绝对不要 display_errors,这个开关我后面会包进环境判断里。
五、一个最小可运行的"骨架插件"
把上面串起来,一个能下断点、有目录层级、能自动加载的最小插件长这样。核心类 src/Core/Plugin.php:
My Plugin %s


微信客服
QQ官方交流群