• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

equalizedigital / accessibility-checker / 16596570333

29 Jul 2025 12:49PM UTC coverage: 31.425%. First build
16596570333

Pull #1124

github

web-flow
Merge 0c693cd43 into 1cdb35c39
Pull Request #1124: Release v1.29.0

183 of 273 new or added lines in 50 files covered. (67.03%)

2827 of 8996 relevant lines covered (31.43%)

1.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/admin/class-orphaned-issues-cleanup.php
1
<?php
2
/**
3
 * Scheduled cleanup of orphaned issues.
4
 *
5
 * @package Accessibility_Checker
6
 */
7

8
namespace EDAC\Admin;
9

10
if ( ! defined( 'ABSPATH' ) ) {
11
        exit; // Exit if accessed directly.
12
}
13

14
/**
15
 * Class Orphaned_Issues_Cleanup
16
 *
17
 * @since 1.29.0
18
 */
19
class Orphaned_Issues_Cleanup {
20

21
        /**
22
         * Cron event name.
23
         *
24
         * @var string
25
         */
26
        const EVENT = 'edac_cleanup_orphaned_issues';
27

28
        /**
29
         * Max number of posts to cleanup per run.
30
         *
31
         * @var int
32
         */
33
        const BATCH_LIMIT = 50;
34

35
        /**
36
         * Batch size for cleanup (overrides BATCH_LIMIT if set).
37
         *
38
         * @var int|null
39
         */
40
        private ?int $batch_size = null;
41

42
        /**
43
         * Register hooks.
44
         *
45
         * @since 1.29.0
46
         *
47
         * @return void
48
         */
49
        public function init_hooks() {
NEW
50
                self::schedule_event();
×
NEW
51
                add_action( self::EVENT, [ $this, 'run_cleanup' ] );
×
52
        }
53

54
        /**
55
         * Schedule the cleanup event.
56
         *
57
         * @since 1.29.0
58
         *
59
         * @return void
60
         */
61
        public static function schedule_event() {
NEW
62
                if ( ! wp_next_scheduled( self::EVENT ) ) {
×
NEW
63
                        wp_schedule_event( time(), 'daily', self::EVENT );
×
64
                }
65
        }
66

67
        /**
68
         * Unschedule the cleanup event.
69
         *
70
         * @since 1.29.0
71
         *
72
         * @return void
73
         */
74
        public static function unschedule_event() {
NEW
75
                $timestamp = wp_next_scheduled( self::EVENT );
×
NEW
76
                if ( $timestamp ) {
×
NEW
77
                        wp_unschedule_event( $timestamp, self::EVENT );
×
78
                }
79
        }
80

81
        /**
82
         * Set a custom batch size for this cleanup instance.
83
         *
84
         * @since 1.29.0
85
         *
86
         * @param int $batch_size A custom size of batch to process.
87
         * @return void
88
         */
89
        public function set_batch_size( int $batch_size ): void {
NEW
90
                $this->batch_size = $batch_size;
×
91
        }
92

93
        /**
94
         * Get the batch size to use.
95
         *
96
         * @return int
97
         */
98
        protected function get_batch_size(): int {
NEW
99
                return $this->batch_size ?? self::BATCH_LIMIT;
×
100
        }
101

102
        /**
103
         * Get orphaned post IDs (posts in issues table but not in posts table).
104
         *
105
         * @since 1.29.0
106
         *
107
         * @return int[] Array of orphaned post IDs.
108
         */
109
        public function get_orphaned_post_ids(): array {
NEW
110
                global $wpdb;
×
NEW
111
                $table_name = edac_get_valid_table_name( $wpdb->prefix . 'accessibility_checker' );
×
NEW
112
                if ( ! $table_name ) {
×
NEW
113
                        return [];
×
114
                }
115
                
NEW
116
                $scannable_post_types = Settings::get_scannable_post_types();
×
NEW
117
                if ( empty( $scannable_post_types ) ) {
×
118
                        // No scannable post types: treat all issues as orphaned for this site.
119
                        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
NEW
120
                        return $wpdb->get_col(
×
NEW
121
                                $wpdb->prepare(
×
NEW
122
                                        'SELECT DISTINCT postid FROM %i WHERE siteid = %d LIMIT %d',
×
NEW
123
                                        $table_name,
×
NEW
124
                                        get_current_blog_id(),
×
NEW
125
                                        $this->get_batch_size()
×
NEW
126
                                )
×
NEW
127
                        );
×
128
                }
129

130
                // Build placeholders for each post type.
NEW
131
                $placeholders = implode( ',', array_fill( 0, count( $scannable_post_types ), '%s' ) );
×
NEW
132
                $sql          = "SELECT DISTINCT t.postid
×
133
                                FROM %i AS t
NEW
134
                                LEFT JOIN {$wpdb->posts} AS p ON t.postid = p.ID
×
135
                                WHERE t.siteid = %d
NEW
136
                                AND (p.ID IS NULL OR p.post_type NOT IN ($placeholders))
×
NEW
137
                                LIMIT %d";
×
138

139
                // Build arguments for prepare: table name, site id, ...post types, batch size.
NEW
140
                $args = array_merge(
×
NEW
141
                        [ $sql, $table_name, get_current_blog_id() ],
×
NEW
142
                        $scannable_post_types,
×
NEW
143
                        [ $this->get_batch_size() ]
×
NEW
144
                );
×
145

146
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching -- Dynamic placeholders for IN() are required for security and cannot be avoided in this context.
NEW
147
                return $wpdb->get_col( call_user_func_array( [ $wpdb, 'prepare' ], $args ) );
×
148
        }
149

150
        /**
151
         * Delete all issues for a given orphaned post ID.
152
         *
153
         * @since 1.29.0
154
         *
155
         * @param int $post_id The orphaned post ID.
156
         */
157
        public function delete_orphaned_post( int $post_id ) {
NEW
158
                Purge_Post_Data::delete_post( $post_id );
×
159
        }
160

161
        /**
162
         * Cleanup orphaned issues (batch process).
163
         *
164
         * @since 1.29.0
165
         *
166
         * @return int[] Array of deleted orphaned post IDs.
167
         */
168
        public function run_cleanup(): array {
NEW
169
                $orphaned = $this->get_orphaned_post_ids();
×
NEW
170
                if ( empty( $orphaned ) ) {
×
NEW
171
                        return [];
×
172
                }
NEW
173
                foreach ( $orphaned as $post_id ) {
×
NEW
174
                        $this->delete_orphaned_post( (int) $post_id );
×
175
                }
NEW
176
                return $orphaned;
×
177
        }
178
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc