get_boolean('config.check') || $force_all_checks) { if ($config->get_boolean('browsercache.enabled')) { $this->rules_cache_add($config, $exs); } else { $this->rules_cache_remove($exs); } if ($config->get_boolean('browsercache.enabled') && $config->get_boolean('browsercache.no404wp')) { $this->rules_no404wp_add($config, $exs); } else { $this->rules_no404wp_remove($exs); } } if (count($exs->exceptions()) > 0) throw $exs; } /** * Fixes environment once event occurs * @throws SelfTestExceptions **/ public function fix_on_event($config, $event, $old_config = null) { } /** * Fixes environment after plugin deactivation * @throws SelfTestExceptions */ public function fix_after_deactivation() { $exs = new SelfTestExceptions(); $this->rules_cache_remove($exs); $this->rules_no404wp_remove($exs); if (count($exs->exceptions()) > 0) throw $exs; } /** * Returns required rules for module * * @param W3_Config $config * @return array */ public function get_required_rules($config) { if (!$config->get_boolean('browsercache.enabled')) return null; $rewrite_rules = array(); $dispatcher = w3_instance('W3_Dispatcher'); if ($dispatcher->should_browsercache_generate_rules_for_cdn($config)) { $domain = $dispatcher->get_cdn_domain(); $cdn_rules_path = sprintf('ftp://%s/%s', $domain, w3_get_cdn_rules_path()); $rewrite_rules[] = array( 'filename' => $cdn_rules_path, 'content' => $this->rules_cache_generate($config, true) ); } $browsercache_rules_cache_path = w3_get_browsercache_rules_cache_path(); $rewrite_rules[] = array( 'filename' => $browsercache_rules_cache_path, 'content' => $this->rules_cache_generate($config) ); if ($config->get_boolean('browsercache.no404wp')) { $browsercache_rules_no404wp_path = w3_get_browsercache_rules_no404wp_path(); $rewrite_rules[] = array( 'filename' => $browsercache_rules_no404wp_path, 'content' => $this->rules_no404wp_generate($config) ); } return $rewrite_rules; } /** * Returns mime types * * @return array */ public function get_mime_types() { return array( 'cssjs' => include W3TC_INC_DIR . '/mime/cssjs.php', 'html' => include W3TC_INC_DIR . '/mime/html.php', 'other' => include W3TC_INC_DIR . '/mime/other.php' ); } /** * Generate rules for FTP upload * * @param W3_Config $config * @return string **/ public function rules_cache_generate_for_ftp($config) { return $this->rules_cache_generate($config, true); } /** * rules cache **/ /** * Writes cache rules * * @throws FilesystemOperationException with S/FTP form if it can't get the required filesystem credentials * @throws FileOperationException */ private function rules_cache_add($config, $exs) { w3_add_rules($exs, w3_get_browsercache_rules_cache_path(), $this->rules_cache_generate($config), W3TC_MARKER_BEGIN_BROWSERCACHE_CACHE, W3TC_MARKER_END_BROWSERCACHE_CACHE, array( W3TC_MARKER_BEGIN_MINIFY_CORE => 0, W3TC_MARKER_BEGIN_PGCACHE_CORE => 0, W3TC_MARKER_BEGIN_BROWSERCACHE_NO404WP => 0, W3TC_MARKER_BEGIN_WORDPRESS => 0, W3TC_MARKER_END_PGCACHE_CACHE => strlen(W3TC_MARKER_END_PGCACHE_CACHE) + 1, W3TC_MARKER_END_MINIFY_CACHE => strlen(W3TC_MARKER_END_MINIFY_CACHE) + 1 ) ); } /** * Removes cache directives * * @throws FilesystemOperationException with S/FTP form if it can't get the required filesystem credentials * @throws FileOperationException */ private function rules_cache_remove($exs) { w3_remove_rules($exs, w3_get_browsercache_rules_cache_path(), W3TC_MARKER_BEGIN_BROWSERCACHE_CACHE, W3TC_MARKER_END_BROWSERCACHE_CACHE); } /** * Returns cache rules * * @param W3_Config $config * @param bool $cdnftp * @return string */ public function rules_cache_generate($config, $cdnftp = false) { switch (true) { case w3_is_apache(): case w3_is_litespeed(): return $this->rules_cache_generate_apache($config); case w3_is_nginx(): return $this->rules_cache_generate_nginx($config, $cdnftp); } return ''; } /** * Returns cache rules * * @param W3_Config $config * @return string */ private function rules_cache_generate_apache($config) { $mime_types2 = $this->get_mime_types(); $cssjs_types = $mime_types2['cssjs']; $cssjs_types = array_unique($cssjs_types); $html_types = $mime_types2['html']; $other_types = $mime_types2['other']; $cssjs_expires = $config->get_boolean('browsercache.cssjs.expires'); $html_expires = $config->get_boolean('browsercache.html.expires'); $other_expires = $config->get_boolean('browsercache.other.expires'); $cssjs_lifetime = $config->get_integer('browsercache.cssjs.lifetime'); $html_lifetime = $config->get_integer('browsercache.html.lifetime'); $other_lifetime = $config->get_integer('browsercache.other.lifetime'); $compatibility = $config->get_boolean('pgcache.compatibility'); $mime_types = array(); if ($cssjs_expires && $cssjs_lifetime) { $mime_types = array_merge($mime_types, $cssjs_types); } if ($html_expires && $html_lifetime) { $mime_types = array_merge($mime_types, $html_types); } if ($other_expires && $other_lifetime) { $mime_types = array_merge($mime_types, $other_types); } $rules = ''; $rules .= W3TC_MARKER_BEGIN_BROWSERCACHE_CACHE . "\n"; if (count($mime_types)) { $rules .= "\n"; foreach ($mime_types as $ext => $mime_type) { $extensions = explode('|', $ext); if (!is_array($mime_type)) $mime_type = (array)$mime_type; foreach($mime_type as $mime_type2) { $rules .= " AddType " . $mime_type2; foreach ($extensions as $extension) { $rules .= " ." . $extension; } $rules .= "\n"; } } $rules .= "\n"; $rules .= "\n"; $rules .= " ExpiresActive On\n"; if ($cssjs_expires && $cssjs_lifetime) { foreach ($cssjs_types as $mime_type) { $rules .= " ExpiresByType " . $mime_type . " A" . $cssjs_lifetime . "\n"; } } if ($html_expires && $html_lifetime) { foreach ($html_types as $mime_type) { $rules .= " ExpiresByType " . $mime_type . " A" . $html_lifetime . "\n"; } } if ($other_expires && $other_lifetime) { foreach ($other_types as $mime_type) { if (is_array($mime_type)) foreach ($mime_type as $mime_type2) $rules .= " ExpiresByType " . $mime_type2 . " A" . $other_lifetime . "\n"; else $rules .= " ExpiresByType " . $mime_type . " A" . $other_lifetime . "\n"; } } $rules .= "\n"; } $cssjs_compression = $config->get_boolean('browsercache.cssjs.compression'); $html_compression = $config->get_boolean('browsercache.html.compression'); $other_compression = $config->get_boolean('browsercache.other.compression'); if ($cssjs_compression || $html_compression || $other_compression) { $compression_types = array(); if ($cssjs_compression) { $compression_types = array_merge($compression_types, $cssjs_types); } if ($html_compression) { $compression_types = array_merge($compression_types, $html_types); } if ($other_compression) { $compression_types = array_merge($compression_types, array( //for some reason the 'other' types are not loaded from the 'other' file 'ico' => 'image/x-icon', 'json' => 'application/json' )); } $rules .= "\n"; if ($compatibility) { $rules .= " \n"; $rules .= " BrowserMatch ^Mozilla/4 gzip-only-text/html\n"; $rules .= " BrowserMatch ^Mozilla/4\\.0[678] no-gzip\n"; $rules .= " BrowserMatch \\bMSIE !no-gzip !gzip-only-text/html\n"; $rules .= " BrowserMatch \\bMSI[E] !no-gzip !gzip-only-text/html\n"; $rules .= " \n"; } $rules .= " \n"; $rules .= " Header append Vary User-Agent env=!dont-vary\n"; $rules .= " \n"; if (version_compare($this->_get_server_version(), '2.3.7', '>=')) { $rules .= " \n"; } $rules .= " AddOutputFilterByType DEFLATE " . implode(' ', $compression_types) . "\n"; $rules .= " \n"; $rules .= " # DEFLATE by extension\n"; $rules .= " AddOutputFilter DEFLATE js css htm html xml\n"; $rules .= " \n"; if (version_compare($this->_get_server_version(), '2.3.7', '>=')) { $rules .= " \n"; } $rules .= "\n"; } foreach ($mime_types2 as $type => $extensions) $rules .= $this->_rules_cache_generate_apache_for_type($config, $extensions, $type); $rules .= W3TC_MARKER_END_BROWSERCACHE_CACHE . "\n"; return $rules; } /** * Writes cache rules * * @param W3_Config $config * @param array $mime_types * @param string $section * @return string */ private function _rules_cache_generate_apache_for_type($config, $mime_types, $section) { $is_disc_enhanced = $config->get_boolean('pgcache.enabled') && $config->get_string('pgcache.engine') == 'file_generic'; $cache_control = $config->get_boolean('browsercache.' . $section . '.cache.control'); $etag = $config->get_boolean('browsercache.' . $section . '.etag'); $w3tc = $config->get_boolean('browsercache.' . $section . '.w3tc'); $unset_setcookie = $config->get_boolean('browsercache.' . $section . '.nocookies'); $set_last_modified = $config->get_boolean('browsercache.' . $section . '.last_modified'); $compatibility = $config->get_boolean('pgcache.compatibility'); $extensions = array_keys($mime_types); // Remove ext from filesmatch if its the same as permalink extension $pext = strtolower(pathinfo(get_option('permalink_structure'), PATHINFO_EXTENSION)); if ($pext) { $extensions = $this->_remove_extension_from_list($extensions, $pext); } $extensions_lowercase = array_map('strtolower', $extensions); $extensions_uppercase = array_map('strtoupper', $extensions); $rules = ''; $headers_rules = ''; if ($cache_control) { $cache_policy = $config->get_string('browsercache.' . $section . '.cache.policy'); switch ($cache_policy) { case 'cache': $headers_rules .= " Header set Pragma \"public\"\n"; $headers_rules .= " Header set Cache-Control \"public\"\n"; break; case 'cache_public_maxage': $expires = $config->get_boolean('browsercache.' . $section . '.expires'); $lifetime = $config->get_integer('browsercache.' . $section . '.lifetime'); $headers_rules .= " Header set Pragma \"public\"\n"; if ($expires) $headers_rules .= " Header append Cache-Control \"public\"\n"; else $headers_rules .= " Header set Cache-Control \"max-age=" . $lifetime . ", public\"\n"; break; case 'cache_validation': $headers_rules .= " Header set Pragma \"public\"\n"; $headers_rules .= " Header set Cache-Control \"public, must-revalidate, proxy-revalidate\"\n"; break; case 'cache_noproxy': $headers_rules .= " Header set Pragma \"public\"\n"; $headers_rules .= " Header set Cache-Control \"private, must-revalidate\"\n"; break; case 'cache_maxage': $expires = $config->get_boolean('browsercache.' . $section . '.expires'); $lifetime = $config->get_integer('browsercache.' . $section . '.lifetime'); $headers_rules .= " Header set Pragma \"public\"\n"; if ($expires) $headers_rules .= " Header append Cache-Control \"public, must-revalidate, proxy-revalidate\"\n"; else $headers_rules .= " Header set Cache-Control \"max-age=" . $lifetime . ", public, must-revalidate, proxy-revalidate\"\n"; break; case 'no_cache': $headers_rules .= " Header set Pragma \"no-cache\"\n"; $headers_rules .= " Header set Cache-Control \"max-age=0, private, no-store, no-cache, must-revalidate\"\n"; break; } } if ($etag) { $rules .= " FileETag MTime Size\n"; } else { if ($compatibility) { $rules .= " FileETag None\n"; $headers_rules .= " Header unset ETag\n"; } } if ($unset_setcookie) $headers_rules .= " Header unset Set-Cookie\n"; if (!$set_last_modified) $headers_rules .= " Header unset Last-Modified\n"; if ($w3tc) $headers_rules .= " Header set X-Powered-By \"" . W3TC_POWERED_BY . "\"\n"; if (strlen($headers_rules) > 0) { $rules .= " \n"; $rules .= $headers_rules; $rules .= " \n"; } if (strlen($rules) > 0) { $rules = "\n" . $rules; $rules .= "\n"; } return $rules; } /** * Returns cache rules * * @param W3_Config $config * @param bool $cdnftp * @return string */ private function rules_cache_generate_nginx($config, $cdnftp = false) { $mime_types = $this->get_mime_types(); $cssjs_types = $mime_types['cssjs']; $cssjs_types = array_unique($cssjs_types); $html_types = $mime_types['html']; $other_types = $mime_types['other']; $rules = ''; $rules .= W3TC_MARKER_BEGIN_BROWSERCACHE_CACHE . "\n"; $cssjs_compression = $config->get_boolean('browsercache.cssjs.compression'); $html_compression = $config->get_boolean('browsercache.html.compression'); $other_compression = $config->get_boolean('browsercache.other.compression'); if ($cssjs_compression || $html_compression || $other_compression) { $compression_types = array(); if ($cssjs_compression) { $compression_types = array_merge($compression_types, $cssjs_types); } if ($html_compression) { $compression_types = array_merge($compression_types, $html_types); } if ($other_compression) { $compression_types = array_merge($compression_types, array( 'ico' => 'image/x-icon' )); } unset($compression_types['html|htm']); $rules .= "gzip on;\n"; $rules .= "gzip_types " . implode(' ', $compression_types) . ";\n"; } foreach ($mime_types as $type => $extensions) $this->_rules_cache_generate_nginx_for_type($config, $rules, $extensions, $type); $rules .= W3TC_MARKER_END_BROWSERCACHE_CACHE . "\n"; return $rules; } /** * Adds cache rules for type to &$rules * * @param W3_Config $config * @param string $rules * @param array $mime_types * @param string $section * @param boolean $write_location * @param boolean $cdnftp * @return void */ private function _rules_cache_generate_nginx_for_type($config, &$rules, $mime_types, $section, $write_location = false, $cdnftp = false) { $expires = $config->get_boolean('browsercache.' . $section . '.expires'); $cache_control = $config->get_boolean('browsercache.' . $section . '.cache.control'); $w3tc = $config->get_boolean('browsercache.' . $section . '.w3tc'); if ($expires || $cache_control || $w3tc) { $lifetime = $config->get_integer('browsercache.' . $section . '.lifetime'); $extensions = array_keys($mime_types); // Remove ext from filesmatch if its the same as permalink extension $pext = strtolower(pathinfo(get_option('permalink_structure'), PATHINFO_EXTENSION)); if ($pext) { $extensions = $this->_remove_extension_from_list($extensions, $pext); } $rules .= "location ~ \\.(" . implode('|', $extensions) . ")$ {\n"; if ($expires) { $rules .= " expires " . $lifetime . "s;\n"; } if ($cache_control) { $cache_policy = $config->get_string('browsercache.cssjs.cache.policy'); switch ($cache_policy) { case 'cache': $rules .= " add_header Pragma \"public\";\n"; $rules .= " add_header Cache-Control \"public\";\n"; break; case 'cache_public_maxage': $rules .= " add_header Pragma \"public\";\n"; $rules .= " add_header Cache-Control \"max-age=" . $lifetime . ", public\";\n"; break; case 'cache_validation': $rules .= " add_header Pragma \"public\";\n"; $rules .= " add_header Cache-Control \"public, must-revalidate, proxy-revalidate\";\n"; break; case 'cache_noproxy': $rules .= " add_header Pragma \"public\";\n"; $rules .= " add_header Cache-Control \"private, must-revalidate\";\n"; break; case 'cache_maxage': $rules .= " add_header Pragma \"public\";\n"; $rules .= " add_header Cache-Control \"max-age=" . $lifetime . ", public, must-revalidate, proxy-revalidate\";\n"; break; case 'no_cache': $rules .= " add_header Pragma \"no-cache\";\n"; $rules .= " add_header Cache-Control \"max-age=0, private, no-store, no-cache, must-revalidate\";\n"; break; } } $w3_dispatcher = w3_instance('W3_Dispatcher'); $rules .= $w3_dispatcher->on_browsercache_rules_generation_for_section( $config, $cdnftp, $section); if ($w3tc) { $rules .= " add_header X-Powered-By \"" . W3TC_POWERED_BY . "\";\n"; } if ($write_location) { $rules .= ' try_files $uri $uri/ $uri.html /index.php?$args;' . "\n"; } $rules .= "}\n"; } } /** * rules_no404wp **/ /** * Writes no 404 by WP rules * * @param W3_Config $config * @param SelfTestExceptions $exs * @throws FilesystemOperationException with S/FTP form * @throws FileOperationException */ private function rules_no404wp_add($config, $exs) { w3_add_rules($exs, w3_get_browsercache_rules_no404wp_path(), $this->rules_no404wp_generate($config), W3TC_MARKER_BEGIN_BROWSERCACHE_NO404WP, W3TC_MARKER_END_BROWSERCACHE_NO404WP, array( W3TC_MARKER_BEGIN_WORDPRESS => 0, W3TC_MARKER_END_PGCACHE_CORE => strlen(W3TC_MARKER_END_PGCACHE_CORE) + 1, W3TC_MARKER_END_MINIFY_CORE => strlen(W3TC_MARKER_END_MINIFY_CORE) + 1, W3TC_MARKER_END_BROWSERCACHE_CACHE => strlen(W3TC_MARKER_END_BROWSERCACHE_CACHE) + 1, W3TC_MARKER_END_PGCACHE_CACHE => strlen(W3TC_MARKER_END_PGCACHE_CACHE) + 1, W3TC_MARKER_END_MINIFY_CACHE => strlen(W3TC_MARKER_END_MINIFY_CACHE) + 1 ) ); } /** * Removes 404 directives * * @throws FilesystemOperationException with S/FTP form * @throws FileOperationException */ private function rules_no404wp_remove($exs) { w3_remove_rules($exs, w3_get_browsercache_rules_no404wp_path(), W3TC_MARKER_BEGIN_BROWSERCACHE_NO404WP, W3TC_MARKER_END_BROWSERCACHE_NO404WP ); } /** * Generate rules related to prevent for media 404 error by WP * * @param W3_Config $config * @return string */ private function rules_no404wp_generate($config) { switch (true) { case w3_is_apache(): case w3_is_litespeed(): return $this->rules_no404wp_generate_apache($config); case w3_is_nginx(): return $this->rules_no404wp_generate_nginx($config); } return false; } /** * Generate rules related to prevent for media 404 error by WP * * @param W3_Config $config * @return string */ private function rules_no404wp_generate_apache($config) { $a = $this->get_mime_types(); $cssjs_types = $a['cssjs']; $html_types = $a['html']; $other_types = $a['other']; $extensions = array_merge(array_keys($cssjs_types), array_keys($html_types), array_keys($other_types)); $permalink_structure = get_option('permalink_structure'); $permalink_structure_ext = ltrim(strrchr($permalink_structure, '.'), '.'); if ($permalink_structure_ext != '') { foreach ($extensions as $index => $extension) { if (strstr($extension, $permalink_structure_ext) !== false) { $extensions[$index] = preg_replace('~\|?' . w3_preg_quote($permalink_structure_ext) . '\|?~', '', $extension); } } } $exceptions = $config->get_array('browsercache.no404wp.exceptions'); $rules = ''; $rules .= W3TC_MARKER_BEGIN_BROWSERCACHE_NO404WP . "\n"; $rules .= "\n"; $rules .= " RewriteEngine On\n"; $rules .= " RewriteCond %{REQUEST_FILENAME} !-f\n"; $rules .= " RewriteCond %{REQUEST_FILENAME} !-d\n"; if (count($exceptions)) { $rules .= " RewriteCond %{REQUEST_URI} !(" . implode('|', $exceptions) . ")\n"; } $rules .= " RewriteCond %{REQUEST_FILENAME} \\.(" . implode('|', $extensions) . ")$ [NC]\n"; $rules .= " RewriteRule .* - [L]\n"; $rules .= "\n"; $rules .= W3TC_MARKER_END_BROWSERCACHE_NO404WP . "\n"; return $rules; } /** * Generate rules related to prevent for media 404 error by WP * * @param W3_Config $config * @return string */ private function rules_no404wp_generate_nginx($config) { $a = $this->get_mime_types(); $cssjs_types = $a['cssjs']; $html_types = $a['html']; $other_types = $a['other']; $extensions = array_merge(array_keys($cssjs_types), array_keys($html_types), array_keys($other_types)); $permalink_structure = get_option('permalink_structure'); $permalink_structure_ext = ltrim(strrchr($permalink_structure, '.'), '.'); if ($permalink_structure_ext != '') { foreach ($extensions as $index => $extension) { if (strstr($extension, $permalink_structure_ext) !== false) { $extensions[$index] = preg_replace('~\|?' . w3_preg_quote($permalink_structure_ext) . '\|?~', '', $extension); } } } $exceptions = $config->get_array('browsercache.no404wp.exceptions'); $rules = ''; $rules .= W3TC_MARKER_BEGIN_BROWSERCACHE_NO404WP . "\n"; $rules .= "if (-f \$request_filename) {\n"; $rules .= " break;\n"; $rules .= "}\n"; $rules .= "if (-d \$request_filename) {\n"; $rules .= " break;\n"; $rules .= "}\n"; if (count($exceptions)) { $rules .= "if (\$request_uri ~ \"(" . implode('|', $exceptions) . ")\") {\n"; $rules .= " break;\n"; $rules .= "}\n"; } $rules .= "if (\$request_uri ~* \\.(" . implode('|', $extensions) . ")$) {\n"; $rules .= " return 404;\n"; $rules .= "}\n"; $rules .= W3TC_MARKER_END_BROWSERCACHE_NO404WP . "\n"; return $rules; } /** * Returns the apache, nginx version * @return string */ private function _get_server_version() { $sig= explode('/', $_SERVER['SERVER_SOFTWARE']); $temp = isset($sig[1]) ? explode(' ', $sig[1]) : array('0'); $version = $temp[0]; return $version; } /** * Takes an array of extensions single per row and/or extensions delimited by | * @param $extensions * @param $ext * @return array */ private function _remove_extension_from_list($extensions, $ext) { for ($i = 0; $i < sizeof($extensions); $i++) { if ($extensions[$i] == $ext) { unset($extensions[$i]); return $extensions; } elseif (strpos($extensions[$i], $ext) !== false && strpos($extensions[$i], '|') !== false) { $exts = explode('|', $extensions[$i]); $key = array_search($ext, $exts); unset($exts[$key]); $extensions[$i] = implode('|', $exts); return $extensions; } } return $extensions; } }