PluginProbe
Image Optimizer – Compress Images and Convert to WebP or AVIF / 1.0.1
Image Optimizer – Compress Images and Convert to WebP or AVIF v1.0.1
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.php

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

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