数据库表"幽灵残留":卸载时 `DROP TABLE IF EXISTS` 删不干净,我查了三天才发现是前缀常量里的下划线在搞鬼
上周帮一个客户清理旧插件数据,明明卸载函数里写了 DROP TABLE IF EXISTS,后台反复提示"卸载成功",数据库里那几张表却像钉子户一样纹丝不动。更诡异的是,我手动进 phpMyAdmin 执行同样的 SQL,啪一下就删掉了。
问题出在表名的拼接方式上。很多教程让你这么写:
global $wpdb;
$table_name = $wpdb->prefix . 'my_plugin_data';
$wpdb->query( "DROP TABLE IF EXISTS {$table_name}" );
看着没毛病对吧?但 $wpdb->prefix 这个值,在 WordPress 多站点和普通站点表现不一样。多站点下它可能是 wp_2_ 这种带数字前缀的,而我在建表的时候,手滑用了硬编码的 'wp_' 当默认值——当时为了本地快速测试,后来改配置时漏了这处。
真正的坑是:建表和删表用了两套生成逻辑。建表时我搞了个"兼容层":
// 建表时"自作聪明"的兼容
$prefix = defined( 'CUSTOM_TABLE_PREFIX' ) ? CUSTOM_TABLE_PREFIX : 'wp_';
$table = $prefix . 'my_plugin_data';
卸载时却走了标准 $wpdb->prefix。同一个插件,建出来是 wp_my_plugin_data,卸载时去删 wp_2_my_plugin_data,IF EXISTS 不报错,结果就是"静默失败"。
现在我统一成了单点入口,所有表名必须经过同一个 resolver:
class Table_Registry {
private static $cache = [];
public static function get( $suffix ) {
if ( ! isset( self::$cache[ $suffix ] ) ) {
// 强制以 $wpdb->prefix 为唯一可信源
global $wpdb;
self::$cache[ $suffix ] = $wpdb->prefix . 'myplugin_' . $suffix;
}
return self::$cache[ $suffix ];
}
}
另外补了个"孤儿表扫描"用于升级时自检——有些用户中间改过表前缀,旧表就永远晾在那儿了:
public static function find_orphans() {
global $wpdb;
$pattern = $wpdb->base_prefix . '%'; // 注意 base_prefix vs prefix
// ... 匹配所有带插件特征后缀但前缀不符的表
}
还有个冷知识:$wpdb->prefix 和 $wpdb->base_prefix 在多站点主站是同一个值,子站就分叉了。如果你的插件需要在网络层面共享表,建表和删表都得想清楚用哪个。
你们卸载逻辑是现场拼表名,还是也有个集中管理的地方?遇到过这种"删了个寂寞"的情况吗?