PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.2.20
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.2.20
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.20, at webp/class-wp-optimize-webp.php

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