/** * Generates directives for file cache dir * * @param Config $config * @return string */ private function rules_cache_generate_nginx($config) { $cache_dir = Util_Rule::filename_to_uri(W3TC_CACHE_MINIFY_DIR); $browsercache = $config->get_boolean('browsercache.enabled'); $compression = $browsercache && $config->get_boolean('browsercache.cssjs.compression'); $expires = $browsercache && $config->get_boolean('browsercache.cssjs.expires'); $lifetime = $browsercache ? $config->get_integer('browsercache.cssjs.lifetime') : 0; $cache_control = $browsercache && $config->get_boolean('browsercache.cssjs.cache.control'); $w3tc = $browsercache && $config->get_integer('browsercache.cssjs.w3tc'); $rules = ''; $rules .= W3TC_MARKER_BEGIN_MINIFY_CACHE . "\n"; $common_rules = ''; if ($expires) { $common_rules .= " expires modified " . $lifetime . "s;\n"; } if ($w3tc) { $common_rules .= " add_header X-Powered-By \"" . Util_Environment::w3tc_header($config) . "\";\n"; } if ($compression) { $common_rules .= " add_header Vary \"Accept-Encoding\";\n"; } if ($cache_control) { $cache_policy = $config->get_string('browsercache.cssjs.cache.policy'); switch ($cache_policy) { case 'cache': $common_rules .= " add_header Pragma \"public\";\n"; $common_rules .= " add_header Cache-Control \"public\";\n"; break; case 'cache_public_maxage': $common_rules .= " add_header Pragma \"public\";\n"; $common_rules .= " add_header Cache-Control \"max-age=" . $lifetime . ", public\";\n"; break; case 'cache_validation': $common_rules .= " add_header Pragma \"public\";\n"; $common_rules .= " add_header Cache-Control \"public, must-revalidate, proxy-revalidate\";\n"; break; case 'cache_noproxy': $common_rules .= " add_header Pragma \"public\";\n"; $common_rules .= " add_header Cache-Control \"private, must-revalidate\";\n"; break; case 'cache_maxage': $common_rules .= " add_header Pragma \"public\";\n"; $common_rules .= " add_header Cache-Control \"max-age=" . $lifetime . ", public, must-revalidate, proxy-revalidate\";\n"; break; case 'no_cache': $common_rules .= " add_header Pragma \"no-cache\";\n"; $common_rules .= " add_header Cache-Control \"max-age=0, private, no-store, no-cache, must-revalidate\";\n"; break; } } $rules .= "location ~ " . $cache_dir . ".*\\.js\$ {\n"; $rules .= " types {}\n"; $rules .= " default_type application/x-javascript;\n"; $rules .= $common_rules; $rules .= "}\n"; $rules .= "location ~ " . $cache_dir . ".*\\.css\$ {\n"; $rules .= " types {}\n"; $rules .= " default_type text/css;\n"; $rules .= $common_rules; $rules .= "}\n"; if ($compression) { $rules .= "location ~ " . $cache_dir . ".*js\\.gzip\$ {\n"; $rules .= " gzip off;\n"; $rules .= " types {}\n"; $rules .= " default_type application/x-javascript;\n"; $rules .= $common_rules; $rules .= " add_header Content-Encoding gzip;\n"; $rules .= "}\n"; $rules .= "location ~ " . $cache_dir . ".*css\\.gzip\$ {\n"; $rules .= " gzip off;\n"; $rules .= " types {}\n"; $rules .= " default_type text/css;\n"; $rules .= $common_rules; $rules .= " add_header Content-Encoding gzip;\n"; $rules .= "}\n"; } $rules .= W3TC_MARKER_END_MINIFY_CACHE . "\n"; return $rules; }
/** * Runs minify * * @param string|null $file * * @return void */ function process($file = NULL, $quiet = false) { /** * Check for rewrite test request */ $rewrite_marker = 'rewrite_test.css'; if (substr($file, strlen($file) - strlen($rewrite_marker)) == $rewrite_marker) { echo 'Minify OK'; exit; } $filelength_test_marker = 'XXX.css'; if (substr($file, strlen($file) - strlen($filelength_test_marker)) == $filelength_test_marker) { $cache = $this->_get_cache(); header('Content-type: text/css'); if (!$cache->store(basename($file), array('content' => 'content ok'))) { echo 'error storing'; } else { if (function_exists('gzencode') && $this->_config->get_boolean('browsercache.enabled') && $this->_config->get_boolean('browsercache.cssjs.compression')) { if (!$cache->store(basename($file) . '.gzip', array('content' => gzencode('content ok')))) { echo 'error storing'; exit; } } $v = $cache->fetch(basename($file)); if ($v['content'] == 'content ok') { echo 'content ok'; } else { echo 'error storing'; } } exit; } // remove querystring if (preg_match('~(.+)(\\?x[0-9]{5})$~', $file, $m)) { $file = $m[1]; } // remove blog_id $levels = ''; if (defined('W3TC_BLOG_LEVELS')) { for ($n = 0; $n < W3TC_BLOG_LEVELS; $n++) { $levels .= '[0-9]+\\/'; } } if (preg_match('~^(' . $levels . '[0-9]+)\\/(.+)$~', $file, $matches)) { $file = $matches[2]; } // normalize according to browsercache $file = Dispatcher::requested_minify_filename($this->_config, $file); // parse file $hash = ''; $matches = null; $location = ''; $type = ''; if (preg_match('~^' . MINIFY_AUTO_FILENAME_REGEX . '$~', $file, $matches)) { list(, $hash, $type) = $matches; } elseif (preg_match('~^' . MINIFY_MANUAL_FILENAME_REGEX . '$~', $file, $matches)) { list(, $theme, $template, $location, , , $type) = $matches; } else { return $this->finish_with_error(sprintf('Bad file param format: "%s"', $file), $quiet, false); } /** * Set cache engine */ $cache = $this->_get_cache(); \Minify0_Minify::setCache($cache); /** * Set cache ID */ $cache_id = $this->get_cache_id($file); \Minify0_Minify::setCacheId($file); /** * Set logger */ \Minify_Logger::setLogger(array($this, 'debug_error')); /** * Set options */ $browsercache = $this->_config->get_boolean('browsercache.enabled'); $serve_options = array_merge($this->_config->get_array('minify.options'), array('debug' => $this->_config->get_boolean('minify.debug'), 'maxAge' => $this->_config->get_integer('browsercache.cssjs.lifetime'), 'encodeOutput' => $browsercache && !defined('W3TC_PAGECACHE_OUTPUT_COMPRESSION_OFF') && !$quiet && $this->_config->get_boolean('browsercache.cssjs.compression'), 'bubbleCssImports' => $this->_config->get_string('minify.css.imports') == 'bubble', 'processCssImports' => $this->_config->get_string('minify.css.imports') == 'process', 'cacheHeaders' => array('use_etag' => $browsercache && $this->_config->get_boolean('browsercache.cssjs.etag'), 'expires_enabled' => $browsercache && $this->_config->get_boolean('browsercache.cssjs.expires'), 'cacheheaders_enabled' => $browsercache && $this->_config->get_boolean('browsercache.cssjs.cache.control'), 'cacheheaders' => $this->_config->get_string('browsercache.cssjs.cache.policy')), 'quiet' => $quiet)); /** * Set sources */ if ($hash) { $_GET['f_array'] = $this->minify_filename_to_filenames_for_minification($hash, $type); $_GET['ext'] = $type; } else { $_GET['g'] = $location; $serve_options['minApp']['groups'] = $this->get_groups($theme, $template, $type); } /** * Set minifier */ $w3_minifier = Dispatcher::component('Minify_ContentMinifier'); if ($type == 'js') { $minifier_type = 'application/x-javascript'; switch (true) { case ($hash || $location == 'include') && $this->_config->get_boolean('minify.js.combine.header'): case $location == 'include-body' && $this->_config->get_boolean('minify.js.combine.body'): case $location == 'include-footer' && $this->_config->get_boolean('minify.js.combine.footer'): $engine = 'combinejs'; break; default: $engine = $this->_config->get_string('minify.js.engine'); if (!$w3_minifier->exists($engine) || !$w3_minifier->available($engine)) { $engine = 'js'; } break; } } elseif ($type == 'css') { $minifier_type = 'text/css'; if (($hash || $location == 'include') && $this->_config->get_boolean('minify.css.combine')) { $engine = 'combinecss'; } else { $engine = $this->_config->get_string('minify.css.engine'); if (!$w3_minifier->exists($engine) || !$w3_minifier->available($engine)) { $engine = 'css'; } } } /** * Initialize minifier */ $w3_minifier->init($engine); $serve_options['minifiers'][$minifier_type] = $w3_minifier->get_minifier($engine); $serve_options['minifierOptions'][$minifier_type] = $w3_minifier->get_options($engine); /** * Send X-Powered-By header */ if (!$quiet && $browsercache && $this->_config->get_boolean('browsercache.cssjs.w3tc')) { @header('X-Powered-By: ' . Util_Environment::w3tc_header()); } if (empty($_GET['f_array']) && empty($_GET['g'])) { return $this->finish_with_error('Nothing to minify', $quiet, false); } /** * Minify! */ $return = array(); try { $return = \Minify0_Minify::serve('MinApp', $serve_options); } catch (\Exception $exception) { return $this->finish_with_error($exception->getMessage(), $quiet); } if (!is_null(\Minify0_Minify::$recoverableError)) { $this->_handle_error(\Minify0_Minify::$recoverableError); } $state = Dispatcher::config_state_master(); if (!$this->_error_occurred && $state->get_boolean('minify.show_note_minify_error')) { $error_file = $state->get_string('minify.error.file'); if ($error_file == $file) { $state->set('minify.show_note_minify_error', false); $state->save(); } } return $return; }
/** * Returns headers for file * * @param array $file CDN file array * @return array */ function _get_headers($file, $block_expires = false) { $local_path = $file['local_path']; $mime_type = Util_Mime::get_mime_type($local_path); $last_modified = time(); $link = $file['original_url']; $headers = array('Content-Type' => $mime_type, 'Last-Modified' => Util_Content::http_date($last_modified), 'Access-Control-Allow-Origin' => '*', 'Link' => '<' . $link . '>; rel="canonical"'); if (isset($this->cache_config[$mime_type])) { if ($this->cache_config[$mime_type]['etag']) { $headers['ETag'] = '"' . @md5_file($local_path) . '"'; } if ($this->cache_config[$mime_type]['w3tc']) { $headers['X-Powered-By'] = Util_Environment::w3tc_header($this->_config); } $expires_set = false; if (!$block_expires && $this->cache_config[$mime_type]['expires']) { $headers['Expires'] = Util_Content::http_date(time() + $this->cache_config[$mime_type]['lifetime']); $expires_set = true; } switch ($this->cache_config[$mime_type]['cache_control']) { case 'cache': $headers = array_merge($headers, array('Pragma' => 'public', 'Cache-Control' => 'public')); break; case 'cache_public_maxage': $headers = array_merge($headers, array('Pragma' => 'public', 'Cache-Control' => ($expires_set ? '' : 'max-age=' . $this->cache_config[$mime_type]['lifetime'] . ', ') . 'public')); break; case 'cache_validation': $headers = array_merge($headers, array('Pragma' => 'public', 'Cache-Control' => 'public, must-revalidate, proxy-revalidate')); break; case 'cache_noproxy': $headers = array_merge($headers, array('Pragma' => 'public', 'Cache-Control' => 'private, must-revalidate')); break; case 'cache_maxage': $headers = array_merge($headers, array('Pragma' => 'public', 'Cache-Control' => ($expires_set ? '' : 'max-age=' . $this->cache_config[$mime_type]['lifetime'] . ', ') . 'public, must-revalidate, proxy-revalidate')); break; case 'no_cache': $headers = array_merge($headers, array('Pragma' => 'no-cache', 'Cache-Control' => 'max-age=0, private, no-store, no-cache, must-revalidate')); break; } } return $headers; }
/** * Adds cache rules for type to &$rules * * @param Config $config * @param string $rules * @param array $mime_types * @param string $section * @return void */ private function _rules_cache_generate_nginx_for_type($config, &$rules, $mime_types, $section) { $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"; } $add_header_rules = ''; if ($cache_control) { $cache_policy = $config->get_string('browsercache.' . $section . '.cache.policy'); switch ($cache_policy) { case 'cache': $add_header_rules .= " add_header Pragma \"public\";\n"; $add_header_rules .= " add_header Cache-Control \"public\";\n"; break; case 'cache_public_maxage': $add_header_rules .= " add_header Pragma \"public\";\n"; $add_header_rules .= " add_header Cache-Control \"max-age=" . $lifetime . ", public\";\n"; break; case 'cache_validation': $add_header_rules .= " add_header Pragma \"public\";\n"; $add_header_rules .= " add_header Cache-Control \"public, must-revalidate, proxy-revalidate\";\n"; break; case 'cache_noproxy': $add_header_rules .= " add_header Pragma \"public\";\n"; $add_header_rules .= " add_header Cache-Control \"private, must-revalidate\";\n"; break; case 'cache_maxage': $add_header_rules .= " add_header Pragma \"public\";\n"; $add_header_rules .= " add_header Cache-Control \"max-age=" . $lifetime . ", public, must-revalidate, proxy-revalidate\";\n"; break; case 'no_cache': $add_header_rules .= " add_header Pragma \"no-cache\";\n"; $add_header_rules .= " add_header Cache-Control \"max-age=0, private, no-store, no-cache, must-revalidate\";\n"; break; } } if ($w3tc) { $add_header_rules .= " add_header X-Powered-By \"" . Util_Environment::w3tc_header($config) . "\";\n"; } $rules .= $add_header_rules; $rules .= Dispatcher::on_browsercache_rules_generation_for_section($config, false, $section, $add_header_rules); if (!$config->get_boolean('browsercache.no404wp')) { $wp_uri = network_home_url('', 'relative'); $wp_uri = rtrim($wp_uri, '/'); $rules .= ' try_files $uri $uri/ $uri.html ' . $wp_uri . '/index.php?$args;' . "\n"; } $rules .= "}\n"; } }
/** * Generates directives for file cache dir * * @param Config $config * @return string */ private function rules_cache_generate_nginx($config) { $cache_root = Util_Environment::normalize_path(W3TC_CACHE_PAGE_ENHANCED_DIR); $cache_dir = rtrim(str_replace(Util_Environment::document_root(), '', $cache_root), '/'); if (Util_Environment::is_wpmu()) { $cache_dir = preg_replace('~/w3tc.*?/~', '/w3tc.*?/', $cache_dir, 1); } $browsercache = $config->get_boolean('browsercache.enabled'); $compression = $browsercache && $config->get_boolean('browsercache.html.compression'); $expires = $browsercache && $config->get_boolean('browsercache.html.expires'); $lifetime = $browsercache ? $config->get_integer('browsercache.html.lifetime') : 0; $cache_control = $browsercache && $config->get_boolean('browsercache.html.cache.control'); $w3tc = $browsercache && $config->get_integer('browsercache.html.w3tc'); $common_rules = ''; if ($expires) { $common_rules .= " expires modified " . $lifetime . "s;\n"; } if ($w3tc) { $common_rules .= " add_header X-Powered-By \"" . Util_Environment::w3tc_header($config) . "\";\n"; } if ($expires) { $common_rules .= " add_header Vary \"Accept-Encoding, Cookie\";\n"; } if ($cache_control) { $cache_policy = $config->get_string('browsercache.html.cache.policy'); switch ($cache_policy) { case 'cache': $common_rules .= " add_header Pragma \"public\";\n"; $common_rules .= " add_header Cache-Control \"public\";\n"; break; case 'cache_public_maxage': $common_rules .= " add_header Pragma \"public\";\n"; $common_rules .= " add_header Cache-Control \"max-age=" . $lifetime . ", public\";\n"; break; case 'cache_validation': $common_rules .= " add_header Pragma \"public\";\n"; $common_rules .= " add_header Cache-Control \"public, must-revalidate, proxy-revalidate\";\n"; break; case 'cache_noproxy': $common_rules .= " add_header Pragma \"public\";\n"; $common_rules .= " add_header Cache-Control \"private, must-revalidate\";\n"; break; case 'cache_maxage': $common_rules .= " add_header Pragma \"public\";\n"; $common_rules .= " add_header Cache-Control \"max-age=" . $lifetime . ", public, must-revalidate, proxy-revalidate\";\n"; break; case 'no_cache': $common_rules .= " add_header Pragma \"no-cache\";\n"; $common_rules .= " add_header Cache-Control \"max-age=0, private, no-store, no-cache, must-revalidate\";\n"; break; } } $rules = ''; $rules .= W3TC_MARKER_BEGIN_PGCACHE_CACHE . "\n"; $rules .= "location ~ " . $cache_dir . ".*html\$ {\n"; $rules .= $common_rules; $rules .= "}\n"; if ($compression) { $rules .= "location ~ " . $cache_dir . ".*gzip\$ {\n"; $rules .= " gzip off;\n"; $rules .= " types {}\n"; $rules .= " default_type text/html;\n"; $rules .= $common_rules; $rules .= " add_header Content-Encoding gzip;\n"; $rules .= "}\n"; } $rules .= W3TC_MARKER_END_PGCACHE_CACHE . "\n"; return $rules; }
/** * Send headers */ function send_headers() { @header('X-Powered-By: ' . Util_Environment::w3tc_header()); }
/** * Sends headers * * @param boolean $is_404 * @param string $etag * @param integer $time * @param string $compression * @param array $custom_headers * @return boolean */ function _send_headers($is_404, $time, $etag, $compression, $custom_headers = array()) { $exit = false; $headers = is_array($custom_headers) ? $custom_headers : array(); $curr_time = time(); $bc_lifetime = $this->_config->get_integer('browsercache.html.lifetime'); $expires = (is_null($time) ? $curr_time : $time) + $bc_lifetime; $max_age = $expires > $curr_time ? $expires - $curr_time : 0; if ($is_404) { /** * Add 404 header */ $headers = array_merge($headers, array('Status' => 'HTTP/1.1 404 Not Found')); } elseif (!is_null($time) && $this->_check_modified_since($time) || $this->_check_match($etag)) { /** * Add 304 header */ $headers = array_merge($headers, array('Status' => 'HTTP/1.1 304 Not Modified')); /** * Don't send content if it isn't modified */ $exit = true; } if ($this->_config->get_boolean('browsercache.enabled')) { if ($this->_config->get_boolean('browsercache.html.last_modified')) { $headers = array_merge($headers, array('Last-Modified' => Util_Content::http_date($time))); } if ($this->_config->get_boolean('browsercache.html.expires')) { $headers = array_merge($headers, array('Expires' => Util_Content::http_date($expires))); } if ($this->_config->get_boolean('browsercache.html.cache.control')) { switch ($this->_config->get_string('browsercache.html.cache.policy')) { case 'cache': $headers = array_merge($headers, array('Pragma' => 'public', 'Cache-Control' => 'public')); break; case 'cache_public_maxage': $headers = array_merge($headers, array('Pragma' => 'public', 'Cache-Control' => sprintf('max-age=%d, public', $max_age))); break; case 'cache_validation': $headers = array_merge($headers, array('Pragma' => 'public', 'Cache-Control' => 'public, must-revalidate, proxy-revalidate')); break; case 'cache_noproxy': $headers = array_merge($headers, array('Pragma' => 'public', 'Cache-Control' => 'private, must-revalidate')); break; case 'cache_maxage': $headers = array_merge($headers, array('Pragma' => 'public', 'Cache-Control' => sprintf('max-age=%d, public, must-revalidate, proxy-revalidate', $max_age))); break; case 'no_cache': $headers = array_merge($headers, array('Pragma' => 'no-cache', 'Cache-Control' => 'max-age=0, private, no-store, no-cache, must-revalidate')); break; } } if ($this->_config->get_boolean('browsercache.html.etag')) { $headers = array_merge($headers, array('ETag' => '"' . $etag . '"')); } if ($this->_config->get_boolean('browsercache.html.w3tc')) { $headers = array_merge($headers, array('X-Powered-By' => Util_Environment::w3tc_header())); } } $vary = ''; //compressed && UAG if ($compression && $this->_get_mobile_group()) { $vary = 'Accept-Encoding,User-Agent,Cookie'; $headers = array_merge($headers, array('Content-Encoding' => $compression)); //compressed } elseif ($compression) { $vary = 'Accept-Encoding'; $headers = array_merge($headers, array('Content-Encoding' => $compression)); //uncompressed && UAG } elseif ($this->_get_mobile_group()) { $vary = 'User-Agent,Cookie'; } //Add Cookie to vary if user logged in and not previously set if (!$this->_check_logged_in() && strpos($vary, 'Cookie') === false) { if ($vary) { $vary .= ',Cookie'; } else { $vary = 'Cookie'; } } /** * Add vary header */ if ($vary) { $headers = array_merge($headers, array('Vary' => $vary)); } /** * Disable caching for preview mode */ if (Util_Environment::is_preview_mode()) { $headers = array_merge($headers, array('Pragma' => 'private', 'Cache-Control' => 'private')); } /** * Send headers to client */ $result = $this->_headers($headers); if ($exit) { exit; } return $result; }
/** * Send headers */ function send_headers() { @header('X-Powered-By: ' . Util_Environment::w3tc_header($this->_config)); }