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 / Abilities / GetMediaStatus.php

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

283 lines 8.1 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\Abilities;
5
6 use Imagify\Optimization\Data\WP;
7 use Imagify\Optimization\Process\ProcessInterface;
8
9 /**
10 * MCP ability: imagify/get-media-status
11 *
12 * Returns the optimization status and key metrics for a given WordPress
13 * media library attachment.
14 *
15 * @since 2.3.0
16 */
17 class GetMediaStatus extends AbstractAbility {
18
19 /**
20 * Returns the ability slug.
21 *
22 * @return string
23 */
24 public function get_id(): string {
25 return 'imagify/get-media-status';
26 }
27
28 /**
29 * Returns the ability label.
30 *
31 * @return string
32 */
33 public function get_name(): string {
34 return 'Get Media Status';
35 }
36
37 /**
38 * Register the ability with the WP Abilities API.
39 *
40 * No-ops silently when `wp_register_ability` is unavailable (WP < 6.9).
41 *
42 * @since 2.3.0
43 *
44 * @return void
45 */
46 public function register(): void {
47 if ( ! function_exists( 'wp_register_ability' ) ) {
48 return;
49 }
50
51 wp_register_ability(
52 'imagify/get-media-status',
53 [
54 'label' => __( 'Get Media Status', 'imagify' ),
55 'description' => __( 'Retrieve the optimization status and metrics for a WordPress media library attachment.', 'imagify' ),
56 'category' => 'imagify',
57 'input_schema' => [
58 'type' => 'object',
59 'properties' => [
60 'media_id' => [
61 'type' => 'integer',
62 'description' => __( 'The WordPress attachment ID.', 'imagify' ),
63 ],
64 ],
65 'required' => [ 'media_id' ],
66 ],
67 'output_schema' => [
68 'type' => 'object',
69 'properties' => [
70 'status' => [
71 'type' => 'string',
72 'description' => __( 'Optimization status: "success", "error", or "unoptimized".', 'imagify' ),
73 'enum' => [ 'success', 'error', 'unoptimized' ],
74 ],
75 'optimization_level' => [
76 'type' => [ 'integer', 'null' ],
77 'description' => __( '0 = lossless, 1 = aggressive, 2 = ultra. Null when not optimized.', 'imagify' ),
78 ],
79 'original_size' => [
80 'type' => 'integer',
81 'description' => __( 'File size in bytes before optimization.', 'imagify' ),
82 ],
83 'optimized_size' => [
84 'type' => 'integer',
85 'description' => __( 'File size in bytes after optimization.', 'imagify' ),
86 ],
87 'webp_available' => [
88 'type' => 'boolean',
89 'description' => __( 'True when a WebP version of the full-size image has been generated.', 'imagify' ),
90 ],
91 'avif_available' => [
92 'type' => 'boolean',
93 'description' => __( 'True when an AVIF version of the full-size image has been generated.', 'imagify' ),
94 ],
95 'error_message' => [
96 'type' => [ 'string', 'null' ],
97 'description' => __( 'Human-readable error message when status is "error". Null otherwise.', 'imagify' ),
98 ],
99 ],
100 ],
101 'execute_callback' => [ $this, 'execute' ],
102 'permission_callback' => [ $this, 'check_permissions' ],
103 'meta' => [
104 'show_in_rest' => true,
105 'mcp' => [ 'public' => true ],
106 'annotations' => [
107 'readonly' => true,
108 'destructive' => false,
109 'idempotent' => true,
110 ],
111 ],
112 ]
113 );
114 }
115
116 /**
117 * Routes through Imagify's capability abstraction so the `imagify_capacity`
118 * filter and multisite network-admin logic are honoured.
119 *
120 * @since 2.3.0
121 *
122 * @return bool True when the current user has the Imagify `manage` capability.
123 */
124 protected function has_permission(): bool {
125 return imagify_get_context( 'wp' )->current_user_can( 'manage' );
126 }
127
128 /**
129 * Execute the ability and return the media optimization status.
130 *
131 * @since 2.3.0
132 *
133 * @param array $args Input arguments. Expects `media_id` (int) — the WordPress attachment ID.
134 * @return array Optimization status response keyed by status, optimization_level, original_size,
135 * optimized_size, webp_available, avif_available, and error_message.
136 */
137 public function execute( array $args = [] ): array {
138 $start_time = microtime( true );
139 $result = $this->do_execute( $args );
140
141 $this->fire_executed( $result, $start_time, $args );
142
143 return $result;
144 }
145
146 /**
147 * Internal execution logic for the ability.
148 *
149 * @param array $args Input arguments.
150 * @return array
151 */
152 private function do_execute( array $args ): array {
153 $media_id = isset( $args['media_id'] ) ? (int) $args['media_id'] : 0;
154
155 if ( $media_id <= 0 ) {
156 return [
157 'status' => 'error',
158 'error_message' => 'Invalid or missing media_id',
159 'optimization_level' => null,
160 'original_size' => 0,
161 'optimized_size' => 0,
162 'webp_available' => false,
163 'avif_available' => false,
164 ];
165 }
166
167 // Verify the attachment exists.
168 if ( ! get_post( $media_id ) ) {
169 return [
170 'status' => 'error',
171 'error_message' => 'Media not found.',
172 'optimization_level' => null,
173 'original_size' => 0,
174 'optimized_size' => 0,
175 'webp_available' => false,
176 'avif_available' => false,
177 ];
178 }
179
180 $wp_data = $this->create_wp_data( $media_id );
181 $opt_data = $wp_data->get_optimization_data();
182
183 $internal_status = isset( $opt_data['status'] ) ? (string) $opt_data['status'] : '';
184 $status = $this->map_status( $internal_status );
185
186 $level = isset( $opt_data['level'] ) && false !== $opt_data['level']
187 ? (int) $opt_data['level']
188 : null;
189
190 // get_original_size(false) falls back to the filesystem when no optimization data
191 // exists yet (unoptimized media), which avoids the 0-byte read from empty meta.
192 $original_size = $wp_data->get_original_size( false );
193 $optimized_size = isset( $opt_data['stats']['optimized_size'] ) ? (int) $opt_data['stats']['optimized_size'] : 0;
194
195 $webp_available = false;
196 $avif_available = false;
197
198 if ( ! empty( $opt_data['sizes'] ) && is_array( $opt_data['sizes'] ) ) {
199 foreach ( array_keys( $opt_data['sizes'] ) as $size_key ) {
200 if ( ! $webp_available && $this->size_key_ends_with( (string) $size_key, ProcessInterface::WEBP_SUFFIX ) ) {
201 $webp_available = true;
202 }
203 if ( ! $avif_available && $this->size_key_ends_with( (string) $size_key, ProcessInterface::AVIF_SUFFIX ) ) {
204 $avif_available = true;
205 }
206 if ( $webp_available && $avif_available ) {
207 break;
208 }
209 }
210 }
211
212 $error_message = null;
213 if ( 'error' === $status ) {
214 $error_message = isset( $opt_data['message'] ) && '' !== $opt_data['message']
215 ? (string) $opt_data['message']
216 : null;
217 }
218
219 return [
220 'status' => $status,
221 'optimization_level' => $level,
222 'original_size' => $original_size,
223 'optimized_size' => $optimized_size,
224 'webp_available' => $webp_available,
225 'avif_available' => $avif_available,
226 'error_message' => $error_message,
227 ];
228 }
229
230 /**
231 * Instantiate the WP optimization-data object for a given media.
232 *
233 * Extracted to a protected method so tests can override it without hitting the database.
234 *
235 * @since 2.3.0
236 *
237 * @param int $media_id WordPress attachment ID.
238 * @return WP
239 */
240 protected function create_wp_data( int $media_id ): WP {
241 return new WP( $media_id );
242 }
243
244 /**
245 * Map the internal Imagify optimization status to the public API status.
246 *
247 * @since 2.3.0
248 *
249 * @param string $internal_status The internal status string from `_imagify_status` meta.
250 * @return string 'success', 'error', or 'unoptimized'.
251 */
252 private function map_status( string $internal_status ): string {
253 if ( 'success' === $internal_status || 'already_optimized' === $internal_status ) {
254 return 'success';
255 }
256
257 if ( 'error' === $internal_status ) {
258 return 'error';
259 }
260
261 return 'unoptimized';
262 }
263
264 /**
265 * Check whether a size key ends with a given suffix.
266 *
267 * @since 2.3.0
268 *
269 * @param string $size_key The thumbnail size key to inspect.
270 * @param string $suffix The suffix to check for.
271 * @return bool
272 */
273 private function size_key_ends_with( string $size_key, string $suffix ): bool {
274 $suffix_length = strlen( $suffix );
275
276 if ( 0 === $suffix_length ) {
277 return true;
278 }
279
280 return substr( $size_key, -$suffix_length ) === $suffix;
281 }
282 }
283