PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 2.3.0
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v2.3.0
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 / Tracking / Tracking.php

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

245 lines 7.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 declare(strict_types=1);
3
4 namespace Imagify\Tracking;
5
6 use Imagify\Optimization\Process\ProcessInterface;
7
8 /**
9 * Concrete tracking class for Imagify media optimization events.
10 *
11 * @since 2.3.0
12 */
13 class Tracking extends BaseTracking {
14
15 /**
16 * Track a "Media Restored" event in Mixpanel.
17 *
18 * Fires on `imagify_after_restore_media`. Guards on opt-in and on a
19 * successful restore — no event is emitted when `$response` is a WP_Error.
20 *
21 * NOTE: `$files` is intentionally unused; it is kept in the signature to
22 * match the four-argument hook and allow WordPress to pass it cleanly.
23 *
24 * @param ProcessInterface $process The optimization process instance.
25 * @param bool|\WP_Error $response True on success, WP_Error on failure.
26 * @param array $files The list of files before restoring (unused).
27 * @param array $data The optimization data captured before deletion.
28 *
29 * @return void
30 */
31 public function track_media_restored( ProcessInterface $process, $response, array $files, array $data ): void {
32 if ( ! $this->can_track() ) {
33 return;
34 }
35
36 if ( is_wp_error( $response ) ) {
37 return;
38 }
39
40 $media = $process->get_media();
41 $media_context = $media ? $media->get_context() : '';
42
43 $raw_level = $data['level'] ?? null;
44 $optimization_level = is_int( $raw_level ) ? $raw_level : null;
45
46 $next_gen_format = $this->resolve_next_gen_format_from_data( $data );
47
48 $event_data = array_merge(
49 $this->get_default_event_properties(),
50 [
51 'media_context' => $media_context,
52 'optimization_level' => $optimization_level,
53 'next_gen_format' => $next_gen_format,
54 ]
55 );
56
57 $this->mixpanel->track_direct( 'Media Restored', $event_data );
58 }
59
60 /**
61 * Track a "Media Optimized" event in Mixpanel.
62 *
63 * @param ProcessInterface $process The optimization process instance.
64 * @param array $item The optimization item data.
65 *
66 * @return void
67 */
68 public function track_media_optimized( ProcessInterface $process, array $item ): void {
69 if ( ! $this->can_track() ) {
70 return;
71 }
72
73 if ( ! in_array( 'full', $item['sizes_done'] ?? [], true ) ) {
74 return;
75 }
76
77 $data = $process->get_data();
78 $full_data = $data ? $data->get_size_data( 'full' ) : null;
79
80 if ( empty( $full_data['success'] ) ) {
81 return;
82 }
83
84 $original_size = (int) ( $full_data['original_size'] ?? 0 );
85 $optimized_size = (int) ( $full_data['optimized_size'] ?? 0 );
86 $savings_percent = $original_size > 0
87 ? round( ( ( $original_size - $optimized_size ) / $original_size ) * 100, 2 )
88 : 0;
89
90 $media = $process->get_media();
91 $media_type = $media ? $media->get_mime_type() : '';
92
93 $optimization_level = $data ? $data->get_optimization_level() : null;
94
95 $next_gen_format = $this->resolve_next_gen_format( $process );
96 $trigger = $this->resolve_trigger( $item );
97
98 $event_data = array_merge(
99 $this->get_default_event_properties(),
100 [
101 'optimization_level' => $optimization_level,
102 'media_type' => $media_type,
103 'original_size' => $original_size,
104 'optimized_size' => $optimized_size,
105 'savings_percent' => $savings_percent,
106 'next_gen_format' => $next_gen_format,
107 'trigger' => $trigger,
108 ]
109 );
110
111 $this->mixpanel->track_direct( 'Media Optimized', $event_data );
112 }
113
114 /**
115 * Track a "Settings Saved" event in Mixpanel.
116 *
117 * @param array $old_value The previous option value. Intentionally unused — kept for
118 * hook-signature symmetry. On multisite the first hook arg is the
119 * option name (string), not the old value, so this parameter is
120 * unreliable and must not be used for business logic.
121 * @param array $new_value The new option value.
122 *
123 * @return void
124 */
125 public function track_settings_saved( array $old_value, array $new_value ): void {
126 if ( ! $this->can_track() ) {
127 return;
128 }
129
130 $event_data = array_merge(
131 $this->get_default_event_properties(),
132 [
133 'optimization_format' => isset( $new_value['optimization_format'] ) ? (string) $new_value['optimization_format'] : null,
134 'lossless' => ! empty( $new_value['lossless'] ),
135 'auto_optimize_on_upload' => ! empty( $new_value['auto_optimize'] ),
136 'backup_original' => ! empty( $new_value['backup'] ),
137 'resize_larger_images' => ! empty( $new_value['resize_larger'] ),
138 'resize_larger_width' => isset( $new_value['resize_larger_w'] ) ? (int) $new_value['resize_larger_w'] : null,
139 'display_nextgen' => ! empty( $new_value['display_nextgen'] ),
140 'display_nextgen_method' => isset( $new_value['display_nextgen_method'] ) ? (string) $new_value['display_nextgen_method'] : null,
141 'cdn_enabled' => ! empty( $new_value['cdn_url'] ),
142 ]
143 );
144
145 $this->mixpanel->track_direct( 'Settings Saved', $event_data );
146 }
147
148 /**
149 * Track an "Internal State Reset" event in Mixpanel.
150 *
151 * @return void
152 */
153 public function track_internal_state_reset(): void {
154 if ( ! $this->can_track() ) {
155 return;
156 }
157
158 $event_data = array_merge(
159 $this->get_default_event_properties(),
160 [
161 'is_multisite' => is_multisite(),
162 ]
163 );
164
165 $this->mixpanel->track_direct( 'Internal State Reset', $event_data );
166 }
167
168 /**
169 * Resolve the next-gen format generated for the full size.
170 *
171 * @param ProcessInterface $process The optimization process instance.
172 *
173 * @return string|null 'avif', 'webp', or null.
174 */
175 protected function resolve_next_gen_format( ProcessInterface $process ): ?string {
176 $data = $process->get_data();
177
178 if ( ! $data ) {
179 return null;
180 }
181
182 $avif_size = 'full' . ProcessInterface::AVIF_SUFFIX;
183 $avif_data = $data->get_size_data( $avif_size );
184
185 if ( ! empty( $avif_data['success'] ) ) {
186 return 'avif';
187 }
188
189 $webp_size = 'full' . ProcessInterface::WEBP_SUFFIX;
190 $webp_data = $data->get_size_data( $webp_size );
191
192 if ( ! empty( $webp_data['success'] ) ) {
193 return 'webp';
194 }
195
196 return null;
197 }
198
199 /**
200 * Resolve the next-gen format from the raw optimization data array.
201 *
202 * Used by `track_media_restored()` because `DataInterface::get_optimization_data()`
203 * is deleted before the `imagify_after_restore_media` hook fires; the raw
204 * array captured beforehand is passed as the `$data` hook argument instead.
205 *
206 * @param array $data The optimization data returned by DataInterface::get_optimization_data().
207 *
208 * @return string|null 'avif', 'webp', or null.
209 */
210 private function resolve_next_gen_format_from_data( array $data ): ?string {
211 $sizes = $data['sizes'] ?? [];
212
213 $avif_size = 'full' . ProcessInterface::AVIF_SUFFIX;
214 if ( ! empty( $sizes[ $avif_size ]['success'] ) ) {
215 return 'avif';
216 }
217
218 $webp_size = 'full' . ProcessInterface::WEBP_SUFFIX;
219 if ( ! empty( $sizes[ $webp_size ]['success'] ) ) {
220 return 'webp';
221 }
222
223 return null;
224 }
225
226 /**
227 * Resolve the trigger for the optimization event.
228 *
229 * @param array $item The optimization item data.
230 *
231 * @return string 'auto', 'bulk', or 'manual'.
232 */
233 protected function resolve_trigger( array $item ): string {
234 if ( ! empty( $item['data']['is_new_upload'] ) ) {
235 return 'auto';
236 }
237
238 if ( get_transient( 'imagify_wp_optimize_running' ) || get_transient( 'imagify_custom-folders_optimize_running' ) ) {
239 return 'bulk';
240 }
241
242 return 'manual';
243 }
244 }
245