PluginProbe
QuickWebP – WebP & AVIF Image Optimizer, Compression & SEO for WordPress / 4.1.1
QuickWebP – WebP & AVIF Image Optimizer, Compression & SEO for WordPress v4.1.1
4.1.1 4.1.0 4.0.1 4.0.0 3.3.1 3.3.0 trunk 1.0.0 2.0.0 2.1.0 3.0.0 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8
← All changes | admin/class-migration.php +76 -160 trunk4.1.1 View file →
@@ -12,19 +12,12 @@
12 12 */
13 13 class Quickwebp_Migration {
14 14
15 15 /**
16 - * Last attachment ID processed by the current migration batch.
17 - *
18 - * @var int
19 - */
20 - private $migration_cursor = 0;
21 -
22 - /**
23 16 * Init the migration.
24 17 */
25 18 public function init_migration() {
26 - $this->maybe_schedule_optimization_stats_migration();
19 + $this->maybe_migrate_optimization_stats();
27 20
28 21 $last_version = get_option( 'quickwebp_plugin_version', '1.0.0' );
29 22
30 23 if ( version_compare( $last_version, QUICKWEBP_VERSION, '<' ) ) {
@@ -33,106 +26,71 @@
33 26 }
34 27 }
35 28
36 29 /**
37 - * Schedule the optimization statistics migration outside the admin request.
30 + * Backfill installation age and optimization statistics for existing sites.
38 31 */
39 - private function maybe_schedule_optimization_stats_migration() {
32 + private function maybe_migrate_optimization_stats() {
40 33 if ( get_option( 'quickwebp_optimization_stats_migrated', false ) ) {
41 34 return;
42 35 }
43 36
44 - if ( ! wp_next_scheduled( 'quickwebp_optimization_stats_migration_hook' ) ) {
45 - wp_schedule_single_event( time() + MINUTE_IN_SECONDS, 'quickwebp_optimization_stats_migration_hook' );
46 - }
47 - }
48 -
49 - /**
50 - * Process one bounded batch of the optimization statistics migration.
51 - */
52 - public function migrate_optimization_stats_batch() {
53 - if ( get_option( 'quickwebp_optimization_stats_migrated', false ) ) {
54 - return;
55 - }
56 -
57 - $lock_token = $this->acquire_migration_lock();
58 - if ( ! $lock_token ) {
59 - $this->schedule_next_migration_batch();
60 - return;
61 - }
62 -
63 - $state = get_option( 'quickwebp_optimization_stats_migration_state', array() );
64 - $state = wp_parse_args(
65 - is_array( $state ) ? $state : array(),
66 - array(
67 - 'cursor' => 0,
68 - 'images_optimized' => 0,
69 - 'bytes_before' => 0,
70 - 'bytes_after' => 0,
71 - 'oldest_time' => 0,
72 - )
37 + $stats = array(
38 + 'images_optimized' => 0,
39 + 'bytes_before' => 0,
40 + 'bytes_after' => 0,
41 + 'bytes_saved' => 0,
73 42 );
74 - $this->migration_cursor = absint( $state['cursor'] );
43 + $page = 1;
75 44
76 - add_filter( 'posts_where', array( $this, 'filter_migration_posts_after_cursor' ), 10, 2 );
77 - $query = new WP_Query(
78 - array(
79 - 'post_type' => 'attachment',
80 - 'post_status' => 'any',
81 - 'posts_per_page' => 50,
82 - 'fields' => 'ids',
83 - 'orderby' => 'ID',
84 - 'order' => 'ASC',
85 - 'no_found_rows' => true,
86 - 'update_post_meta_cache' => true,
87 - 'update_post_term_cache' => false,
88 - 'quickwebp_migration' => true,
89 - // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
90 - 'meta_query' => array(
91 - 'relation' => 'OR',
92 - array(
93 - 'key' => 'quickwebp_already_optimized',
94 - 'compare' => 'EXISTS',
45 + do {
46 + $query = new WP_Query(
47 + array(
48 + 'post_type' => 'attachment',
49 + 'post_status' => 'any',
50 + 'posts_per_page' => 200,
51 + 'paged' => $page,
52 + 'fields' => 'ids',
53 + 'orderby' => 'ID',
54 + 'order' => 'ASC',
55 + 'no_found_rows' => false,
56 + 'update_post_meta_cache' => false,
57 + 'update_post_term_cache' => false,
58 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
59 + 'meta_query' => array(
60 + 'relation' => 'OR',
61 + array(
62 + 'key' => 'quickwebp_already_optimized',
63 + 'compare' => 'EXISTS',
64 + ),
65 + array(
66 + 'key' => 'quickwebp_data',
67 + 'compare' => 'EXISTS',
68 + ),
95 69 ),
96 - array(
97 - 'key' => 'quickwebp_data',
98 - 'compare' => 'EXISTS',
99 - ),
100 - ),
101 - )
102 - );
103 - remove_filter( 'posts_where', array( $this, 'filter_migration_posts_after_cursor' ), 10 );
70 + )
71 + );
104 72
105 - foreach ( $query->posts as $attachment_id ) {
106 - $data = get_post_meta( $attachment_id, 'quickwebp_data', true );
107 - $state['cursor'] = $attachment_id;
108 - $state['images_optimized']++;
109 - $attachment_time = (int) get_post_time( 'U', true, $attachment_id );
110 - $state['oldest_time'] = ! $state['oldest_time'] ? $attachment_time : min( $state['oldest_time'], $attachment_time );
73 + foreach ( $query->posts as $attachment_id ) {
74 + $data = get_post_meta( $attachment_id, 'quickwebp_data', true );
111 75
112 - if ( ! is_array( $data ) ) {
113 - continue;
114 - }
76 + $stats['images_optimized']++;
115 77
116 - foreach ( $data as $size_data ) {
117 - $state['bytes_before'] += absint( $size_data['original_size'] ?? 0 );
118 - $state['bytes_after'] += absint( $size_data['optimized_size'] ?? 0 );
78 + if ( ! is_array( $data ) ) {
79 + continue;
80 + }
81 +
82 + foreach ( $data as $size_data ) {
83 + $stats['bytes_before'] += absint( $size_data['original_size'] ?? 0 );
84 + $stats['bytes_after'] += absint( $size_data['optimized_size'] ?? 0 );
85 + }
119 86 }
120 - }
121 87
122 - if ( count( $query->posts ) === 50 ) {
123 - update_option( 'quickwebp_optimization_stats_migration_state', $state, false );
124 - $this->release_migration_lock( $lock_token );
125 - $this->schedule_next_migration_batch();
126 - return;
127 - }
88 + $max_pages = (int) $query->max_num_pages;
89 + $page++;
90 + } while ( $page <= $max_pages );
128 91
129 - $stats = array(
130 - 'images_optimized' => absint( $state['images_optimized'] ),
131 - 'bytes_before' => absint( $state['bytes_before'] ),
132 - 'bytes_after' => absint( $state['bytes_after'] ),
133 - 'bytes_saved' => max( 0, absint( $state['bytes_before'] ) - absint( $state['bytes_after'] ) ),
134 - );
92 + $stats['bytes_saved'] = max( 0, $stats['bytes_before'] - $stats['bytes_after'] );
135 93
136 94 if ( $stats['images_optimized'] > 0 ) {
137 95 update_option( 'quickwebp_optimization_stats', $stats, false );
138 96 }
@@ -137,80 +95,38 @@
137 95 update_option( 'quickwebp_optimization_stats', $stats, false );
138 96 }
139 97
140 98 if ( ! get_option( 'quickwebp_installed_at', false ) ) {
141 - $installed_at = $state['oldest_time'] ? absint( $state['oldest_time'] ) : time() - ( 8 * DAY_IN_SECONDS );
99 + $oldest_optimized = get_posts(
100 + array(
101 + 'post_type' => 'attachment',
102 + 'post_status' => 'any',
103 + 'posts_per_page' => 1,
104 + 'orderby' => 'date',
105 + 'order' => 'ASC',
106 + 'fields' => 'ids',
107 + 'update_post_meta_cache' => false,
108 + 'update_post_term_cache' => false,
109 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
110 + 'meta_query' => array(
111 + array(
112 + 'key' => 'quickwebp_already_optimized',
113 + 'compare' => 'EXISTS',
114 + ),
115 + ),
116 + )
117 + );
118 +
119 + if ( ! empty( $oldest_optimized ) ) {
120 + $installed_at = get_post_time( 'U', true, $oldest_optimized[0] );
121 + } else {
122 + $installed_at = time() - ( 8 * DAY_IN_SECONDS );
123 + }
124 +
142 125 add_option( 'quickwebp_installed_at', $installed_at, '', false );
143 126 }
144 127
145 128 update_option( 'quickwebp_optimization_stats_migrated', 1, false );
146 - delete_option( 'quickwebp_optimization_stats_migration_state' );
147 - $this->release_migration_lock( $lock_token );
148 - }
149 -
150 - /**
151 - * Restrict the migration query to attachment IDs after the saved cursor.
152 - *
153 - * @param string $where Query WHERE clause.
154 - * @param WP_Query $query Current query.
155 - * @return string
156 - */
157 - public function filter_migration_posts_after_cursor( $where, $query ) {
158 - global $wpdb;
159 -
160 - if ( $query->get( 'quickwebp_migration' ) ) {
161 - $where .= $wpdb->prepare( " AND {$wpdb->posts}.ID > %d", $this->migration_cursor );
162 - }
163 -
164 - return $where;
165 - }
166 -
167 - /**
168 - * Schedule the next migration batch unless one is already pending.
169 - */
170 - private function schedule_next_migration_batch() {
171 - if ( ! wp_next_scheduled( 'quickwebp_optimization_stats_migration_hook' ) ) {
172 - wp_schedule_single_event( time() + MINUTE_IN_SECONDS, 'quickwebp_optimization_stats_migration_hook' );
173 - }
174 - }
175 -
176 - /**
177 - * Acquire an expiring migration lock.
178 - *
179 - * @return string|false
180 - */
181 - private function acquire_migration_lock() {
182 - $lock_token = wp_generate_uuid4();
183 - $lock = array(
184 - 'token' => $lock_token,
185 - 'expires' => time() + ( 5 * MINUTE_IN_SECONDS ),
186 - );
187 -
188 - if ( add_option( 'quickwebp_optimization_stats_migration_lock', $lock, '', false ) ) {
189 - return $lock_token;
190 - }
191 -
192 - $existing_lock = get_option( 'quickwebp_optimization_stats_migration_lock', array() );
193 - if ( is_array( $existing_lock ) && absint( $existing_lock['expires'] ?? 0 ) < time() ) {
194 - delete_option( 'quickwebp_optimization_stats_migration_lock' );
195 - if ( add_option( 'quickwebp_optimization_stats_migration_lock', $lock, '', false ) ) {
196 - return $lock_token;
197 - }
198 - }
199 -
200 - return false;
201 - }
202 -
203 - /**
204 - * Release the migration lock owned by this process.
205 - *
206 - * @param string $lock_token Lock ownership token.
207 - */
208 - private function release_migration_lock( $lock_token ) {
209 - $lock = get_option( 'quickwebp_optimization_stats_migration_lock', array() );
210 - if ( is_array( $lock ) && hash_equals( (string) ( $lock['token'] ?? '' ), $lock_token ) ) {
211 - delete_option( 'quickwebp_optimization_stats_migration_lock' );
212 - }
213 129 }
214 130
215 131 /**
216 132 * Run migration tasks based on the last version.