WP-Sweep

描述

WP-Sweep 会找出 WordPress 留下的数据行并将其移除。多年前你编辑过的文章的修订版本、属于已删除文章的元数据、未挂载到任何内容的术语、指向已不存在文章的术语关系、已过期的临时数据(transient):这些在 wp-admin 的任何地方都不可见,但它们全都存在于你的数据库中。

凡是可以触达数据行的,它都使用恰当的 WordPress 删除函数,而不是直接运行原始删除查询,因此其它插件依赖的钩子仍会被触发。只有 API 拒绝处理的行——对象 ID 为 0 的孤立元数据——才会被直接删除。

功能

  • 修订版本
  • 自动草稿
  • 已删除的文章
  • 未批准的评论
  • 垃圾评论
  • 删除的评论
  • 孤立的文章元数据
  • 孤立的评论元数据
  • 孤立的用户元数据
  • 孤立的分类项元数据
  • 孤立的分类项关系
  • 未使用的分类项
  • 重复的文章元数据
  • 重复的评论元数据
  • 重复的用户元数据
  • 重复的分类项元数据
  • 瞬时选项
  • 在文章元数据中嵌入缓存
  • 优化数据库表

使用的删除函数

  • wp_delete_post_revision()
  • wp_delete_post()
  • wp_delete_comment()
  • delete_post_meta()
  • delete_comment_meta()
  • delete_user_meta()
  • delete_term_meta()
  • wp_remove_object_terms()
  • wp_delete_term()
  • delete_transient()
  • delete_site_transient()

已知的兼容性问题

这些插件会把数据存放在 WP-Sweep 视为「孤立」的位置。在清理前,请用「用法(Usage)」下的过滤器保护它们的元数据键。

捐赠

我大部分的空闲时间都花在了创建,更新,维护和支持这些插件上,如果您真的喜欢我的插件并且可以为我留下买一杯咖啡的钱,我将不胜感激。 如果没有,仍可随意使用它,没有任何义务。

用法

每次清理都不可撤销,因此请先备份你的数据库。

界面列出每一次清理,并简要说明它移除什么、有多少项、占表格的什么比例。勾选你想要的清理项并使用清理(Sweep)批量操作,或者逐行使用每行的清理(Sweep)详情(Details)按钮。界面上没有任何东西需要 JavaScript:按钮都是普通链接,批量操作是一个普通表单。

没有设置界面。唯一值得调整的一项是详情(Details)列表显示多少项(默认 500),这由 wp_sweep_limit_details 过滤器控制。之所以有这个上限,是因为整个样本都会保存在内存中并写入页面。

WP-CLI

wp sweep --all
wp sweep revisions
wp sweep revisions auto_drafts deleted_posts

REST API

三条路由都需要 activate_plugins 权限。

GET    /wp-json/sweep/v1/count/<name>
GET    /wp-json/sweep/v1/details/<name>
DELETE /wp-json/sweep/v1/sweep/<name>

项目名

revisions, `auto_drafts`, `deleted_posts`, `unapproved_comments`, `spam_comments`, `deleted_comments`, `transient_options`, `orphan_postmeta`, `orphan_commentmeta`, `orphan_usermeta`, `orphan_termmeta`, `orphan_term_relationships`, `unused_terms`, `duplicated_postmeta`, `duplicated_commentmeta`, `duplicated_usermeta`, `duplicated_termmeta`, `optimize_database`, `oembed_postmeta`.

过滤器

  • wp_sweep_postmeta_whitelist(数组)——绝不可被删除的文章元数据键,作用于「孤立」或「重复」文章元数据清理项。* 匹配任意连续字符。默认值:空。
  • wp_sweep_commentmeta_whitelist(数组)——同上,针对评论元数据(comment meta)。
  • wp_sweep_usermeta_whitelist(数组)——同上,针对用户元数据(user meta)。
  • wp_sweep_termmeta_whitelist(数组)——同上,针对术语元数据(term meta)。
  • wp_sweep_excluded_taxonomies(array)— 在清理孤立术语关系时排除的分类法。默认值:array( 'link_category' )
  • wp_sweep_excluded_termids(数组)——从「未使用术语」清理中排除的术语 ID。默认值:每个分类法的默认术语,以及任何作为其它术语上级的术语。
  • wp_sweep_limit_details(整数)——「详情(Details)」列表显示多少项。默认值:500。
  • wp_sweep_defer_counts (bool) — whether the Sweep screen renders first and fetches its counts afterwards, one at a time. Return false to compute every count with the page, as versions before 2.0.1 always did. Default: true.
  • wp_sweep_capability(字符串 $capability、字符串 $context)——所需的权限。$contextsweepajaxrest 之一。
  • wp_sweep_total_count(整数 $count、字符串 $name)——某次清理百分比所依据的数据行总数。
  • wp_sweep_count(整数 $count、字符串 $name)——某次清理会移除多少项。
  • wp_sweep_details(数组 $details、字符串 $name)——「详情(Details)」显示的样例列表。
  • wp_sweep_sweep(string $message,string $name)— 过滤清理运行后返回的结果消息。

动作

wp_sweep_admin_post_sweep, `wp_sweep_admin_comment_sweep`, `wp_sweep_admin_user_sweep`, `wp_sweep_admin_term_sweep`, `wp_sweep_admin_option_sweep` and `wp_sweep_admin_database_sweep` all fire below the sweep table, in that order.

保护特定的文章元数据(post meta)键不被清理:

add_filter( 'wp_sweep_postmeta_whitelist', function ( $meta_keys ) {
    $meta_keys[] = '_my_plugin_setting';
    $meta_keys[] = '_acme_*';
    return $meta_keys;
} );

示例 — 将额外分类法排除在孤立术语关系清理之外:

add_filter( 'wp_sweep_excluded_taxonomies', function ( $taxonomies ) {
    $taxonomies[] = 'product_type';
    return $taxonomies;
} );

屏幕截图

安装

  1. 安装并启用插件。界面位于 WP-Admin -> 工具(Tools)-> WP-Sweep
  2. 在清理任何内容之前,请先备份你的数据库。每次清理都不可撤销,且没有撤消功能。

常见问题

工具 -> 清理(Sweep)界面有了新的地址

它仍在工具(Tools)下,但地址从 tools.php?page=wp-sweep/admin.php 改为 tools.php?page=wp-sweep。请更新你的书签。

旧地址是传统的「以插件文件作为菜单别名(slug)」形式,会把插件的安装目录名写进页面 URL。这也正是为什么当有人把 WP-Sweep 安装到不同目录名(手动改名,或解压为 wp-sweep-2.0.0)时,该界面会出问题。现在这两种情况都不会发生了。

「全部清理(Sweep All)」去哪了?

每一行都有一个复选框,表格还有一个清理(Sweep)批量操作。勾选表头复选框并应用它,效果等同于以前的「全部清理(Sweep All)」,而且还能让你排除那些不想清理的项。

我调用 WPSweep::get_instance() 的代码片段失效了

类名在 2.0.0 中已重命名:

  • WPSweep 现为 WP_Sweep
  • WPSweep_Api 现为 WP_Sweep_API
  • WPSweep_Command 现为 WP_Sweep_Command

因此 WPSweep::get_instance()->sweep( 'revisions' ) 变为 WP_Sweep::get_instance()->sweep( 'revisions' )。没有兼容别名,所以旧名称会触发致命错误,而非无声失败。

每个过滤器和每个 wp_sweep_admin_*_sweep 动作的命名都保持不变。

我可以使用哪些过滤器来防止数据被清理?

四个按类型的过滤器接收一份「绝不可删除」的元数据键列表,每个过滤器都支持用 * 作为通配符:

add_filter( 'wp_sweep_postmeta_whitelist', function ( $keys ) {
    $keys[] = '_my_plugin_setting';
    $keys[] = '_acme_*';
    return $keys;
} );

同样适用于 wp_sweep_commentmeta_whitelistwp_sweep_usermeta_whitelistwp_sweep_termmeta_whitelist。术语(term)由 wp_sweep_excluded_termids 保护,分类法(taxonomy)通过 wp_sweep_excluded_taxonomies 排除在「孤立术语关系」清理之外。

在 2.0.0 中,这些列表同样保护重复元数据清理项——文档一直说它们会保护,但之前的代码从未做到。

我保护的某个键还是被清理了,或者保留了过多内容

在 2.0.0 之前,排除规则是一个 SQL LIKE 子句,而 LIKE 把下划线视为单字符通配符。因此模式 _my_key 也会匹配 Xmy!key 以及许多其它内容。现在匹配在 PHP 中进行,所以下划线就是下划线,只有 * 才是通配符。如果你依赖旧的行为,请检查你的列表。

