本地调试环境"最后一公里":我用 Valet + Xdebug + 单文件入口搭了一套"即插即跑"的插件原型工作台

小助手
小助手 版主圣羽星庭 勋望元宿志愿先锋
社区管理
插件开发 143 浏览 0 回复

写插件最怕的不是逻辑复杂,是还没写到逻辑就被环境劝退。我见过太多新手在"目录该放哪""怎么让改动实时生效""断点为什么不触发"这三件事上卡一周。这篇不聊架构,只聊让第一个 `var_dump` 顺利弹出来的最小环境。

一、目录结构:拒绝"一步到位",先要能跑

很多教程一上来就教你按 MVC 拆目录,结果 `autoload` 配不对,类找不到,调试还没开始先调目录。我的做法是先扁平,后分层

my-plugin/
├── my-plugin.php          ← 唯一入口,WordPress 只认这个
├── uninstall.php          ← 卸载钩子,可选但建议有
├── assets/
│   ├── css/
│   └── js/
├── includes/              ← 刚开始可以只有这一个目录
│   ├── class-activator.php
│   └── class-admin.php
└── .vscode/               ← 调试配置丢这里,团队共享
    └── launch.json

关键认知:my-plugin.php 的文件名必须和目录名一致,否则激活时 WordPress 会报"插件文件不存在"。这个坑我踩过两次,第二次才发现是手滑多了个连字符。

二、入口文件:只做三件事

入口文件越薄越好,我的模板固定三段:

<?php
/**
 * Plugin Name: My Plugin
 * Version:     0.1.0
 * Author:      You
 */

// 1. 防直接访问
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

// 2. 定义常量(路径、URL、版本)
define( 'MY_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
define( 'MY_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
define( 'MY_PLUGIN_VERSION', '0.1.0' );

// 3. 懒加载启动(不在全局 new,等钩子触发)
add_action( 'plugins_loaded', function() {
    require_once MY_PLUGIN_DIR . 'includes/class-admin.php';
    new My_Plugin_Admin();
} );

注意第 3 点:很多教程让你直接在文件末尾 new My_Plugin(),这在未激活状态下也会执行,可能触发 fatal error。挂到 plugins_loaded 是更安全的习惯。

三、Valet 本地环境:比 Docker 轻,比 MAMP 快

我主力用 Laravel Valet,不是因为它多先进,是零配置 SSL + 自动 .test 域名对调试太友好:

# 一次性安装
valet install

# 把插件丢进 WordPress 的 wp-content/plugins
# 然后在 WordPress 根目录执行
valet link myplugin-dev
valet secure myplugin-dev

# 得到 https://myplugin-dev.test
# 自带 HTTPS,Xdebug 配一次到处用

比 Docker 省内存,比本地 Apache 省配置时间。唯一要注意的是 Valet 用 php-fpm,改 php.ini 后记得 valet restart 而不是只重启 Nginx。

四、Xdebug 3:三行配置搞定断点

Xdebug 3 的配置比 2.x 简洁太多,我的 /usr/local/etc/php/8.2/conf.d/ext-xdebug.ini 只保留这些:

[xdebug]
zend_extension="xdebug.so"
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_port=9003
xdebug.log_level=0

VS Code 的 .vscode/launch.json 对应配法:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for Xdebug",
            "type": "php",
            "request": "launch",
            "port": 9003,
            "pathMappings": {
                "/Users/你的名字/.config/valet/Nginx/...": "${workspaceFolder}"
            }
        }
    ]
}

路径映射是隐形杀手。Valet 的站点实际通过 Nginx 指向你的项目目录,如果 pathMappings 左边填错,断点永远红圈变灰。我的 trick 是在入口文件顶部加 error_log( __FILE__ ),看日志里的绝对路径,原样复制过去。

五、热调试技巧:不用每次改代码都刷新

三个让我效率翻倍的习惯:

1. 用 wp_die() 做"穷人的断点"

// 在任意位置插,比 echo 干净,不会破坏 JSON 响应
wp_die( '<pre>' . print_r( $some_var, true ) . '</pre>' );

2. 日志调试法(生产环境也适用)

// 自定义一个不会刷屏的日志函数
function my_debug_log( $data ) {
    if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
        error_log( '[MY_PLUGIN] ' . print_r( $data, true ) );
    }
}
// 配合 tail -f /Users/.../php_error.log 实时看

3. 条件断点过滤垃圾请求

在 VS Code 断点上右键"Edit Breakpoint",输入 $_GET['page'] === 'my-plugin',这样后台其他页面的请求不会触发,只停在你的插件页面。

六、一个能直接复制走的"最小可运行"骨架

我把上面这些打包成了一个 GitHub 模板(此处无外链,自行搜索),核心就三个文件:

  • my-plugin.php:入口,含常量定义 + 懒加载钩子
  • includes/class-admin.php:示例菜单页,带一个故意留的断点位置
  • .vscode/launch.json:预设好 Valet 路径映射模板

clone 下来改个前缀就能开始写业务,不用先配两小时环境。

最后

插件开发的"入门门槛"其实不在 PHP 语法,在工具链的默契度。目录结构决定你能维护多久,调试环境决定你能查多深的 bug。先把这一公里跑顺,后面写 hook、调 API 都是水到渠成。

你们本地用什么环境?Valet、Local WP、Docker 还是直接宝塔?好奇不同环境下调试配置的坑点是不是一样的。

评论0
回复 · 0
还没有回复
微信客服 微信客服