PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.2.22
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.2.22
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / webp / class-wp-optimize-webp.php

class-wp-optimize-webp.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 3.2.22, at webp/class-wp-optimize-webp.php

405 lines 11.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('WPO_VERSION')) die('No direct access allowed');
4
5 if (!class_exists('WP_Optimize_WebP')) :
6
7 class WP_Optimize_WebP {
8
9 private $_htaccess = null;
10
11 private $_should_use_webp = false;
12
13 /**
14 * Constructor
15 */
16 private function __construct() {
17 $this->_should_use_webp = WP_Optimize()->get_options()->get_option('webp_conversion');
18 if ($this->should_run_webp_conversion_test()) {
19 $this->set_converter_status();
20 }
21
22 if ($this->get_webp_conversion_test_result()) {
23 if (!is_admin()) {
24 $this->maybe_decide_webp_serve_method();
25 }
26 } else {
27 $this->empty_htaccess_file();
28 }
29
30 $this->init_webp_cron_scheduler();
31 }
32
33 /**
34 * Returns singleton instance
35 *
36 * @return WP_Optimize_WebP
37 */
38 public static function get_instance() {
39 static $instance = null;
40 if (null === $instance) {
41 $instance = new WP_Optimize_WebP();
42 }
43 return $instance;
44 }
45
46 /**
47 * Test Run and find converter status
48 */
49 private function set_converter_status() {
50 if (!class_exists('WPO_WebP_Test_Run')) {
51 require_once WPO_PLUGIN_MAIN_PATH . 'webp/class-wpo-webp-test-run.php';
52 }
53 $converter_status = WPO_WebP_Test_Run::get_converter_status();
54 if ($this->is_webp_conversion_successful()) {
55 WP_Optimize()->get_options()->update_option('webp_conversion_test', true);
56 WP_Optimize()->get_options()->update_option('webp_converters', $converter_status['working_converters']);
57 }
58 }
59
60 /**
61 * If webp images should be used, then decide whether it is possible to server webp
62 * using rewrite rules or using altered html method
63 */
64 private function maybe_decide_webp_serve_method() {
65 if (!$this->_should_use_webp) {
66 $this->empty_htaccess_file();
67 } else {
68 $this->save_htaccess_rules();
69 if (!$this->is_webp_redirection_possible()) {
70 $this->empty_htaccess_file();
71 $this->maybe_use_alter_html();
72 }
73 }
74 }
75
76 /**
77 * If alter html method is possible, then use it
78 */
79 private function maybe_use_alter_html() {
80 if ($this->is_alter_html_possible()) {
81 $this->use_alter_html();
82 }
83 }
84
85 /**
86 * Even if server support .htaccess rewrite, sometimes it is not possible
87 * to serve webp images. So, find it webp redirection is possible or not
88 *
89 * @return bool
90 */
91 private function is_webp_redirection_possible() {
92 $redirection_possible = WP_Optimize()->get_options()->get_option('redirection_possible');
93 if (!empty($redirection_possible)) return 'true' === $redirection_possible;
94 return $this->run_webp_serving_self_test();
95 }
96
97 /**
98 * Decide whether the browser requesting the URL can accept webp images or not
99 *
100 * @return bool
101 */
102 private function is_browser_accepting_webp() {
103 return (isset($_SERVER['HTTP_ACCEPT']) && false !== strpos($_SERVER['HTTP_ACCEPT'], 'image/webp'));
104 }
105
106 /**
107 * Detect whether using alter HTML method is possible or not
108 *
109 * @return bool
110 */
111 private function is_alter_html_possible() {
112 if ($this->is_browser_accepting_webp()) {
113 return true;
114 }
115 return false;
116 }
117
118 /**
119 * Setup alter html method
120 */
121 private function use_alter_html() {
122 if (!class_exists('WPO_WebP_Alter_HTML')) {
123 require_once WPO_PLUGIN_MAIN_PATH . 'webp/class-wpo-webp-alter-html.php';
124 }
125 WPO_WebP_Alter_HTML::get_instance();
126 }
127
128 /**
129 * Initialize .htaccess
130 */
131 private function setup_htaccess_file() {
132 if (null !== $this->_htaccess) return;
133 $wp_uploads = wp_get_upload_dir();
134 $htaccess_file = $wp_uploads['basedir'] . '/.htaccess';
135 if (!file_exists($htaccess_file)) {
136 file_put_contents($htaccess_file, '');
137 }
138 if (!class_exists('WP_Optimize_Htaccess')) {
139 require_once WPO_PLUGIN_MAIN_PATH . 'includes/class-wp-optimize-htaccess.php';
140 }
141 $this->_htaccess = new WP_Optimize_Htaccess($htaccess_file);
142 }
143
144 /**
145 * Save .htaccess rules
146 *
147 * @return void
148 */
149 private function save_htaccess_rules() {
150 $this->setup_htaccess_file();
151 $this->add_webp_mime_type();
152 $htaccess_comment_section = 'WP-Optimize WebP Rules';
153 if ($this->_htaccess->is_commented_section_exists($htaccess_comment_section)) return;
154 $this->_htaccess->update_commented_section($this->prepare_webp_htaccess_rules(), $htaccess_comment_section);
155 $this->_htaccess->write_file();
156 }
157
158 /**
159 * Empty .htaccess file
160 */
161 public function empty_htaccess_file() {
162 $this->setup_htaccess_file();
163 $htaccess_comment_sections = array(
164 'WP-Optimize WebP Rules',
165 'Register webp mime type',
166 );
167 foreach ($htaccess_comment_sections as $htaccess_comment_section) {
168 $this->_htaccess->remove_commented_section($htaccess_comment_section);
169 $this->_htaccess->write_file();
170 }
171 }
172
173 /**
174 * Prepare array of htaccess rules to use webp images.
175 *
176 * @return array
177 */
178 private function prepare_webp_htaccess_rules() {
179 return array(
180 array(
181 '<IfModule mod_rewrite.c>',
182 'RewriteEngine On',
183 '',
184 '# Redirect to existing converted image in same dir (if browser supports webp)',
185 'RewriteCond %{HTTP_ACCEPT} image/webp',
186 'RewriteCond %{REQUEST_FILENAME} (?i)(.*)(\.jpe?g|\.png)$',
187 'RewriteCond %1%2\.webp -f',
188 'RewriteRule (?i)(.*)(\.jpe?g|\.png)$ %1%2\.webp [T=image/webp,E=EXISTING:1,E=ADDVARY:1,L]',
189 '',
190 '# Make sure that browsers which does not support webp also gets the Vary:Accept header',
191 '# when requesting images that would be redirected to webp on browsers that does.',
192 array(
193 '<IfModule mod_headers.c>',
194 array(
195 '<FilesMatch "(?i)\.(jpe?g|png)$">',
196 'Header append "Vary" "Accept"',
197 '</FilesMatch>',
198 ),
199 '</IfModule>',
200 ),
201 '',
202 '</IfModule>',
203 '',
204 ),
205 array(
206 '# Rules for handling requests for webp images',
207 '# ---------------------------------------------',
208 '',
209 '# Set Vary:Accept header if we came here by way of our redirect, which set the ADDVARY environment variable',
210 '# The purpose is to make proxies and CDNs aware that the response varies with the Accept header',
211 '<IfModule mod_headers.c>',
212 array(
213 '<IfModule mod_setenvif.c>',
214 '# Apache appends "REDIRECT_" in front of the environment variables defined in mod_rewrite, but LiteSpeed does not',
215 '# So, the next lines are for Apache, in order to set environment variables without "REDIRECT_"',
216 'SetEnvIf REDIRECT_EXISTING 1 EXISTING=1',
217 'SetEnvIf REDIRECT_ADDVARY 1 ADDVARY=1',
218 '',
219 'Header append "Vary" "Accept" env=ADDVARY',
220 '',
221 '# Set X-WPO-WebP header for diagnose purposes',
222 'Header set "X-WPO-WebP" "Redirected directly to existing webp" env=EXISTING',
223 '</IfModule>',
224 ),
225 '</IfModule>',
226 ),
227 );
228 }
229
230 /**
231 * Add webp mime type to htaccess rules.
232 */
233 private function add_webp_mime_type() {
234 $htaccess_comment_section = 'Register webp mime type';
235 if ($this->_htaccess->is_exists() && !$this->_htaccess->is_commented_section_exists($htaccess_comment_section)) {
236 $webp_mime_type = array(
237 array(
238 '<IfModule mod_mime.c>',
239 'AddType image/webp .webp',
240 '</IfModule>',
241 ),
242 );
243 $this->_htaccess->update_commented_section($webp_mime_type, $htaccess_comment_section);
244 $this->_htaccess->write_file();
245 }
246 }
247
248 /**
249 * Checks whether webp conversion test is successful or not
250 *
251 * @return bool
252 */
253 private function is_webp_conversion_successful() {
254 $upload_dir = wp_upload_dir();
255 $destination = $upload_dir['basedir']. '/wpo/images/wpo_logo_small.png.webp';
256 return file_exists($destination);
257 }
258
259 /**
260 * Checks whether sample webp conversion test should be run or not
261 *
262 * @return bool Returns true if sample test should be run, false otherwise
263 */
264 private function should_run_webp_conversion_test() {
265 $webp_conversion_test = $this->get_webp_conversion_test_result();
266 return (true != $webp_conversion_test);
267 }
268
269 /**
270 * Returns webp conversion test result
271 */
272 private function get_webp_conversion_test_result() {
273 return WP_Optimize()->get_options()->get_option('webp_conversion_test');
274 }
275
276 /**
277 * Checks whether the webp redirection is possible or not and sets flag
278 *
279 * @return bool Returns true if webp is served successfully, false otherwise
280 */
281 private function run_webp_serving_self_test() {
282 $self_test = WPO_WebP_Self_Test::get_instance();
283
284 if ($self_test->is_webp_served()) {
285 WP_Optimize()->get_options()->update_option('redirection_possible', 'true');
286 return true;
287 }
288 WP_Optimize()->get_options()->update_option('redirection_possible', 'false');
289 $this->empty_htaccess_file();
290 return false;
291 }
292
293 /**
294 * Resets webp serving method by running self test, if needed purges cache and empties `uploads/.htaccess` file
295 */
296 public function reset_webp_serving_method() {
297 $this->reset_webp_options();
298 $this->run_self_test();
299 list($old_redirection_possible, $new_redirection_possible) = $this->get_old_and_new_redirection_possibility();
300 $this->maybe_purge_cache($old_redirection_possible, $new_redirection_possible);
301 $this->maybe_empty_htaccess_file($new_redirection_possible);
302 }
303
304 /**
305 * Resets WebP related options
306 */
307 private function reset_webp_options() {
308 $options = WP_Optimize()->get_options();
309 $options->update_option('old_redirection_possible', $options->get_option('redirection_possible'));
310 $options->update_option('webp_conversion_test', false);
311 $options->update_option('webp_converters', false);
312 $options->update_option('redirection_possible', false);
313 }
314
315 /**
316 * Running self test to find available converters and possibility of serving webp using redirection method
317 */
318 private function run_self_test() {
319 $this->set_converter_status();
320 if ($this->get_webp_conversion_test_result()) {
321 $this->save_htaccess_rules();
322 $this->run_webp_serving_self_test();
323 }
324 }
325
326 /**
327 * Gets old and new redirection possibility values
328 *
329 * @return array
330 */
331 private function get_old_and_new_redirection_possibility() {
332 $options = WP_Optimize()->get_options();
333 return array(
334 $options->get_option('old_redirection_possible'),
335 $options->get_option('redirection_possible'),
336 );
337 }
338
339 /**
340 * Cache is cleared when there is a change in the potential for serving WebP using redirection.
341 *
342 * @param string $old_redirection_possible
343 * @param string $new_redirection_possible
344 */
345 private function maybe_purge_cache($old_redirection_possible, $new_redirection_possible) {
346 if ($old_redirection_possible !== $new_redirection_possible) {
347 WP_Optimize()->get_page_cache()->purge();
348 }
349 }
350
351 /**
352 * Remove redirection rules from `uploads/.htaccess` file if redirection is not possible
353 *
354 * @param string $new_redirection_possible
355 */
356 private function maybe_empty_htaccess_file($new_redirection_possible) {
357 if ('false' === $new_redirection_possible) {
358 $this->empty_htaccess_file();
359 }
360 }
361
362 /**
363 * Initialize cron scheduler
364 */
365 private function init_webp_cron_scheduler() {
366 WPO_WebP_Cron_Scheduler::get_instance();
367 }
368
369 /**
370 * Determines whether the php shell functions are available or not
371 *
372 * @return bool
373 */
374 public static function is_shell_functions_available() {
375 $shell_functions = self::get_shell_functions();
376 foreach ($shell_functions as $shell_function) {
377 if (!function_exists($shell_function)) return false;
378 }
379 return true;
380 }
381
382 /**
383 * List of php shell function names
384 *
385 * @return string[]
386 */
387 public static function get_shell_functions() {
388 return array(
389 'escapeshellarg',
390 'escapeshellcmd',
391 'exec',
392 'passthru',
393 'proc_close',
394 'proc_get_status',
395 'proc_nice',
396 'proc_open',
397 'proc_terminate',
398 'shell_exec',
399 'system',
400 );
401 }
402 }
403
404 endif;
405