PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.3.6
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.3.6
1.3.6 1.3.5 1.3.4 1.3.3 1.3.2 1.3.1 1.3.0 1.2.4 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 All 32 releases
← All changes | includes/modules/Database/DatabaseModule.php +92 -6 1.0.3 → 1.3.6 View file →
@@ -12,8 +12,10 @@
12 12 declare(strict_types=1);
13 13
14 14 namespace XSpeed\Modules\Database;
15 15
16 +defined( 'ABSPATH' ) || exit;
17 +
16 18 use XSpeed\Database_Cleaner;
17 19 use XSpeed\Module;
18 20 use XSpeed\Settings_Manager;
19 21
@@ -24,11 +26,11 @@
24 26 public const VERSION = '1.0.0';
25 27
26 28 public function ui_metadata(): array {
27 29 return array(
28 - 'label' => 'Database',
30 + 'label' => __( 'Database', 'xspeed' ),
29 31 'icon' => 'Trash2',
30 - 'description' => 'Scan + clean WordPress bloat (revisions, spam, transients, orphan meta) and optimize tables.',
32 + 'description' => __( 'Scan + clean WordPress bloat (revisions, spam, transients, orphan meta) and optimize tables.', 'xspeed' ),
31 33 'custom_panel' => 'DatabaseCleanerPanel',
32 34 );
33 35 }
34 36
@@ -43,21 +45,94 @@
43 45 'hourly' => 'Hourly',
44 46 'daily' => 'Daily',
45 47 'weekly' => 'Weekly',
46 48 ),
47 - 'label' => 'Auto-Cleanup Schedule',
48 - 'description' => 'How often to run cleanup automatically. Manual means cleanup only runs when you press the button.',
49 + 'label' => __( 'Auto-Cleanup Schedule', 'xspeed' ),
50 + 'description' => __( 'How often to run cleanup automatically. Manual means cleanup only runs when you press the button.', 'xspeed' ),
49 51 ),
50 52 'included_types' => array(
51 53 'type' => 'list',
52 54 'default' => array(),
53 55 'item_type' => 'string',
54 - 'label' => 'Auto-Cleanup Types',
55 - 'description' => 'Which cleanup categories run on the schedule above. Leave empty to keep auto-cleanup disabled even if a schedule is set.',
56 + 'label' => __( 'Auto-Cleanup Types', 'xspeed' ),
57 + 'description' => __( 'Which cleanup categories run on the schedule above. Leave empty to keep auto-cleanup disabled even if a schedule is set.', 'xspeed' ),
56 58 ),
59 + 'limit_revisions' => array(
60 + 'type' => 'bool',
61 + 'default' => false,
62 + 'label' => __( 'Limit Post Revisions', 'xspeed' ),
63 + 'description' => __( 'Cap how many revisions WordPress keeps for each post. Cleanup removes revisions that already exist; this stops them piling up again.', 'xspeed' ),
64 + ),
65 + 'revisions_to_keep' => array(
66 + 'type' => 'int',
67 + 'default' => 5,
68 + 'min' => 0,
69 + 'max' => 500,
70 + 'label' => __( 'Revisions to Keep', 'xspeed' ),
71 + 'description' => __( 'Used when Limit Post Revisions is on. 0 turns revisions off completely. Older revisions are trimmed the next time each post is saved; to remove the backlog now, clean Post Revisions above.', 'xspeed' ),
72 + ),
57 73 );
58 74 }
59 75
76 + /**
77 + * WP_POST_REVISIONS as wp-config.php (or an earlier plugin) set it, or
78 + * null when nobody did. Read in boot(), on plugins_loaded: after that,
79 + * core defines it as `true` itself, so defined() stops telling the two
80 + * cases apart.
81 + *
82 + * Not the schema's `constants` pinning (#398): that tests defined() at
83 + * request time, when core's own default makes it always true.
84 + *
85 + * @var bool|int|null
86 + */
87 + private static $configured_revisions = null;
88 +
89 + /**
90 + * @param int $num Revisions WordPress would keep; -1 means all.
91 + * @param \WP_Post $post
92 + * @return int
93 + */
94 + public static function filter_revisions_to_keep( $num, $post ) {
95 + // A wp-config.php value is the site owner's decision; leave it alone.
96 + if ( null !== self::$configured_revisions ) {
97 + return $num;
98 + }
99 + // Core already answered 0 for post types without revision support.
100 + if ( ! $post instanceof \WP_Post || ! post_type_supports( $post->post_type, 'revisions' ) ) {
101 + return $num;
102 + }
103 + $opts = Settings_Manager::get( self::SLUG );
104 + if ( empty( $opts['limit_revisions'] ) ) {
105 + return $num;
106 + }
107 + return max( 0, (int) ( $opts['revisions_to_keep'] ?? 5 ) );
108 + }
109 +
110 + public function ui_notices(): array {
111 + if ( null === self::$configured_revisions ) {
112 + return array();
113 + }
114 + $value = self::$configured_revisions;
115 + if ( true === $value ) {
116 + $shown = 'true';
117 + } elseif ( false === $value ) {
118 + $shown = 'false';
119 + } else {
120 + $shown = (string) (int) $value;
121 + }
122 + return array(
123 + array(
124 + 'tone' => 'info',
125 + 'title' => __( 'The revision limit is set outside xSpeed', 'xspeed' ),
126 + 'body' => sprintf(
127 + /* translators: %s: value of WP_POST_REVISIONS, e.g. 5 or false. */
128 + __( 'WP_POST_REVISIONS is set to %s, usually in wp-config.php, so Limit Post Revisions below has no effect. Remove that definition to manage the limit here.', 'xspeed' ),
129 + $shown
130 + ),
131 + ),
132 + );
133 + }
134 +
60 135 public function rest_routes(): array {
61 136 // Schema POST/GET come from the base; we add three custom routes.
62 137 $default = parent::rest_routes();
63 138 return array_merge(
@@ -87,8 +162,9 @@
87 162 array(
88 163 'name' => 'xspeed db',
89 164 'callback' => array( $this, 'cli_handler' ),
90 165 'shortdesc' => 'Scan or clean WordPress bloat. Use --types=a,b to clean specific categories.',
166 + 'ai_hint' => 'Find and remove database bloat (post revisions, spam comments, expired transients, orphaned metadata). Always `scan` first to see what would go; `clean` is destructive and requires confirmation.',
91 167 'synopsis' => array(
92 168 array(
93 169 'type' => 'positional',
94 170 'name' => 'action',
@@ -105,8 +181,10 @@
105 181 );
106 182 }
107 183
108 184 public function boot(): void {
185 + self::$configured_revisions = defined( 'WP_POST_REVISIONS' ) ? constant( 'WP_POST_REVISIONS' ) : null;
186 + add_filter( 'wp_revisions_to_keep', array( __CLASS__, 'filter_revisions_to_keep' ), 10, 2 );
109 187 add_action( Database_Cleaner::CRON_HOOK, array( Database_Cleaner::class, 'cron_tick' ) );
110 188 add_action( 'update_option_xspeed_module_database', array( $this, 'on_settings_change' ), 10, 2 );
111 189 add_action( 'add_option_xspeed_module_database', array( $this, 'on_settings_added' ), 10, 2 );
112 190 }
@@ -115,8 +193,16 @@
115 193 wp_clear_scheduled_hook( Database_Cleaner::CRON_HOOK );
116 194 }
117 195
118 196 public function on_settings_change( $old, $new ): void {
197 + // A revision-limit save must not reschedule cleanup: apply_schedule()
198 + // restarts the timer, which pulled a weekly run forward on every save.
199 + $old = is_array( $old ) ? $old : array();
200 + if ( is_array( $new )
201 + && ( $old['schedule'] ?? null ) === ( $new['schedule'] ?? null )
202 + && ( $old['included_types'] ?? null ) === ( $new['included_types'] ?? null ) ) {
203 + return;
204 + }
119 205 $schedule = is_array( $new ) ? (string) ( $new['schedule'] ?? 'manual' ) : 'manual';
120 206 $types = is_array( $new ) && isset( $new['included_types'] ) && is_array( $new['included_types'] ) ? $new['included_types'] : array();
121 207 Database_Cleaner::apply_schedule( $schedule, $types );
122 208 }