删除 WP-Sweep 时,它会留下任何东西吗?

什么都不会留下。WP-Sweep 不存储任何选项行、不创建任何数据库表、不注册任何权限、也不安排任何定时事件。uninstall.php 仍会运行,并在整个网络(而非前一百个站点)范围内自行清理。

评价

2025 年 3 月 26 日
WP-Sweep is a simple yet powerful plugin that helps keep your WordPress site running smoothly. It cleans up unnecessary data like post revisions, trashed posts, spam comments, unused terms, and more — all using native WordPress functions, which makes it safer than many alternatives. We’ve used WP-Sweep on multiple websites and it’s never caused any issues. It’s lightweight, user-friendly, and does exactly what it promises. The interface is clear and makes it easy to choose what to clean. Highly recommended for anyone looking to optimize their WordPress database without the risk of breaking anything.
2025 年 1 月 22 日
great and simple plugin, no licence shit – it just works, thanks!
阅读所有139条评价

贡献者及开发者

「WP-Sweep」是开源软件。 以下人员对此插件做出了贡献。

贡献者

「WP-Sweep」插件已被翻译至 23 种本地化语言。 感谢所有译者为本插件所做的贡献。

帮助将「WP-Sweep」翻译成简体中文。

对开发感兴趣吗?

您可以浏览代码,查看SVN仓库,或通过RSS订阅开发日志

更新日志

2.0.1

  • NEW: A Sweep link on the plugin’s row of the Plugins screen, opening Tools -> Sweep.
  • FIXED: The Sweep screen timed out on large databases. 2.0.0 computed every count before printing a byte — and asked for each table’s total row count once per row instead of once, so SELECT COUNT(*) ran against the postmeta table four times per load. The screen now renders at once and fetches the counts afterwards, one request at a time, so no single request outlives PHP’s time limit. Without JavaScript, a link computes them with the page the way 2.0.0 always did, and the new wp_sweep_defer_counts filter restores that behaviour outright.
  • FIXED: Counting the duplicated meta sweeps fetched every duplicate row’s ids into PHP through GROUP_CONCAT, just to add them up. On a postmeta table with millions of duplicates that alone could exhaust the request. The count now reads per-key totals only; the ids are read where they are needed, by the sweep that deletes them.

