| @@ -50,20 +50,42 @@ | ||
| 50 | 50 | return $_instance; |
| 51 | 51 | } |
| 52 | 52 | |
| 53 | 53 | /** |
| 54 | - * Make http request to theme style.css, get 'server' line and check headers for gzip/brotli encoding option. | |
| 54 | + * Make http request to $url, get 'server' line and check headers for gzip/brotli encoding option. | |
| 55 | 55 | * |
| 56 | + * @param string $url | |
| 57 | + * | |
| 56 | 58 | * @return array|WP_Error |
| 57 | 59 | */ |
| 58 | - public function get_headers_information() { | |
| 59 | - static $headers_information; | |
| 60 | - if (isset($headers_information)) return $headers_information; | |
| 60 | + public function get_headers_information($url) { | |
| 61 | + static $cached_headers_information = array(); | |
| 62 | + if (isset($cached_headers_information[$url])) return $cached_headers_information[$url]; | |
| 61 | 63 | |
| 62 | - $headers = $this->get_stylesheet_headers(); | |
| 64 | + $response = wp_remote_get($url, array('timeout' => 10)); | |
| 63 | 65 | |
| 64 | - if (is_wp_error($headers)) return $headers; | |
| 66 | + if (is_wp_error($response)) { | |
| 67 | + return $response; | |
| 68 | + } | |
| 65 | 69 | |
| 70 | + $response_code = wp_remote_retrieve_response_code($response); | |
| 71 | + | |
| 72 | + if (200 !== $response_code) { | |
| 73 | + // translators: %1$s is a requested URL, %2$s is the HTTP response code | |
| 74 | + return new WP_Error($response_code, sprintf(__('Unexpected response code when trying to get response for %1$s: %2$s (expected 200)', 'wp-optimize'), $url, $response_code)); | |
| 75 | + } | |
| 76 | + | |
| 77 | + $headers = wp_remote_retrieve_headers($response); | |
| 78 | + | |
| 79 | + if (empty($headers)) { | |
| 80 | + // translators: %s is a requested URL | |
| 81 | + return new WP_Error($response_code, sprintf(__('Unable to retrieve HTTP headers information for %s', 'wp-optimize'), $url)); | |
| 82 | + } | |
| 83 | + | |
| 84 | + if (is_object($headers) && method_exists($headers, 'getAll')) { | |
| 85 | + $headers = $headers->getAll(); | |
| 86 | + } | |
| 87 | + | |
| 66 | 88 | $headers_information = array( |
| 67 | 89 | 'server' => array_key_exists('server', $headers) ? $headers['server'] : '', |
| 68 | 90 | ); |
| 69 | 91 | |
| @@ -73,13 +95,17 @@ | ||
| 73 | 95 | $this->disable(); |
| 74 | 96 | } elseif (array_key_exists('content-encoding', $headers) && preg_match('/gzip/i', $headers['content-encoding'])) { |
| 75 | 97 | // check if there exists Content-encoding header with gzip value. |
| 76 | 98 | $headers_information['compression'] = 'gzip'; |
| 77 | - | |
| 99 | + } elseif (array_key_exists('content-encoding', $headers) && preg_match('/zstd/i', $headers['content-encoding'])) { | |
| 100 | + // check if there exists Content-encoding header with zstd value. | |
| 101 | + $headers_information['compression'] = 'zstd'; | |
| 78 | 102 | } else { |
| 79 | 103 | $headers_information['compression'] = false; |
| 80 | 104 | } |
| 81 | 105 | |
| 106 | + $cached_headers_information[$url] = $headers_information; | |
| 107 | + | |
| 82 | 108 | return $headers_information; |
| 83 | 109 | } |
| 84 | 110 | |
| 85 | 111 | /** |
| @@ -111,26 +137,22 @@ | ||
| 111 | 137 | |
| 112 | 138 | /** |
| 113 | 139 | * Check if Gzip compression is enabled. |
| 114 | 140 | * |
| 115 | - * @param boolean $force_check - force the check | |
| 141 | + * @param boolean $use_cache - use cached data | |
| 116 | 142 | * @return bool|WP_Error |
| 117 | 143 | */ |
| 118 | - public function is_gzip_compression_enabled($force_check = false) { | |
| 144 | + public function is_gzip_compression_enabled($use_cache = true) { | |
| 119 | 145 | |
| 120 | - if (!$force_check) return WP_Optimize()->get_options()->get_option('is_gzip_compression_enabled'); | |
| 146 | + $compression_types = $this->get_compression_types($use_cache); | |
| 121 | 147 | |
| 122 | - // trying to get info about gzip in headers. | |
| 123 | - $headers_info = $this->get_headers_information(); | |
| 148 | + $is_gzip_compression_enabled = true; | |
| 124 | 149 | |
| 125 | - // we can't determine then return WP_Error. | |
| 126 | - if (is_wp_error($headers_info)) return $headers_info; | |
| 150 | + foreach ($compression_types as $compression) { | |
| 151 | + if (is_wp_error($compression)) return $compression; | |
| 152 | + if (!in_array($compression, array('gzip', 'brotli', 'zstd'))) $is_gzip_compression_enabled = false; | |
| 153 | + } | |
| 127 | 154 | |
| 128 | - $is_gzip_compression_enabled = $headers_info['compression']; | |
| 129 | - | |
| 130 | - // if we got error then trying to get info from api otherwise get result from check_headers_for_gzip(). | |
| 131 | - // $is_gzip_compression_enabled = is_wp_error($is_gzip_compression_enabled) ? $this->check_api_for_gzip() : $is_gzip_compression_enabled; | |
| 132 | - | |
| 133 | 155 | // if Gzip is not enabled, but we have added settings and Apache modules nt loaded then return error. |
| 134 | 156 | if (!$is_gzip_compression_enabled && $this->is_gzip_compression_section_exists()) { |
| 135 | 157 | if (false === $this->_wp_optimize->is_apache_module_loaded(array('mod_filter', 'mod_deflate'))) { |
| 136 | 158 | return new WP_Error('gzip_missing_module', __('We successfully added Gzip compression settings into .htaccess file.', 'wp-optimize').' '.__('However, the test file we fetched was not Gzip-compressed.', 'wp-optimize').' '.__('It seems one of Apache modules - mod_filter or mod_deflate - is not active.', 'wp-optimize')); |
| @@ -140,14 +162,35 @@ | ||
| 140 | 162 | return new WP_Error('gzip_unsuccessful', __('We successfully added Gzip compression settings into .htaccess file.', 'wp-optimize').' '.__('However, the test file we fetched was not Gzip-compressed.', 'wp-optimize').' '.__('You should speak to your web hosting support to find how to enable it.', 'wp-optimize')); |
| 141 | 163 | } |
| 142 | 164 | } |
| 143 | 165 | |
| 144 | - WP_Optimize()->get_options()->update_option('is_gzip_compression_enabled', $is_gzip_compression_enabled); | |
| 166 | + // Stored value used to restore gzip compression settings used when plugin being activated, | |
| 167 | + // i.e. we need to update it only when we have added settings to .htaccess or gzip is disabled | |
| 168 | + if (!$is_gzip_compression_enabled || $this->is_gzip_compression_section_exists()) { | |
| 169 | + WP_Optimize()->get_options()->update_option('is_gzip_compression_enabled', $is_gzip_compression_enabled); | |
| 170 | + } | |
| 145 | 171 | |
| 146 | 172 | return $is_gzip_compression_enabled; |
| 147 | 173 | } |
| 148 | 174 | |
| 149 | 175 | /** |
| 176 | + * Returns the compression type of the given URL. | |
| 177 | + * | |
| 178 | + * @param string $url | |
| 179 | + * @return string|WP_Error | |
| 180 | + */ | |
| 181 | + private function get_compression_type($url) { | |
| 182 | + | |
| 183 | + $headers_info = $this->get_headers_information($url); | |
| 184 | + | |
| 185 | + if (is_wp_error($headers_info)) return $headers_info; | |
| 186 | + | |
| 187 | + if (!isset($headers_info['compression'])) return ''; | |
| 188 | + | |
| 189 | + return $headers_info['compression']; | |
| 190 | + } | |
| 191 | + | |
| 192 | + /** | |
| 150 | 193 | * Check if section with Gzip options already exists in htaccess file. |
| 151 | 194 | * |
| 152 | 195 | * @return bool |
| 153 | 196 | */ |
| @@ -207,9 +250,9 @@ | ||
| 207 | 250 | // set correct $section-updated flag. |
| 208 | 251 | $section_updated = $enable === $section_exists; |
| 209 | 252 | } |
| 210 | 253 | |
| 211 | - $is_gzip_compression_enabled = $this->is_gzip_compression_enabled(true); | |
| 254 | + $is_gzip_compression_enabled = $this->is_gzip_compression_enabled(false); | |
| 212 | 255 | |
| 213 | 256 | if ($section_updated) { |
| 214 | 257 | return array( |
| 215 | 258 | 'success' => true, |
| @@ -237,8 +280,131 @@ | ||
| 237 | 280 | join(PHP_EOL, $this->_htaccess->get_flat_array($gzip_section)). |
| 238 | 281 | PHP_EOL.$this->_htaccess->get_section_end_comment($this->_htaccess_section_comment)), |
| 239 | 282 | ); |
| 240 | 283 | } |
| 284 | + } | |
| 285 | + | |
| 286 | + /** | |
| 287 | + * Retrieve the compression statuses for HTML, CSS, and JS resources in HTML format. | |
| 288 | + * | |
| 289 | + * @param bool $use_cache if true, the cached value will be returned if possible | |
| 290 | + * @return string | |
| 291 | + */ | |
| 292 | + public function get_compression_test_results_html($use_cache = false) { | |
| 293 | + | |
| 294 | + $compression_types = $this->get_compression_types($use_cache); | |
| 295 | + | |
| 296 | + $result_html = ''; | |
| 297 | + | |
| 298 | + foreach ($compression_types as $title => $compression) { | |
| 299 | + if (is_wp_error($compression)) { | |
| 300 | + $value = $compression->get_error_message(); | |
| 301 | + } elseif (!empty($compression)) { | |
| 302 | + $value = '<b>'.$compression.'</b>'; | |
| 303 | + } else { | |
| 304 | + $value = __('No compression', 'wp-optimize'); | |
| 305 | + } | |
| 306 | + | |
| 307 | + $result_html .= $title.': '. $value . '<br>'; | |
| 308 | + } | |
| 309 | + | |
| 310 | + return wp_kses_post($result_html); | |
| 311 | + } | |
| 312 | + | |
| 313 | + /** | |
| 314 | + * Get list of content types for which enabled/disabled gzip compression (html, css, js). | |
| 315 | + * | |
| 316 | + * @param bool $use_cache if true, the cached value will be returned if possible | |
| 317 | + * | |
| 318 | + * @return array | |
| 319 | + */ | |
| 320 | + public function get_enabled_disabled_compression_content_types($use_cache = false) { | |
| 321 | + $result = array( | |
| 322 | + 'enabled' => array(), | |
| 323 | + 'disabled' => array(), | |
| 324 | + ); | |
| 325 | + | |
| 326 | + $compression_types = $this->get_compression_types($use_cache); | |
| 327 | + | |
| 328 | + foreach ($compression_types as $type => $status) { | |
| 329 | + if (false !== $status && '' !== $status && !is_wp_error($status)) { | |
| 330 | + $result['enabled'][] = $type; | |
| 331 | + } else { | |
| 332 | + $result['disabled'][] = $type; | |
| 333 | + } | |
| 334 | + } | |
| 335 | + | |
| 336 | + return $result; | |
| 337 | + } | |
| 338 | + | |
| 339 | + /** | |
| 340 | + * Get compression types for html, css, js files. | |
| 341 | + * | |
| 342 | + * @param bool $use_cache if true, the cached value will be returned if possible | |
| 343 | + * | |
| 344 | + * @return array | |
| 345 | + */ | |
| 346 | + private function get_compression_types($use_cache = false) { | |
| 347 | + $transient_key = 'wpo_compression_test_results'; | |
| 348 | + $compression_types = array('HTML' => '', 'CSS' => '', 'JS' => ''); | |
| 349 | + $cached_compression_types = array(); | |
| 350 | + | |
| 351 | + if ($use_cache) { | |
| 352 | + $cached_compression_types = get_transient($transient_key); | |
| 353 | + } | |
| 354 | + | |
| 355 | + $should_update_cache = false; | |
| 356 | + | |
| 357 | + foreach ($compression_types as $content_type => $compression) { | |
| 358 | + // if we have cached value and it is not WP_Error then use it, otherwise get compression type and update cache later. | |
| 359 | + if ($use_cache && isset($cached_compression_types[$content_type]) && 'WP_Error' !== $cached_compression_types[$content_type]) { | |
| 360 | + $compression_types[$content_type] = $cached_compression_types[$content_type]; | |
| 361 | + } else { | |
| 362 | + $compression_types[$content_type] = $this->get_compression_type_by_content_type($content_type); | |
| 363 | + $should_update_cache = true; | |
| 364 | + } | |
| 365 | + } | |
| 366 | + | |
| 367 | + if ($should_update_cache) { | |
| 368 | + $compression_types_for_cache = $compression_types; | |
| 369 | + foreach ($compression_types_for_cache as $content_type => $compression) { | |
| 370 | + if (is_wp_error($compression)) { | |
| 371 | + $compression_types_for_cache[$content_type] = 'WP_Error'; | |
| 372 | + } | |
| 373 | + } | |
| 374 | + set_transient($transient_key, $compression_types_for_cache, HOUR_IN_SECONDS); | |
| 375 | + } | |
| 376 | + | |
| 377 | + return $compression_types; | |
| 378 | + } | |
| 379 | + | |
| 380 | + /** | |
| 381 | + * Get compression type for the given content type (html, css, js). | |
| 382 | + * | |
| 383 | + * @param string $content_type - content type (html, css, js) | |
| 384 | + * @return string|false|WP_Error | |
| 385 | + */ | |
| 386 | + private function get_compression_type_by_content_type($content_type) { | |
| 387 | + $content_type = strtoupper($content_type); | |
| 388 | + switch ($content_type) { | |
| 389 | + case 'HTML': | |
| 390 | + return $this->get_compression_type(get_home_url(null, '?cache=false')); | |
| 391 | + case 'CSS': | |
| 392 | + return $this->get_compression_type($this->css_resource_url()); | |
| 393 | + case 'JS': | |
| 394 | + return $this->get_compression_type(site_url('wp-includes/js/admin-bar.min.js')); | |
| 395 | + default: | |
| 396 | + return ''; | |
| 397 | + } | |
| 398 | + } | |
| 399 | + | |
| 400 | + /** | |
| 401 | + * Get URL to the CSS resource for checking Gzip compression status of CSS files. | |
| 402 | + * | |
| 403 | + * @return string | |
| 404 | + */ | |
| 405 | + public function css_resource_url() { | |
| 406 | + return WPO_PLUGIN_URL . 'css/wp-optimize-admin.css'; | |
| 241 | 407 | } |
| 242 | 408 | |
| 243 | 409 | /** |
| 244 | 410 | * Prepare array with options to switch on gzip in htaccess. |