PluginProbe
Image Optimization – Compress Images and Convert to WebP or AVIF / 1.5.0
Image Optimization – Compress Images and Convert to WebP or AVIF v1.5.0
1.7.6 1.7.5 1.7.4 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.2.0 1.2.1 1.3.0 1.4.0 1.4.1 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 All 32 releases
image-optimization / classes / client / client.php

client.php in Image Optimization – Compress Images and Convert to WebP or AVIF 1.5.0, at classes/client/client.php

280 lines 8.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace ImageOptimization\Classes\Client;
4
5 use ImageOptimization\Classes\Exceptions\Client_Exception;
6 use ImageOptimization\Classes\Image\Image;
7 use ImageOptimization\Modules\Oauth\{
8 Classes\Data,
9 Components\Connect
10 };
11 use ImageOptimization\Modules\Stats\Classes\Optimization_Stats;
12 use WP_Error;
13
14 use ImageOptimization\Plugin;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit; // Exit if accessed directly.
18 }
19
20 /**
21 * Class Client
22 */
23 class Client {
24 const BASE_URL = 'https://my.elementor.com/api/v2/image-optimizer/';
25 const STATUS_CHECK = 'status/check';
26
27 private bool $refreshed = false;
28
29 public static ?Client $instance = null;
30
31 /**
32 * get_instance
33 * @return Client|null
34 */
35 public static function get_instance(): ?Client {
36 if ( ! self::$instance ) {
37 self::$instance = new self();
38 }
39 return self::$instance;
40 }
41
42 public static function get_site_info(): array {
43 return [
44 // Which API version is used.
45 'app_version' => IMAGE_OPTIMIZATION_VERSION,
46 // Which language to return.
47 'site_lang' => get_bloginfo( 'language' ),
48 // site to connect
49 'site_url' => trailingslashit( home_url() ),
50 // current user
51 'local_id' => get_current_user_id(),
52 // Media library stats
53 'media_data' => base64_encode( wp_json_encode( self::get_request_stats() ) ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
54 ];
55 }
56
57 private static function get_request_stats(): array {
58 $optimization_stats = Optimization_Stats::get_image_stats();
59 $image_sizes = [];
60
61 foreach ( wp_get_registered_image_subsizes() as $image_size_key => $image_size_data ) {
62 $image_sizes[] = [
63 'label' => $image_size_key,
64 'size' => "{$image_size_data['width']}x{$image_size_data['height']}",
65 ];
66 }
67
68 return [
69 'not_optimized_images' => $optimization_stats['total_image_count'] - $optimization_stats['optimized_image_count'],
70 'optimized_images' => $optimization_stats['optimized_image_count'],
71 'images_sizes' => $image_sizes,
72 ];
73 }
74
75 public function make_request( $method, $endpoint, $body = [], array $headers = [], $file = false, $file_name = '' ) {
76 $headers = array_replace_recursive([
77 'x-elementor-image-optimizer' => IMAGE_OPTIMIZATION_VERSION,
78 ], $headers);
79
80 $headers = array_replace_recursive(
81 $headers,
82 $this->is_connected() ? $this->generate_authentication_headers( $endpoint ) : []
83 );
84
85 $body = array_replace_recursive( $body, $this->get_site_info() );
86
87 try {
88 if ( $file ) {
89 $boundary = wp_generate_password( 24, false );
90 $body = $this->get_upload_request_body( $body, $file, $boundary, $file_name );
91 // add content type header
92 $headers['Content-Type'] = 'multipart/form-data; boundary=' . $boundary;
93 }
94 } catch ( Client_Exception $ce ) {
95 return new WP_Error( 500, $ce->getMessage() );
96 }
97
98 $response = $this->request(
99 $method,
100 $endpoint,
101 [
102 'timeout' => 100,
103 'headers' => $headers,
104 'body' => $body,
105 ]
106 );
107
108 return ( new Client_Response( $response ) )->handle();
109 }
110
111 private static function get_remote_url( $endpoint ): string {
112 return self::BASE_URL . $endpoint;
113 }
114
115 protected function is_connected(): bool {
116 return Plugin::instance()->modules_manager->get_modules( 'connect-manager' )->connect_instance->is_connected();
117 }
118
119 protected function generate_authentication_headers( $endpoint ): array {
120
121 $connect_instance = Plugin::instance()->modules_manager->get_modules( 'connect-manager' )->connect_instance;
122
123 if ( ! $connect_instance->get_is_connect_on_fly() ) {
124 $headers = [
125 'data' => base64_encode( // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
126 wp_json_encode( [ 'app' => 'library' ] )
127 ),
128 'access_token' => $connect_instance->get_access_token() ?? '',
129 'client_id' => $connect_instance->get_client_id() ?? '',
130 ];
131
132 if ( $connect_instance->is_activated() ) {
133 $headers['key'] = $connect_instance->get_activation_state() ?? '';
134 }
135 } else {
136 $headers = $this->add_bearer_token([
137 'x-elementor-apps-connect' => true,
138 ]);
139 }
140
141 $headers['endpoint'] = $endpoint;
142 $headers['x-elementor-apps'] = 'image-optimizer';
143
144 return $headers;
145 }
146
147 protected function request( $method, $endpoint, $args = [] ) {
148 $args['method'] = $method;
149
150 $response = wp_remote_request(
151 self::get_remote_url( $endpoint ),
152 $args
153 );
154
155 if ( is_wp_error( $response ) ) {
156 $message = $response->get_error_message();
157
158 return new WP_Error(
159 $response->get_error_code(),
160 is_array( $message ) ? join( ', ', $message ) : $message
161 );
162 }
163
164 $body = wp_remote_retrieve_body( $response );
165 $response_code = (int) wp_remote_retrieve_response_code( $response );
166
167 if ( ! $response_code ) {
168 return new WP_Error( 500, 'No Response' );
169 }
170
171 // Server sent a success message without content.
172 if ( 'null' === $body ) {
173 $body = true;
174 }
175
176 $body = json_decode( $body );
177
178 if ( false === $body ) {
179 return new WP_Error( 422, 'Wrong Server Response' );
180 }
181
182 if ( Plugin::instance()->modules_manager->get_modules( 'connect-manager' )->connect_instance->get_is_connect_on_fly() ) {
183 // If the token is invalid, refresh it and try again once only.
184 if ( ! $this->refreshed && ! empty( $body->message ) && ( false !== strpos( $body->message, 'Invalid Token' ) ) ) {
185 Plugin::instance()->modules_manager->get_modules( 'connect-manager' )->connect_instance->refresh_token();
186 $this->refreshed = true;
187 $args['headers'] = $this->add_bearer_token( $args['headers'] );
188 return $this->request( $method, $endpoint, $args );
189 }
190 }
191
192 if ( 200 !== $response_code ) {
193 // In case $as_array = true.
194 $message = $body->message ?? wp_remote_retrieve_response_message( $response );
195 $message = is_array( $message ) ? join( ', ', $message ) : $message;
196 $code = isset( $body->code ) ? (int) $body->code : $response_code;
197
198 return new WP_Error( $code, $message );
199 }
200
201 return $body;
202 }
203
204 public function add_bearer_token( $headers ) {
205 if ( $this->is_connected() ) {
206 $headers['Authorization'] = 'Bearer ' . Plugin::instance()->modules_manager->get_modules( 'connect-manager' )->connect_instance->get_access_token();
207 }
208 return $headers;
209 }
210
211 /**
212 * get_upload_request_body
213 *
214 * @param array $body
215 * @param $file
216 * @param string $boundary
217 * @param string $file_name
218 *
219 * @return string
220 * @throws Client_Exception
221 */
222 private function get_upload_request_body( array $body, $file, string $boundary, string $file_name = '' ): string {
223 $payload = '';
224 // add all body fields as standard POST fields:
225 foreach ( $body as $name => $value ) {
226 $payload .= '--' . $boundary;
227 $payload .= "\r\n";
228 $payload .= 'Content-Disposition: form-data; name="' . esc_attr( $name ) . '"' . "\r\n\r\n";
229 $payload .= $value;
230 $payload .= "\r\n";
231 }
232
233 if ( is_array( $file ) ) {
234 foreach ( $file as $key => $file_data ) {
235 $payload .= $this->get_file_payload( $file_data['name'], $file_data['type'], $file_data['path'], $boundary );
236 }
237 } else {
238 $image_mime = image_type_to_mime_type( exif_imagetype( $file ) );
239
240 if ( ! in_array( $image_mime, Image::get_supported_mime_types(), true ) ) {
241 throw new Client_Exception( "Unsupported mime type `$image_mime`" );
242 }
243
244 if ( empty( $file_name ) ) {
245 $file_name = basename( $file );
246 }
247
248 $payload .= $this->get_file_payload( $file_name, $image_mime, $file, $boundary );
249 }
250
251 $payload .= '--' . $boundary . '--';
252
253 return $payload;
254 }
255
256 /**
257 * get_file_payload
258 * @param string $filename
259 * @param string $file_type
260 * @param string $file_path
261 * @param string $boundary
262 * @return string
263 */
264 private function get_file_payload( string $filename, string $file_type, string $file_path, string $boundary ): string {
265 $name = $filename ?? basename( $file_path );
266 $mine_type = 'image' === $file_type ? image_type_to_mime_type( exif_imagetype( $file_path ) ) : $file_type;
267 $payload = '';
268 // Upload the file
269 $payload .= '--' . $boundary;
270 $payload .= "\r\n";
271 $payload .= 'Content-Disposition: form-data; name="' . esc_attr( $name ) . '"; filename="' . esc_attr( $name ) . '"' . "\r\n";
272 $payload .= 'Content-Type: ' . $mine_type . "\r\n";
273 $payload .= "\r\n";
274 $payload .= file_get_contents( $file_path );
275 $payload .= "\r\n";
276
277 return $payload;
278 }
279 }
280