PluginProbe
QuickWebP – WebP & AVIF Image Optimizer, Compression & SEO for WordPress / 4.1.2
QuickWebP – WebP & AVIF Image Optimizer, Compression & SEO for WordPress v4.1.2
4.1.2 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
quickwebp / admin / class-migration.php

class-migration.php in QuickWebP – WebP & AVIF Image Optimizer, Compression & SEO for WordPress 4.1.2, at admin/class-migration.php

242 lines 7.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
4 /**
5 * The class responsible for handling the migration.
6 *
7 * @link https://webdeclic.com
8 * @since 3.4.0
9 *
10 * @package Quickwebp
11 * @subpackage Quickwebp/admin
12 */
13 class Quickwebp_Migration {
14
15 /**
16 * Last attachment ID processed by the current migration batch.
17 *
18 * @var int
19 */
20 private $migration_cursor = 0;
21
22 /**
23 * Init the migration.
24 */
25 public function init_migration() {
26 $this->maybe_schedule_optimization_stats_migration();
27
28 $last_version = get_option( 'quickwebp_plugin_version', '1.0.0' );
29
30 if ( version_compare( $last_version, QUICKWEBP_VERSION, '<' ) ) {
31 $this->run_migration_tasks( $last_version );
32 update_option( 'quickwebp_plugin_version', QUICKWEBP_VERSION );
33 }
34 }
35
36 /**
37 * Schedule the optimization statistics migration outside the admin request.
38 */
39 private function maybe_schedule_optimization_stats_migration() {
40 if ( get_option( 'quickwebp_optimization_stats_migrated', false ) ) {
41 return;
42 }
43
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 )
73 );
74 $this->migration_cursor = absint( $state['cursor'] );
75
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',
95 ),
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 );
104
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 );
111
112 if ( ! is_array( $data ) ) {
113 continue;
114 }
115
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 );
119 }
120 }
121
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 }
128
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 );
135
136 if ( $stats['images_optimized'] > 0 ) {
137 update_option( 'quickwebp_optimization_stats', $stats, false );
138 }
139
140 if ( ! get_option( 'quickwebp_installed_at', false ) ) {
141 $installed_at = $state['oldest_time'] ? absint( $state['oldest_time'] ) : time() - ( 8 * DAY_IN_SECONDS );
142 add_option( 'quickwebp_installed_at', $installed_at, '', false );
143 }
144
145 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 }
214
215 /**
216 * Run migration tasks based on the last version.
217 */
218 private function run_migration_tasks( $last_version ) {
219
220 if ( version_compare( $last_version, '3.4.0', '<' ) ) {
221
222 $quality = get_option( 'quickwebp_settings_conversion_quality', '' );
223 if ( ! empty( $quality ) && ! in_array( $quality, array( 'low', 'medium', 'high', 'extra_high' ) ) ) {
224
225 $quality = (int) $quality;
226
227 if ( $quality < 50 ) {
228 $quality = 'low';
229 } elseif ( $quality < 60 ) {
230 $quality = 'medium';
231 } elseif ( $quality < 80 ) {
232 $quality = 'high';
233 } else {
234 $quality = 'extra_high';
235 }
236
237 update_option( 'quickwebp_settings_conversion_quality', $quality );
238 }
239 }
240 }
241 }
242