PluginProbe
ezCache / 2.5
ezCache v2.5
2.6.5 2.6.4 2.6.2 2.6.3 2.6.1 2.6.0 2.5.6 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5 2.2.1 2.2.2 trunk 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.3 1.3.1 1.3.10 1.3.11 All 49 releases
ezcache / includes / BackgroundProcesses / ConvertWebpProcess.php

ConvertWebpProcess.php in ezCache 2.5, at includes/BackgroundProcesses/ConvertWebpProcess.php

220 lines 6.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Upress\EzCache\BackgroundProcesses;
4
5 use Upress\EzCache\LicenseApi;
6 use Upress\EzCache\PremiumFeatures;
7 use Upress\EzCache\Settings;
8 use Upress\EzCache\Utilities\Logger;
9 use Upress\EzCache\WebpApi;
10 use \WP_Background_Process;
11
12 class ConvertWebpProcess {
13 protected $action = 'ezcache_convert_images_to_webp';
14 protected $hook;
15 protected $convert_queue;
16
17 public function __construct() {
18 $this->hook = $this->action . '_hook';
19 $this->convert_queue = $this->action . '_convert_queue';
20
21 add_action( $this->hook, [ $this, 'process_batch' ] );
22 }
23
24 public function schedule() {
25 $settings = Settings::get_settings();
26
27 if ( $settings->enable_webp_support && ! wp_next_scheduled( $this->hook ) ) {
28 wp_schedule_single_event( time(), $this->hook );
29
30 wp_safe_remote_get( add_query_arg( [ 'doing_wp_cron' => time() ], site_url( '/wp-cron.php' ) ), [
31 'user-agent' => 'ezCache Background WebP Processor Cron Dispatcher',
32 'timeout' => 0.1,
33 ] );
34 }
35 }
36
37 public function add_to_queue( $image_id, $cache_file ) {
38 if ( ! $cache_file ) {
39 return false;
40 }
41
42 $queue = get_site_option( $this->convert_queue, [] );
43
44 $key = 'i' . $image_id;
45
46 if ( ! isset( $queue[ $key ] ) ) {
47 $queue[ $key ] = [
48 'image_id' => $image_id,
49 'cache_files' => [],
50 ];
51 }
52
53 if ( in_array( $cache_file, $queue[ $key ]['cache_files'] ) ) {
54 return true;
55 }
56
57 $queue[ $key ]['cache_files'][] = $cache_file;
58
59 update_site_option( $this->convert_queue, $queue );
60 }
61
62 protected function shift_queue( $count ) {
63 $queue = get_site_option( $this->convert_queue, [] );
64 if ( ! count( $queue ) ) {
65 return [];
66 }
67
68 $to_run = array_splice( $queue, 0, $count );
69
70 update_site_option( $this->convert_queue, $queue );
71
72 return $to_run;
73 }
74
75 protected function is_queue_empty() {
76 $queue = get_site_option( $this->convert_queue, [] );
77
78 return count( $queue ) == 0;
79 }
80
81
82 public function process_batch() {
83 global $wpdb;
84
85 $queue = $this->shift_queue( 15 );
86 if ( 0 >= count( $queue ) ) {
87 return;
88 }
89
90 $image_ids = array_column( $queue, 'image_id' );
91 $images = $wpdb->get_results(
92 $wpdb->prepare(
93 "SELECT * FROM `{$wpdb->prefix}ezcache_webp_images` WHERE `id` IN (" . rtrim( str_repeat( '%d,', count( $image_ids ) ), ',' ) . ") LIMIT 15",
94 $image_ids
95 )
96 );
97 $precached_images = [];
98 foreach ( $images as $image ) {
99 $precached_images[ $image->id ] = $image;
100 }
101
102 foreach ( $queue as $data ) {
103 $image_id = $data['image_id'];
104 $cache_files = $data['cache_files'];
105
106 try {
107 $this->convert_image( $image_id, $cache_files, $precached_images );
108 } catch ( \Exception $ex ) {
109 Logger::log( "ezCache ConvertWebpProcess::convert_image error: {$ex->getMessage()}\n{$ex->getTraceAsString()}" );
110 }
111 }
112
113 if ( ! $this->is_queue_empty() ) {
114 $this->schedule();
115
116 return;
117 }
118
119 $wpdb->query( "OPTIMIZE TABLE `{$wpdb->prefix}ezcache_webp_images`" );
120 }
121
122 protected function convert_image( $image_id, $cache_files, $precached_images = [] ) {
123 global $wpdb;
124
125 $image = isset( $precached_images[$image_id] ) ? $precached_images[$image_id] : $wpdb->get_row(
126 $wpdb->prepare(
127 "SELECT * FROM `{$wpdb->prefix}ezcache_webp_images` WHERE `id` = %s LIMIT 1",
128 [ $image_id ]
129 )
130 );
131
132 if ( ! $image ) {
133 Logger::log( "ezCache WebP Background Processor: image with ID {$image_id} was not found." );
134
135 return;
136 }
137
138 if ( 'completed' == $image->status ) {
139 $this->replace_links( $cache_files, $image->url, $image->webp_url );
140
141 return;
142 }
143
144 // only download the file if we don't have it locally
145 if ( ! file_exists( $image->webp_path ) || filesize( $image->webp_path ) <= 2 || stripos( file_get_contents( $image->webp_path ), '"success":false' ) ) {
146 // Premium gate — Pro is unlocked for everyone, kept as defensive guard.
147 if ( ! PremiumFeatures::is_premium() ) {
148 Logger::log( "ezCache WebP: image {$image_id} skipped, premium required." );
149 return;
150 }
151
152 $converter = new WebpApi( 'unlocked_pro' );
153 $response = $converter->convert( $image->path );
154
155 if ( is_wp_error( $response ) || stripos( $response['info']['content-type'], 'json' ) || stripos( $response['data'], '"success":false' ) ) {
156 $wpdb->query(
157 $wpdb->prepare(
158 "UPDATE `{$wpdb->prefix}ezcache_webp_images` SET `status` = %s WHERE `id` = %d",
159 [ 'failed', $image_id ]
160 )
161 );
162 Logger::log( "ezCache WebP Background Processor: " . ( is_wp_error( $response ) ? $response->get_error_message() : $response['data'] ) );
163
164 // delete the broken file
165 if ( file_exists( $image->webp_path ) ) {
166 unlink( $image->webp_path );
167 }
168
169 return;
170 }
171
172 file_put_contents( $image->webp_path, $response['data'] );
173 }
174
175 $original_size = filesize( $image->path );
176 $webp_size = filesize( $image->webp_path );
177
178 $wpdb->query(
179 $wpdb->prepare(
180 "UPDATE `{$wpdb->prefix}ezcache_webp_images` SET `status` = %s, `original_size` = %d, `webp_size` = %d WHERE `id` = %d",
181 [ 'completed', $original_size, $webp_size, $image_id ]
182 )
183 );
184
185 $this->replace_links( $cache_files, $image->url, $image->webp_url );
186 }
187
188 protected function replace_links( $cache_files, $image_url, $webp_url ) {
189 foreach ( $cache_files as $cache_file ) {
190 if ( ! file_exists( $cache_file ) ) {
191 // File has been deleted? maybe the cache got cleared before the process finished
192 continue;
193 }
194
195 $handle = @fopen( $cache_file, 'c+' );
196
197 if ( $handle && @flock( $handle, LOCK_EX ) ) {
198 $contents = gzdecode( fread( $handle, filesize( $cache_file ) ) );
199
200 $contents = preg_replace(
201 '/' . preg_quote( $image_url, '/' ) . '(?!\.webp)/i',
202 $webp_url,
203 $contents
204 );
205
206 ftruncate( $handle, 0 );
207 fseek( $handle, 0 );
208 fwrite( $handle, gzencode( $contents, 6, FORCE_GZIP ) );
209 flock( $handle, LOCK_UN );
210 } else {
211 Logger::log( "ezCache WebP Background Processor: Could not get a lock on {$cache_file}, failed updating WebP image URLs" );
212 }
213
214 if ( $handle ) {
215 fclose( $handle );
216 }
217 }
218 }
219 }
220