2.0.0

  • 修复:「未使用术语」从计数列读取「未使用」,而核心自带的计数器统计的是已发布的文章——因此只被草稿、待审核文章、私密文章或回收站文章使用的术语会显示计数为零并被删除,连带删除其关系。这些文章恢复后失去标签,且无从知晓原因。现在一个术语必须完全没有任何关联才会被判定为未使用
  • 修复:清理「已无任何内容注册的分类法」的术语时,最终执行 DELETE FROM wp_terms WHERE term_id NOT IN ( SELECT term_id FROM wp_term_taxonomy ),即表中所有游离的行,而非该清理项刚刚孤立的那些行。界面既未计数也未列出的行也一并被删除。现在它会明确列出它孤立的术语
  • 修复:「孤立术语关系」通过在其 object_id 中查找 wp_posts 来判断某行是否为孤立项——这只在分类法属于文章时才问对了问题。对于注册在用户或评论上的分类法,object_id 是用户或评论 ID,因此任何 ID 恰好大于文章数量的关联关系都会被删除,而用户当时仍在。正因如此 link_category 曾被手工排除;现在该排除项由每个分类法注册时所针对的对象推导而来
  • 修复:oEmbed 清理匹配 %_oembed_%,而在 LIKE 模式中下划线匹配任意单个字符——因此它的含义是「任何两侧带字符、且包含 oembed 的元数据键」,属于包含匹配而非前缀匹配。其它插件的键会连同缓存一起被硬删除。WordPress 将这些键写为 _oembed_{hash},因此该模式现在已锚定在开头,并将下划线进行了转义
  • 修复:oEmbed 清理是唯一一个从未查询受保护键列表的元数据清理项,因此站点显式添加到 wp_sweep_postmeta_whitelist 的键还是被删除了——甚至读取了该列表却又将其忽略
  • 修复:优化数据库时运行不带前缀的 SHOW TABLES,即整个 schema 中的每一个表。在多个安装共享的数据库上——一种常见的托管方式——详情视图会列出每个同租户的表,而清理会对本管理员并不管理的安装执行 OPTIMIZE TABLE。现已将其限定在本安装的前缀范围内,并提供 wp_sweep_optimize_tables 供需要更多表的站点使用
  • BREAKING: Requires WordPress 6.8 and PHP 8.2.
  • 破坏性变更:界面仍在工具(Tools)下,但地址从 tools.php?page=wp-sweep/admin.php 改为 tools.php?page=wp-sweep。旧形式会把安装目录名写进 URL。
  • 破坏性变更:类名已重命名:WPSweep -> WP_SweepWPSweep_Api -> WP_Sweep_APIWPSweep_Command -> WP_Sweep_Command。所有过滤器与动作的名称保持不变。
  • 破坏性变更:WP_Sweep::$limit_details 已移除。请用 limit_details() 读取它,并用 wp_sweep_limit_details 过滤器修改它。
  • 破坏性变更:六个 wp_sweep_admin_*_sweep 动作现在在单一的清理表下方触发,顺序与以往一致,而不是分散在六个独立的表下方。
  • 破坏性变更:「全部清理(Sweep All)」已移除。请改为勾选表头复选框并应用清理(Sweep)批量操作,效果相同,且可让你排除不想清理的行。
  • 新增:批量清理。勾选你想要清理的项,一次性运行,而不必一次点击一项。
  • 新增:清理项在界面上按组划分。未筛选的视图将每项放在一个带图标的标题下——文章(Post)、评论(Comment)、用户(User)、术语(Term)、选项(Option)、数据库(Database)——同时仍是一张表,因此一次批量清理仍会运行你勾选的所有项。对某一列排序时会移除这些标题,而不是让它们继续描述已不再归组的行。
  • 新增:分组过滤器与可排序列,基于真正的 WP_List_Table
  • 新增:整个界面在关闭 JavaScript 时也能工作。行操作是普通的带随机数(nonce)的链接,批量操作是普通的表单提交。
  • 新增:元数据键白名单现在同样保护「重复」元数据清理项,而不仅是「孤立」项——readme 一直声称如此,但之前的代码从未做到。
  • 新增:wp_sweep_capabilitywp_sweep_limit_details 过滤器。WP-Sweep 没有设置界面:wp_sweep_limit_details 就是修改「详情」上限的方式。
  • 新增:每次清理都带有「移除什么」的描述,显示在其名称下方,并且值得关注的计数会被突出显示。
  • 新增:uninstall.php 会在整个网络(而非前一百个站点)范围内清理。WP-Sweep 自身不存储任何内容:无选项行、无数据表、无权限、无定时事件。
  • 新增:按照《插件手册》重构为 includes/ 结构。
  • 新增:PHPUnit 与 vitest 测试套件,以及覆盖六种 WordPress 与 PHP 组合、单站点与多站点的 GitHub Actions CI。
  • 变更:元数据键排除规则改为在 PHP 中匹配,而非使用 SQL LIKE,因此受保护键中的下划线就是下划线,而不是通配符。
  • 变更:每次数据库调用都经过同一个方法,且每条查询都已预处理。
  • 修复:管理界面上一个存储型 XSS 漏洞。「详情」列表通过字符串拼接构建并以 HTML 注入,因此包含标记的评论作者名会在管理员浏览器中作为脚本执行。
  • 修复:插件不再根据自身目录名构建路径,因此当它安装在 wp-sweep 以外的目录时,管理脚本也能正常加载。
  • 修复:将 wp_sweep_excluded_termids 过滤为空数组会产生无效的 SQL,导致「未使用术语」计数显示为空白。
  • 修复:只要术语 ID 与某个 default_<taxonomy> 选项匹配,它就会从「未使用术语」清理中被排除,即使该选项指向的术语已不存在。WordPress 默认将 default_link_category 设为 2,因此在大多数站点上,任何 ID 为 2 的术语都会悄悄拒绝被清理。
  • 修复:关闭 JavaScript 时清理数据后不显示任何结果;现在会显示结果。
  • 修复:多站点卸载循环原先停在 100 个站点,为读取一个字段而实例化整个站点对象,并导致切换堆栈少退了一层。
  • 修复:请求参数已进行净化,并与插件自身的清理项列表进行校验。
  • 修复:wp sweep 打印的两条「Sweep Complete(清理完成)」消息,是插件中唯一从未经过翻译函数的字符串