_config = & w3_instance('W3_Config'); } /** * PHP4 constructor */ function W3_Minify() { $this->__construct(); } /** * Runs minify * * @return void */ function process() { require_once W3TC_LIB_W3_DIR . '/Request.php'; /** * Check for rewrite test request */ $rewrite_test = W3_Request::get_boolean('w3tc_rewrite_test'); if ($rewrite_test) { echo 'OK'; exit(); } $file = W3_Request::get_string('file'); if (!$file) { $this->error('File param is missing', false); return; } $hash = ''; $matches = null; if (preg_match('~^([a-f0-9]+)\\.[a-f0-9]+\\.(css|js)$~', $file, $matches)) { list(, $hash, $type) = $matches; } elseif (preg_match('~^([a-f0-9]+)\\/(.+)\\.(include(\\-(footer|body))?(-nb)?)\\.[a-f0-9]+\\.(css|js)$~', $file, $matches)) { list(, $theme, $template, $location, , , , $type) = $matches; } else { $this->error(sprintf('Bad file param format: "%s"', $file), false); return; } require_once W3TC_LIB_MINIFY_DIR . '/Minify.php'; require_once W3TC_LIB_MINIFY_DIR . '/HTTP/Encoder.php'; /** * Fix DOCUMENT_ROOT */ $_SERVER['DOCUMENT_ROOT'] = w3_get_document_root(); /** * Set cache engine */ Minify::setCache($this->_get_cache()); /** * Set cache ID */ $cache_id = $this->get_cache_id($file); Minify::setCacheId($cache_id); /** * Set logger */ require_once W3TC_LIB_MINIFY_DIR . '/Minify/Logger.php'; Minify_Logger::setLogger(array( &$this, '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 && $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') ) )); /** * Set sources */ if ($hash) { $_GET['f'] = $this->get_files($hash, $type); } else { $_GET['g'] = $location; $serve_options['minApp']['groups'] = $this->get_groups($theme, $template, $type); } /** * Set minifier */ $w3_minifier = & w3_instance('W3_Minifier'); if ($type == 'js') { $minifier_type = 'application/x-javascript'; switch (true) { case (($hash || $location == 'include' || $location == 'include-nb') && $this->_config->get_boolean('minify.js.combine.header')): case (($location == 'include-body' || $location == 'include-body-nb') && $this->_config->get_boolean('minify.js.combine.body')): case (($location == 'include-footer' || $location == 'include-footer-nb') && $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 ($browsercache && $this->_config->get_boolean('browsercache.cssjs.w3tc')) { @header('X-Powered-By: ' . W3TC_POWERED_BY); } /** * Minify! */ try { Minify::serve('MinApp', $serve_options); } catch (Exception $exception) { $this->error($exception->getMessage()); } } /** * Flushes cache * * @return boolean */ function flush() { $cache = & $this->_get_cache(); return $cache->flush(); } /** * Log * * @param string $msg * @return bool */ function log($msg) { $data = sprintf("[%s] [%s] [%s] %s\n", date('r'), $_SERVER['REQUEST_URI'], (!empty($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '-'), $msg); return @file_put_contents(W3TC_MINIFY_LOG_FILE, $data, FILE_APPEND); } /** * Returns files array * * @param string $hash * @param string $type * @return string */ function get_files($hash, $type) { $files = $this->get_custom_files($hash, $type); $files = implode(',', $files); return $files; } /** * Returns minify groups * * @param string $theme * @param string $template * @param string $type * @return array */ function get_groups($theme, $template, $type) { $result = array(); switch ($type) { case 'css': $groups = $this->_config->get_array('minify.css.groups'); break; case 'js': $groups = $this->_config->get_array('minify.js.groups'); break; default: return $result; } if (isset($groups[$theme]['default'])) { $locations = (array) $groups[$theme]['default']; } else { $locations = array(); } if ($template != 'default' && isset($groups[$theme][$template])) { $locations = array_merge_recursive($locations, (array) $groups[$theme][$template]); } foreach ($locations as $location => $config) { if (!empty($config['files'])) { foreach ((array) $config['files'] as $file) { $file = w3_normalize_file_minify2($file); if (w3_is_url($file)) { $precached_file = $this->_precache_file($file, $type); if ($precached_file) { $result[$location][$file] = $precached_file; } else { $this->error(sprintf('Unable to cache remote file: "%s"', $file)); } } else { $path = w3_get_document_root() . '/' . $file; if (file_exists($path)) { $result[$location][$file] = '//' . $file; } else { $this->error(sprintf('File "%s" doesn\'t exist', $path)); } } } } } return $result; } /** * Returns minify cache ID * * @param string $file * @return string */ function get_cache_id($file) { if ($this->_config->get_string('minify.engine') == 'file') { $cache_id = $file; } else { $cache_id = sprintf('w3tc_%s_minify_%s', w3_get_host_id(), md5($file)); } return $cache_id; } /** * Returns array of group sources * * @param string $theme * @param string $template * @param string $location * @param string $type * @return array */ function get_sources_group($theme, $template, $location, $type) { $sources = array(); $groups = $this->get_groups($theme, $template, $type); if (isset($groups[$location])) { $files = (array) $groups[$location]; $document_root = w3_get_document_root(); foreach ($files as $file) { if (is_a($file, 'Minify_Source')) { $path = $file->filepath; } else { $path = $document_root . '/' . $file; } $sources[] = $path; } } return $sources; } /** * Returns array of custom sources * * @param string $hash * @param string $type * @return array */ function get_sources_custom($hash, $type) { $sources = array(); $files = $this->get_custom_files($hash, $type); if (count($files)) { $document_root = w3_get_document_root(); foreach ($files as $file) { $sources[] = $document_root . '/' . $file; } } return $sources; } /** * Returns ID key for group * * @param $theme * @param $template * @param $location * @param $type * @return string */ function get_id_key_group($theme, $template, $location, $type) { if ($this->_config->get_string('minify.engine') == 'file') { $key = sprintf('%s/%s.%s.%s.id', $theme, $template, $location, $type); } else { $key = sprintf('w3tc_%s_minify_id_%s', w3_get_host_id(), md5($theme . $template . $location . $type)); } return $key; } /** * Returns ID key for custom files * * @param string $hash * @param string $type * @return string */ function get_id_key_custom($hash, $type) { if ($this->_config->get_string('minify.engine') == 'file') { $key = sprintf('%s.%s.id', $hash, $type); } else { $key = sprintf('w3tc_%s_minify_id_%s', w3_get_host_id(), md5($hash . $type)); } return $key; } /** * Returns id for custom files * * @param string $hash * @param string $type * @return integer */ function get_id_custom($hash, $type) { $key = $this->get_id_key_custom($hash, $type); $id = $this->_cache_get($key); if ($id === false) { $sources = $this->get_sources_custom($hash, $type); if (count($sources)) { $id = $this->_generate_id($sources, $type); if ($id) { $this->_cache_set($key, $id); } } } return $id; } /** * Returns id for group * * @param string $theme * @param string $template * @param string $location * @param string $type * @return integer */ function get_id_group($theme, $template, $location, $type) { $key = $this->get_id_key_group($theme, $template, $location, $type); $id = $this->_cache_get($key); if ($id === false) { $sources = $this->get_sources_group($theme, $template, $location, $type); if (count($sources)) { $id = $this->_generate_id($sources, $type); if ($id) { $this->_cache_set($key, $id); } } } return $id; } /** * Returns custom files hash * * @param array $files * @return string */ function get_custom_files_hash($files) { return substr(md5(implode('', $files)), 0, 8); } /** * Returns custom files key * * @param string $hash * @param string $type * @return string */ function get_custom_files_key($hash, $type) { if ($this->_config->get_string('minify.engine') == 'file') { $key = sprintf('%s.%s.files', $hash, $type); } else { $key = sprintf('w3tc_%s_minify_files_%s', w3_get_host_id(), md5($hash . $type)); } return $key; } /** * Sets custom files * * @param array $files * @param string $type * @return bool */ function set_custom_files($files, $type) { $hash = $this->get_custom_files_hash($files); $key = $this->get_custom_files_key($hash, $type); return $this->_cache_set($key, $files); } /** * Returns custom files * * @param string $hash * @param string $type * @return array */ function get_custom_files($hash, $type) { $key = $this->get_custom_files_key($hash, $type); $files = $this->_cache_get($key); if ($files) { $files = array_map('w3_normalize_file_minify2', (array) $files); } else { $this->error(sprintf('Unable to fetch custom files list: "%s.%s"', $hash, $type), false, 404); } return $files; } /** * Sends error response * * @param string $error * @param boolean $handle * @param integer $status * @return void */ function error($error, $handle = true, $status = 400) { $debug = $this->_config->get_boolean('minify.debug'); if ($debug) { $this->log($error); } if ($handle) { $this->_handle_error($error); } if (defined('W3TC_IN_MINIFY')) { status_header($status); echo '
%s.
', $error); } else { echo 'Enable debug mode to see error message.
'; } die(); } } /** * Pre-caches external file * * @param string $url * @param string $type * @return string */ function _precache_file($url, $type) { $lifetime = $this->_config->get_integer('minify.lifetime'); $cache_path = sprintf('%s/minify_%s.%s', W3TC_CACHE_FILE_MINIFY_DIR, md5($url), $type); if (!file_exists($cache_path) || @filemtime($cache_path) < (time() - $lifetime)) { require_once W3TC_INC_DIR . '/functions/http.php'; w3_download($url, $cache_path); } return (file_exists($cache_path) ? $this->_get_minify_source($cache_path, $url) : false); } /** * Returns minify source * * @param $file_path * @param $url * @return Minify_Source */ function _get_minify_source($file_path, $url) { require_once W3TC_LIB_MINIFY_DIR . '/Minify/Source.php'; return new Minify_Source(array( 'filepath' => $file_path, 'minifyOptions' => array( 'prependRelativePath' => $url ) )); } /** * Returns minify cache object * * @return object */ function &_get_cache() { static $cache = array(); if (!isset($cache[0])) { switch ($this->_config->get_string('minify.engine')) { case 'memcached': require_once W3TC_LIB_W3_DIR . '/Cache/Memcached.php'; require_once W3TC_LIB_MINIFY_DIR . '/Minify/Cache/Memcache.php'; @$w3_cache_memcached = & new W3_Cache_Memcached(array( 'servers' => $this->_config->get_array('minify.memcached.servers'), 'persistant' => $this->_config->get_boolean('minify.memcached.persistant') )); @$cache[0] = & new Minify_Cache_Memcache($w3_cache_memcached); break; case 'apc': require_once W3TC_LIB_MINIFY_DIR . '/Minify/Cache/APC.php'; @$cache[0] = & new Minify_Cache_APC(); break; case 'eaccelerator': require_once W3TC_LIB_MINIFY_DIR . '/Minify/Cache/Eaccelerator.php'; @$cache[0] = & new Minify_Cache_Eaccelerator(); break; case 'xcache': require_once W3TC_LIB_MINIFY_DIR . '/Minify/Cache/XCache.php'; @$cache[0] = & new Minify_Cache_XCache(); break; case 'wincache': require_once W3TC_LIB_MINIFY_DIR . '/Minify/Cache/Wincache.php'; @$cache[0] = & new Minify_Cache_Wincache(); break; case 'file': default: require_once W3TC_LIB_MINIFY_DIR . '/Minify/Cache/File.php'; @$cache[0] = & new Minify_Cache_File( W3TC_CACHE_FILE_MINIFY_DIR, array( '.htaccess', 'index.php' ), $this->_config->get_boolean('minify.file.locking'), $this->_config->get_integer('timelimit.cache_flush') ); break; } } return $cache[0]; } /** * Handle minify error * * @param string $error * @return void */ function _handle_error($error) { $notification = $this->_config->get_string('minify.error.notification'); if ($notification) { if (stristr($notification, 'admin') !== false) { $this->_config->set('minify.error.last', $error); $this->_config->set('notes.minify_error', true); } if (stristr($notification, 'email') !== false) { $last = $this->_config->get_integer('minify.error.notification.last'); /** * Prevent email flood: send email every 5 min */ if ((time() - $last) > 300) { $this->_config->set('minify.error.notification.last', time()); $this->_send_notification(); } } $this->_config->save(); } } /** * Send E-mail notification when error occured * * @return boolean */ function _send_notification() { $from_email = 'wordpress@' . w3_get_domain($_SERVER['SERVER_NAME']); $from_name = get_option('blogname'); $to_name = $to_email = get_option('admin_email'); $body = @file_get_contents(W3TC_INC_DIR . '/email/minify_error_notification.php'); $headers = array( sprintf('From: "%s" <%s>', addslashes($from_name), $from_email), sprintf('Reply-To: "%s" <%s>', addslashes($to_name), $to_email), 'Content-Type: text/html; charset=UTF-8' ); @set_time_limit($this->_config->get_integer('timelimit.email_send')); $result = @wp_mail($to_email, 'W3 Total Cache Error Notification', $body, implode("\n", $headers)); return $result; } /** * Generates file ID * * @param array $sources * @param string $type * @return string */ function _generate_id($sources, $type) { $values = $sources; foreach ($sources as $source) { if (file_exists($source)) { $data = @file_get_contents($source); if ($data !== false) { $values[] = md5($data); } else { return false; } } } $keys = array( 'minify.debug', 'minify.engine', 'minify.options', 'minify.symlinks', ); if ($type == 'js') { $engine = $this->_config->get_string('minify.js.engine'); switch ($engine) { case 'js': $keys = array_merge($keys, array( 'minify.js.combine.header', 'minify.js.combine.body', 'minify.js.combine.footer', 'minify.js.strip.comments', 'minify.js.strip.crlf', )); break; case 'yuijs': $keys = array_merge($keys, array( 'minify.yuijs.options.line-break', 'minify.yuijs.options.nomunge', 'minify.yuijs.options.preserve-semi', 'minify.yuijs.options.disable-optimizations', )); break; case 'ccjs': $keys = array_merge($keys, array( 'minify.ccjs.options.compilation_level', 'minify.ccjs.options.formatting', )); break; } } elseif ($type == 'css') { $engine = $this->_config->get_string('minify.css.engine'); switch ($engine) { case 'css': $keys = array_merge($keys, array( 'minify.css.combine', 'minify.css.strip.comments', 'minify.css.strip.crlf', 'minify.css.imports', )); break; case 'yuicss': $keys = array_merge($keys, array( 'minify.yuicss.options.line-break', )); break; case 'csstidy': $keys = array_merge($keys, array( 'minify.csstidy.options.remove_bslash', 'minify.csstidy.options.compress_colors', 'minify.csstidy.options.compress_font-weight', 'minify.csstidy.options.lowercase_s', 'minify.csstidy.options.optimise_shorthands', 'minify.csstidy.options.remove_last_;', 'minify.csstidy.options.case_properties', 'minify.csstidy.options.sort_properties', 'minify.csstidy.options.sort_selectors', 'minify.csstidy.options.merge_selectors', 'minify.csstidy.options.discard_invalid_properties', 'minify.csstidy.options.css_level', 'minify.csstidy.options.preserve_css', 'minify.csstidy.options.timestamp', 'minify.csstidy.options.template', )); break; } } foreach ($keys as $key) { $values[] = $this->_config->get($key); } $id = substr(md5(implode('', $values)), 0, 6); return $id; } /** * Returns cache data * * @param string $key * @return string */ function _cache_get($key) { $cache =& $this->_get_cache(); $data = $cache->fetch($key); if ($data) { $value = @unserialize($data); return $value; } return false; } /** * Sets cache date * * @param string $key * @param string $value * @return boolean */ function _cache_set($key, $value) { $cache =& $this->_get_cache(); return $cache->store($key, serialize($value)); } }