PluginProbe
Image Optimizer – Compress Images and Convert to WebP or AVIF / 1.3.0
Image Optimizer – Compress Images and Convert to WebP or AVIF v1.3.0
1.7.7 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 All 33 releases
image-optimization / classes / client / client.php

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

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