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 / classes / Optimization / Data / CustomFolders.php

CustomFolders.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF trunk, at classes/Optimization/Data/CustomFolders.php

338 lines 9.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Imagify\Optimization\Data;
3
4 use Imagify\Traits\MediaRowTrait;
5
6 /**
7 * Optimization data class for the custom folders.
8 * This class constructor accepts:
9 * - A media ID (int).
10 * - An array of data coming from the files DB table /!\
11 * - An object of data coming from the files DB table /!\
12 * - A \Imagify\Media\MediaInterface object.
13 *
14 * @since 1.9
15 * @see Imagify\Media\CustomFolders
16 * @author Grégory Viguier
17 */
18 class CustomFolders extends AbstractData {
19 use MediaRowTrait;
20
21 /**
22 * The attachment SQL DB class.
23 *
24 * @var string
25 * @since 1.9
26 * @access protected
27 * @author Grégory Viguier
28 */
29 protected $db_class_name = 'Imagify_Files_DB';
30
31 /**
32 * The constructor.
33 *
34 * @since 1.9
35 * @access public
36 * @author Grégory Viguier
37 *
38 * @param mixed $id An ID, or whatever type the "Media" class constructor accepts.
39 */
40 public function __construct( $id ) {
41 parent::__construct( $id );
42
43 if ( ! $this->is_valid() ) {
44 return;
45 }
46
47 $media = $this->get_media();
48
49 if ( ! $media ) {
50 return;
51 }
52
53 // This is required by MediaRowTrait.
54 $this->id = $media->get_id();
55
56 // In this context, the media data and the optimization data are stored in the same DB table, so, no need to request twice the DB.
57 $this->row = $media->get_row(); // @phpstan-ignore-line
58 }
59
60 /**
61 * Get the whole media optimization data.
62 *
63 * @since 1.9
64 * @access public
65 * @author Grégory Viguier
66 *
67 * @return array The data. See parent method for details.
68 */
69 public function get_optimization_data() {
70 if ( ! $this->is_valid() ) {
71 return $this->default_optimization_data;
72 }
73
74 $row = array_merge( $this->get_row_db_instance()->get_column_defaults(), $this->get_row() );
75 $data = $this->default_optimization_data;
76
77 $data['status'] = $row['status'];
78 $data['level'] = $row['optimization_level'];
79 $data['level'] = is_numeric( $data['level'] ) ? (int) $data['level'] : false;
80
81 if ( 'success' === $row['status'] ) {
82 /**
83 * Success.
84 */
85 $data['sizes']['full'] = [
86 'success' => true,
87 'original_size' => $row['original_size'],
88 'optimized_size' => $row['optimized_size'],
89 'percent' => $row['percent'],
90 ];
91 } elseif ( ! empty( $row['status'] ) ) {
92 /**
93 * Error.
94 */
95 $data['sizes']['full'] = [
96 'success' => false,
97 'error' => $row['error'],
98 ];
99 }
100
101 if ( ! empty( $row['data']['sizes'] ) && is_array( $row['data']['sizes'] ) ) {
102 unset( $row['data']['sizes']['full'] );
103 $data['sizes'] = array_merge( $data['sizes'], $row['data']['sizes'] );
104 $data['sizes'] = array_filter( $data['sizes'], 'is_array' );
105 }
106
107 if ( empty( $data['sizes'] ) ) {
108 return $data;
109 }
110
111 foreach ( $data['sizes'] as $size_data ) {
112 // Cast.
113 if ( isset( $size_data['original_size'] ) ) {
114 $size_data['original_size'] = (int) $size_data['original_size'];
115 }
116 if ( isset( $size_data['optimized_size'] ) ) {
117 $size_data['optimized_size'] = (int) $size_data['optimized_size'];
118 }
119 if ( isset( $size_data['percent'] ) ) {
120 $size_data['percent'] = round( $size_data['percent'], 2 );
121 }
122 // Stats.
123 if ( ! empty( $size_data['original_size'] ) && ! empty( $size_data['optimized_size'] ) ) {
124 $data['stats']['original_size'] += $size_data['original_size'];
125 $data['stats']['optimized_size'] += $size_data['optimized_size'];
126 }
127 }
128
129 if ( $data['stats']['original_size'] && $data['stats']['optimized_size'] ) {
130 $data['stats']['percent'] = $data['stats']['original_size'] - $data['stats']['optimized_size'];
131 $data['stats']['percent'] = round( $data['stats']['percent'] / $data['stats']['original_size'] * 100, 2 );
132 }
133
134 return $data;
135 }
136
137 /**
138 * Update the optimization data, level, and status for a size.
139 *
140 * @since 1.9
141 * @access public
142 * @author Grégory Viguier
143 *
144 * @param string $size The size name.
145 * @param array $data The optimization data. See parent method for details.
146 */
147 public function update_size_optimization_data( $size, array $data ) {
148 if ( ! $this->is_valid() ) {
149 return;
150 }
151
152 $old_data = array_merge( $this->get_reset_data(), $this->get_row() );
153
154 if ( 'full' === $size ) {
155 /**
156 * Original file.
157 */
158 $old_data['optimization_level'] = $data['level'];
159 $old_data['status'] = $data['status'];
160 $old_data['modified'] = 0;
161
162 $file_path = $this->get_media()->get_fullsize_path();
163
164 if ( $file_path ) {
165 $old_data['hash'] = md5_file( $file_path );
166 }
167
168 if ( key_exists( 'message', $data ) ) {
169 $old_data['message'] = $data['message'];
170 }
171
172 if ( ! $data['success'] ) {
173 /**
174 * Error.
175 */
176 $old_data['error'] = $data['error'];
177 } else {
178 /**
179 * Success.
180 */
181 $old_data['original_size'] = $data['original_size'];
182 $old_data['optimized_size'] = $data['optimized_size'];
183 $old_data['percent'] = $data['original_size'] - $data['optimized_size'];
184 $old_data['percent'] = round( ( $old_data['percent'] / $data['original_size'] ) * 100, 2 );
185 }
186 } else {
187 /**
188 * WebP version or any other size.
189 */
190 $old_data['data'] = ! empty( $old_data['data'] ) && is_array( $old_data['data'] ) ? $old_data['data'] : [];
191 $old_data['data']['sizes'] = ! empty( $old_data['data']['sizes'] ) && is_array( $old_data['data']['sizes'] ) ? $old_data['data']['sizes'] : [];
192
193 if ( ! $data['success'] ) {
194 /**
195 * Error.
196 */
197 $size_data = [
198 'success' => false,
199 'error' => $data['error'],
200 ];
201
202 if ( ! empty( $data['permanent_error'] ) ) {
203 /**
204 * `permanent_error` is written as the FIRST key on purpose: the bulk queries match the
205 * serialized data with a LIKE on the `<size>";a:3:{s:15:"permanent_error";b:1;` prefix,
206 * which only stays deterministic if the key order does.
207 */
208 $size_data = array_merge( [ 'permanent_error' => true ], $size_data );
209 }
210
211 $old_data['data']['sizes'][ $size ] = $size_data;
212 } else {
213 /**
214 * Success.
215 */
216 $old_data['data']['sizes'][ $size ] = [
217 'success' => true,
218 'original_size' => $data['original_size'],
219 'optimized_size' => $data['optimized_size'],
220 'percent' => round( ( ( $data['original_size'] - $data['optimized_size'] ) / $data['original_size'] ) * 100, 2 ),
221 ];
222 }
223 }
224
225 if ( isset( $old_data['data']['sizes'] ) && ( ! $old_data['data']['sizes'] || ! is_array( $old_data['data']['sizes'] ) ) ) {
226 unset( $old_data['data']['sizes'] );
227 }
228
229 $this->update_row( $old_data );
230 }
231
232 /**
233 * Delete the media optimization data, level, and status.
234 *
235 * @since 1.9
236 * @access public
237 * @author Grégory Viguier
238 */
239 public function delete_optimization_data() {
240 if ( ! $this->is_valid() ) {
241 return;
242 }
243
244 $this->update_row( $this->get_reset_data() );
245 }
246
247 /**
248 * Delete the optimization data for the given sizes.
249 * If all sizes are removed, all optimization data is deleted.
250 * Status and level are not modified nor removed if the "full" size is removed. This leaves the media in a Schrödinger state.
251 *
252 * @since 1.9.8
253 * @access public
254 * @author Grégory Viguier
255 *
256 * @param array $sizes A list of sizes to remove.
257 */
258 public function delete_sizes_optimization_data( array $sizes ) {
259 if ( ! $sizes || ! $this->is_valid() ) {
260 return;
261 }
262
263 $data = array_merge( $this->get_reset_data(), $this->get_row() );
264
265 $data['data']['sizes'] = ! empty( $data['data']['sizes'] ) && is_array( $data['data']['sizes'] ) ? $data['data']['sizes'] : [];
266
267 if ( ! $data['data']['sizes'] ) {
268 return;
269 }
270
271 $remaining_sizes_data = array_diff_key( $data['data']['sizes'], array_flip( $sizes ) );
272
273 if ( ! $remaining_sizes_data ) {
274 // All sizes have been removed: delete everything.
275 $this->delete_optimization_data();
276 return;
277 }
278
279 if ( count( $remaining_sizes_data ) === count( $data['data']['sizes'] ) ) {
280 // Nothing has been removed.
281 return;
282 }
283
284 $data['data']['sizes'] = $remaining_sizes_data;
285
286 $this->update_row( $data );
287 }
288
289 /**
290 * Get default values used to reset optimization data.
291 *
292 * @since 1.9
293 * @access protected
294 * @author Grégory Viguier
295 *
296 * @return array {
297 * The default values related to the optimization.
298 *
299 * @type string $hash The file hash.
300 * @type int $modified 0 to tell that the file has not been modified
301 * @type int $optimized_size File size after optimization.
302 * @type int $percent Saving optimized/original in percent.
303 * @type int $optimization_level The optimization level.
304 * @type string $status The status: success, already_optimized, error.
305 * @type string $error An error message.
306 * }
307 */
308 protected function get_reset_data() {
309 static $column_defaults;
310
311 if ( ! isset( $column_defaults ) ) {
312 $column_defaults = $this->get_row_db_instance()->get_column_defaults();
313
314 // All DB columns that have `null` as default value, are Imagify data.
315 foreach ( $column_defaults as $column_name => $value ) {
316 if ( 'hash' === $column_name || 'modified' === $column_name || 'data' === $column_name ) {
317 continue;
318 }
319
320 if ( isset( $value ) ) {
321 unset( $column_defaults[ $column_name ] );
322 }
323 }
324 }
325
326 $imagify_columns = $column_defaults;
327
328 // Also set the new file hash.
329 $file_path = $this->get_media()->get_fullsize_path();
330
331 if ( $file_path ) {
332 $imagify_columns['hash'] = md5_file( $file_path );
333 }
334
335 return $imagify_columns;
336 }
337 }
338