PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / trunk
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF vtrunk
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 trunk 1.10 1.3.3 1.3.4 1.3.5 1.3.5.1 1.3.5.2 1.3.6 1.3.6.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 All 103 releases
imagify / inc / 3rd-party / nextgen-gallery / classes / Bulk / NGG.php

NGG.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF trunk, at inc/3rd-party/nextgen-gallery/classes/Bulk/NGG.php

293 lines 8.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Imagify\ThirdParty\NGG\Bulk;
3
4 use C_Gallery_Storage;
5 use Imagify\Bulk\AbstractBulk;
6 use Imagify\ThirdParty\NGG\DB;
7
8 /**
9 * Class to use for bulk for NextGen Gallery.
10 *
11 * @since 1.9
12 */
13 class NGG extends AbstractBulk {
14 /**
15 * Context "short name".
16 *
17 * @var string
18 * @since 1.9
19 */
20 protected $context = 'ngg';
21
22 /**
23 * Get all unoptimized media ids.
24 *
25 * @since 1.9
26 *
27 * @param int $optimization_level The optimization level.
28 * @return array A list of unoptimized media IDs.
29 */
30 public function get_unoptimized_media_ids( $optimization_level ) {
31 // @codeCoverageIgnoreStart — requires live NGG POPE classes and DB; not available in unit tests.
32 if ( ! imagify_ngg_has_pope_storage() || ! DB::get_instance()->can_operate() ) {
33 return [];
34 }
35 // @codeCoverageIgnoreEnd
36
37 global $wpdb;
38
39 $this->set_no_time_limit();
40
41 $storage = C_Gallery_Storage::get_instance();
42 $ngg_table = $wpdb->prefix . 'ngg_pictures';
43 $data = [];
44 $images = $wpdb->get_results(
45 $wpdb->prepare( // WPCS: unprepared SQL ok.
46 "
47 SELECT DISTINCT picture.pid as id, picture.filename, idata.optimization_level, idata.status, idata.data
48 FROM $ngg_table as picture
49 LEFT JOIN $wpdb->ngg_imagify_data as idata
50 ON picture.pid = idata.pid
51 WHERE idata.pid IS NULL
52 OR idata.optimization_level != %d
53 OR idata.status = 'error'
54 LIMIT %d",
55 $optimization_level,
56 imagify_get_unoptimized_attachment_limit()
57 ),
58 ARRAY_A
59 );
60
61 if ( ! $images ) {
62 return [];
63 }
64
65 foreach ( $images as $image ) {
66 $id = absint( $image['id'] );
67 $file_path = $storage->get_image_abspath( $id );
68
69 if ( ! $file_path || ! $this->filesystem->exists( $file_path ) ) {
70 continue;
71 }
72
73 $attachment_data = maybe_unserialize( $image['data'] );
74 $attachment_error = '';
75
76 if ( isset( $attachment_data['sizes']['full']['error'] ) ) {
77 $attachment_error = $attachment_data['sizes']['full']['error'];
78 }
79
80 $attachment_error = trim( $attachment_error );
81 $attachment_status = $image['status'];
82 $attachment_optimization_level = $image['optimization_level'];
83 $attachment_backup_path = get_imagify_ngg_attachment_backup_path( $file_path );
84
85 // Don't try to re-optimize if the optimization level is still the same.
86 if ( $optimization_level === $attachment_optimization_level && is_string( $attachment_error ) ) {
87 continue;
88 }
89
90 // Don't try to re-optimize if there is no backup file.
91 if ( 'success' === $attachment_status && $optimization_level !== $attachment_optimization_level && ! $this->filesystem->exists( $attachment_backup_path ) ) {
92 continue;
93 }
94
95 // Don't try to re-optimize images already compressed.
96 if ( 'already_optimized' === $attachment_status && $attachment_optimization_level >= $optimization_level ) {
97 continue;
98 }
99
100 // Don't try to re-optimize images with an empty error message.
101 if ( 'error' === $attachment_status && empty( $attachment_error ) ) {
102 continue;
103 }
104
105 $data[] = $id;
106 }
107
108 return $data;
109 }
110
111 /**
112 * Get all optimized media ids that can be restored.
113 *
114 * @since 2.3
115 *
116 * @return array A list of optimized media IDs with backup files available.
117 */
118 public function get_optimized_media_ids(): array {
119 return [];
120 }
121
122 /**
123 * Get ids of all optimized media without next-gen versions.
124 *
125 * @since 2.2
126 *
127 * @param string $format Format we are looking for. (webp|avif).
128 *
129 * @return array {
130 * @type array $ids A list of media IDs.
131 * @type array $errors {
132 * @type array $no_file_path A list of media IDs.
133 * @type array $no_backup A list of media IDs.
134 * }
135 * }
136 */
137 public function get_optimized_media_ids_without_format( $format ) {
138 // @codeCoverageIgnoreStart — requires live NGG POPE classes and DB; not available in unit tests.
139 if ( ! imagify_ngg_has_pope_storage() || ! DB::get_instance()->can_operate() ) {
140 return [
141 'ids' => [],
142 'errors' => [
143 'no_file_path' => [],
144 'no_backup' => [],
145 ],
146 ];
147 }
148 // @codeCoverageIgnoreEnd
149
150 global $wpdb;
151
152 $this->set_no_time_limit();
153
154 $storage = C_Gallery_Storage::get_instance();
155 $ngg_table = $wpdb->prefix . 'ngg_pictures';
156 $data_table = DB::get_instance()->get_table_name();
157 $suffix = constant( imagify_get_optimization_process_class_name( 'ngg' ) . '::WEBP_SUFFIX' );
158
159 if ( 'avif' === get_imagify_option( 'optimization_format' ) ) {
160 $suffix = constant( imagify_get_optimization_process_class_name( 'ngg' ) . '::AVIF_SUFFIX' );
161 }
162
163 $files = $wpdb->get_col(
164 $wpdb->prepare( // WPCS: unprepared SQL ok.
165 "
166 SELECT ngg.pid
167 FROM $ngg_table as ngg
168 INNER JOIN $data_table AS data
169 ON ( ngg.pid = data.pid )
170 WHERE
171 ( data.status = 'success' OR data.status = 'already_optimized' )
172 AND data.data NOT LIKE %s
173 AND data.data NOT LIKE %s
174 ORDER BY ngg.pid DESC",
175 '%' . $wpdb->esc_like( $suffix . '";a:4:{s:7:"success";b:1;' ) . '%',
176 /**
177 * Second predicate: skip images the API permanently refused to convert (the next-gen file
178 * would be heavier than the original, or the file is already compressed). Those are stored
179 * as `<size><suffix>";a:3:{s:15:"permanent_error";b:1;s:7:"success";b:0;s:5:"error";…`.
180 * Transient failures serialize as a 2-element array without the `permanent_error` key, so
181 * they keep being retried. Matching serialized data with LIKE is fragile: the key order
182 * written in Imagify\NGG\Optimization\Data\NGG must not change.
183 */
184 '%' . $wpdb->esc_like( $suffix . '";a:3:{s:15:"permanent_error";b:1;' ) . '%'
185 )
186 );
187
188 $wpdb->flush();
189 unset( $ngg_table, $data_table, $suffix );
190
191 $data = [
192 'ids' => [],
193 'errors' => [
194 'no_file_path' => [],
195 'no_backup' => [],
196 ],
197 ];
198
199 if ( ! $files ) {
200 return $data;
201 }
202
203 foreach ( $files as $file_id ) {
204 $file_id = absint( $file_id );
205 $file_path = $storage->get_image_abspath( $file_id );
206
207 if ( ! $file_path ) {
208 // Problem.
209 $data['errors']['no_file_path'][] = $file_id;
210 continue;
211 }
212
213 $backup_path = get_imagify_ngg_attachment_backup_path( $file_path );
214
215 if ( ! $this->filesystem->exists( $backup_path ) ) {
216 // No backup, no next-gen.
217 $data['errors']['no_backup'][] = $file_id;
218 continue;
219 }
220
221 $data['ids'][] = $file_id;
222 }
223
224 return $data;
225 }
226
227 /**
228 * Tell if there are optimized media without next-gen versions.
229 *
230 * @since 2.2
231 *
232 * @return int The number of media.
233 */
234 public function has_optimized_media_without_nextgen() {
235 // @codeCoverageIgnoreStart — requires live NGG DB table; not available in unit tests.
236 if ( ! DB::get_instance()->can_operate() ) {
237 return 0;
238 }
239 // @codeCoverageIgnoreEnd
240
241 global $wpdb;
242
243 $ngg_table = $wpdb->prefix . 'ngg_pictures';
244 $data_table = DB::get_instance()->get_table_name();
245 $suffix = constant( imagify_get_optimization_process_class_name( 'ngg' ) . '::WEBP_SUFFIX' );
246
247 if ( 'avif' === get_imagify_option( 'optimization_format' ) ) {
248 $suffix = constant( imagify_get_optimization_process_class_name( 'ngg' ) . '::AVIF_SUFFIX' );
249 }
250
251 return (int) $wpdb->get_var(
252 $wpdb->prepare( // WPCS: unprepared SQL ok.
253 "
254 SELECT COUNT(ngg.pid)
255 FROM $ngg_table as ngg
256 INNER JOIN $data_table AS data
257 ON ( ngg.pid = data.pid )
258 WHERE
259 ( data.status = 'success' OR data.status = 'already_optimized' )
260 AND data.data NOT LIKE %s",
261 '%' . $wpdb->esc_like( $suffix . '";a:4:{s:7:"success";b:1;' ) . '%'
262 )
263 );
264 }
265
266 /**
267 * Get the context data.
268 *
269 * @since 1.9
270 *
271 * @return array {
272 * The formated data.
273 *
274 * @type string $count-optimized Number of media optimized.
275 * @type string $count-errors Number of media having an optimization error, with a link to the page listing the optimization errors.
276 * @type string $optimized-size Optimized filesize.
277 * @type string $original-size Original filesize.
278 * }
279 */
280 public function get_context_data() {
281 $total_saving_data = imagify_count_saving_data();
282 $data = [
283 'count-optimized' => imagify_ngg_count_optimized_attachments(),
284 'count-errors' => imagify_ngg_count_error_attachments(),
285 'optimized-size' => $total_saving_data['optimized_size'],
286 'original-size' => $total_saving_data['original_size'],
287 'errors_url' => get_imagify_admin_url( 'folder-errors', $this->context ),
288 ];
289
290 return $this->format_context_data( $data );
291 }
292 }
293