_config = & W3_Config::instance(); } /** * PHP4 constructor * @return W3_Minify */ function W3_Minify() { $this->__construct(); } /** * Runs minify */ function process() { /** * Request variables: * * tt - theme name in format template:stylesheet * gg - template * g - location (include, include-footer, etc...) * t - type (js or css) */ if (isset($_GET['tt']) && isset($_GET['gg']) && isset($_GET['g']) && isset($_GET['t'])) { require_once W3TC_LIB_MINIFY_DIR . '/Minify.php'; require_once W3TC_LIB_MINIFY_DIR . '/HTTP/Encoder.php'; /** * Fix DOCUMENT_ROOT for minify */ $_SERVER['DOCUMENT_ROOT'] = w3_get_document_root(); HTTP_Encoder::$encodeToIe6 = true; Minify::$uploaderHoursBehind = $this->_config->get_integer('minify.fixtime'); Minify::setCache($this->_get_cache()); $browsercache = $this->_config->get_boolean('browsercache.enabled'); $serve_options = $this->_config->get_array('minify.options'); $serve_options['maxAge'] = $this->_config->get_integer('browsercache.cssjs.lifetime'); $serve_options['encodeOutput'] = ($browsercache && $this->_config->get_boolean('browsercache.cssjs.compression')); $serve_options['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') ); $serve_options['minApp']['groups'] = $this->get_groups($_GET['tt'], $_GET['gg'], $_GET['t']); if (stripos(PHP_OS, 'win') === 0) { Minify::setDocRoot(); } foreach ($this->_config->get_array('minify.symlinks') as $link => $target) { $link = str_replace('//', realpath($_SERVER['DOCUMENT_ROOT']), $link); $link = strtr($link, '/', DIRECTORY_SEPARATOR); $serve_options['minifierOptions']['text/css']['symlinks'][$link] = realpath($target); } if ($this->_config->get_boolean('minify.debug')) { $serve_options['debug'] = true; } if ($this->_config->get('minify.debug')) { require_once W3TC_LIB_MINIFY_DIR . '/Minify/Logger.php'; Minify_Logger::setLogger($this); } /** * Handle combine option */ switch (true) { case ($_GET['t'] == 'js' && $_GET['g'] == 'include') && $this->_config->get_boolean('minify.js.combine.header'): case ($_GET['t'] == 'js' && $_GET['g'] == 'include-nb') && $this->_config->get_boolean('minify.js.combine.header'): case ($_GET['t'] == 'js' && $_GET['g'] == 'include-body') && $this->_config->get_boolean('minify.js.combine.body'): case ($_GET['t'] == 'js' && $_GET['g'] == 'include-body-nb') && $this->_config->get_boolean('minify.js.combine.body'): case ($_GET['t'] == 'js' && $_GET['g'] == 'include-footer') && $this->_config->get_boolean('minify.js.combine.footer'): case ($_GET['t'] == 'js' && $_GET['g'] == 'include-footer-nb') && $this->_config->get_boolean('minify.js.combine.footer'): $serve_options['minifiers']['application/x-javascript'] = array( 'Minify_CombineOnly', 'minify' ); break; case ($_GET['t'] == 'css' && $_GET['g'] == 'include') && $this->_config->get_boolean('minify.css.combine'): $serve_options['minifiers']['text/css'] = array( 'Minify_CombineOnly', 'minify' ); break; default: $serve_options['postprocessor'] = array( &$this, 'postprocessor' ); } /** * Setup user-friendly cache ID for disk cache */ if ($this->_config->get_string('minify.engine') == 'file') { $id = $this->get_id($_GET['tt'], $_GET['gg'], $_GET['g'], $_GET['t']); $cacheId = sprintf('%s/%s.%s.%s.%s', $_GET['tt'], $_GET['gg'], $_GET['g'], $id, $_GET['t']); Minify::setCacheId($cacheId); } if ($this->_config->get_boolean('browsercache.enabled') && $this->_config->get_boolean('browsercache.cssjs.w3tc')) { @header('X-Powered-By: ' . W3TC_POWERED_BY); } try { Minify::serve('MinApp', $serve_options); } catch (Exception $exception) { printf('W3 Total Cache Error: Minify error: %s', $exception->getMessage()); } } else { die('This file cannot be accessed directly'); } } /** * Minify postprocessor * * @param string $content * @param string $type * @return string */ function postprocessor($content, $type) { switch ($type) { case 'text/css': if ($this->_config->get_boolean('minify.css.strip.comments')) { $content = preg_replace('~/\*.*\*/~Us', '', $content); } if ($this->_config->get_boolean('minify.css.strip.crlf')) { $content = preg_replace("~[\r\n]+~", ' ', $content); } else { $content = preg_replace("~[\r\n]+~", "\n", $content); } break; case 'application/x-javascript': if ($this->_config->get_boolean('minify.js.strip.comments')) { $content = preg_replace('~^//.*$~m', '', $content); $content = preg_replace('~/\*.*\*/~Us', '', $content); } if ($this->_config->get_boolean('minify.js.strip.crlf')) { $content = preg_replace("~[\r\n]+~", '', $content); } else { $content = preg_replace("~[\r\n]+~", "\n", $content); } break; } /** * Regenerate ID */ if (isset($_GET['tt']) && isset($_GET['gg']) && isset($_GET['g']) && isset($_GET['t'])) { $id = $this->_generate_id($_GET['tt'], $_GET['gg'], $_GET['g'], $_GET['t']); $this->_save_id($id, $_GET['tt'], $_GET['gg'], $_GET['g'], $_GET['t']); } return trim($content); } /** * Flushes cache */ function flush() { static $cache_path = null; $cache = & $this->_get_cache(); if (is_a($cache, 'W3_Cache_Memcached') && class_exists('Memcache')) { return $this->_memcached->flush(); } elseif (is_a($cache, 'Minify_Cache_APC') && function_exists('apc_clear_cache')) { return apc_clear_cache('user'); } elseif (is_a($cache, 'Minify_Cache_File')) { if (!@is_dir(W3TC_CACHE_FILE_MINIFY_DIR)) { $this->log(sprintf('Cache directory %s does not exists', W3TC_CACHE_FILE_MINIFY_DIR)); } return w3_emptydir(W3TC_CACHE_FILE_MINIFY_DIR, array( W3TC_CACHE_FILE_MINIFY_DIR . '/index.php', W3TC_CACHE_FILE_MINIFY_DIR . '/.htaccess' )); } return false; } /** * Returns onject instance * * @return W3_Minify */ function &instance() { static $instances = array(); if (!isset($instances[0])) { $class = __CLASS__; $instances[0] = & new $class(); } return $instances[0]; } /** * Log * * @param mixed $object * @param string $label */ function log($object, $label = null) { $data = sprintf("[%s] [%s] %s\n", date('r'), $_SERVER['REQUEST_URI'], $object); return @file_put_contents(W3TC_MINIFY_LOG_FILE, $data, FILE_APPEND); } /** * 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 = $this->_normalize_file($file); if (w3_is_url($file)) { $precached_file = $this->_precache_file($file, $type); if ($precached_file) { $result[$location][$file] = $precached_file; } else { $this->_handle_error($file); } } else { $file = urldecode($file); $path = w3_get_document_root() . '/' . $file; if (file_exists($path)) { $result[$location][$file] = '//' . $file; } else { $this->_handle_error($file); } } } } } return $result; } /** * Returns id of file * * @param string $theme * @param string $template * @param string $location * @param string $type * @return string */ function get_id($theme, $template, $location, $type) { $id = $this->_load_id($theme, $template, $location, $type); if (!$id) { $id = $this->_generate_id($theme, $template, $location, $type); $this->_save_id($id, $theme, $template, $location, $type); } return $id; } /** * Precaches external file * * @param string $url * @param string $type * @return string */ function _precache_file($url, $type) { $lifetime = $this->_config->get_integer('minify.lifetime'); $file_path = sprintf('%s/minify_%s.%s', W3TC_CACHE_FILE_MINIFY_DIR, md5($url), $type); $file_exists = file_exists($file_path); if ($file_exists && @filemtime($file_path) >= (time() - $lifetime)) { return $this->_get_minify_source($file_path, $url); } if (@is_dir(W3TC_CACHE_FILE_MINIFY_DIR)) { if (w3_download($url, $file_path) !== false) { return $this->_get_minify_source($file_path, $url); } else { $this->log(sprintf('Unable to download URL: %s', $url)); } } else { $this->log(sprintf('The cache directory %s does not exist', W3TC_CACHE_FILE_MINIFY_DIR)); } return ($file_exists ? $this->_get_minify_source($file_path, $url) : false); } /** * Normalizes URL * @param string $url * @return string */ function _normalize_file($file) { $file = preg_replace('~\?ver=[^&]+$~', '', $file); $file = w3_normalize_file_minify($file); $file = w3_translate_file($file); return $file; } /** * 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'; $this->_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($this->_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 'file': default: require_once W3TC_LIB_MINIFY_DIR . '/Minify/Cache/File.php'; if (!@is_dir(W3TC_CACHE_FILE_MINIFY_DIR)) { $this->log(sprintf('Cache directory %s does not exist', W3TC_CACHE_FILE_MINIFY_DIR)); } $cache[0] = & new Minify_Cache_File(W3TC_CACHE_FILE_MINIFY_DIR, $this->_config->get_boolean('minify.file.locking')); break; } } return $cache[0]; } /** * Handle minify error * * @param string file * @return void */ function _handle_error($file) { $notification = $this->_config->get_string('minify.error.notification'); if ($notification) { if (stristr($notification, 'admin') !== false) { $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_DIR . '/inc/email/minify_error_notification.html'); $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(120); $result = @wp_mail($to_email, 'W3 Total Cache Error Notification', $body, implode("\n", $headers)); return $result; } /** * Generates file ID * * @param string $theme * @param string $template * @param string $location * @param string $type * @return string */ function _generate_id($theme, $template, $location, $type) { $hash = ''; $files = 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; } $hash .= $path . @file_get_contents($path); } $id = abs(crc32($hash)); return $id; } /** * Returns key for ID data * * @param string $theme * @param string $template * @param string $location * @param string $type * @return string */ function _get_id_key($theme, $template, $location, $type) { $key = sprintf('%s/%s.%s.%s.id', $theme, $template, $location, $type); return $key; } /** * Returns cached ID * * @param string $theme * @param string $template * @param string $location * @param string $type * @return string */ function _load_id($theme, $template, $location, $type) { $key = $this->_get_id_key($theme, $template, $location, $type); $cache = & $this->_get_cache(); $id = @$cache->fetch($key); return $id; } /** * Cache ID * * @param string $id * @param string $theme * @param string $template * @param string $location * @param string $type * @return boolean */ function _save_id($id, $theme, $template, $location, $type) { $key = $this->_get_id_key($theme, $template, $location, $type); $cache = & $this->_get_cache(); return $cache->store($key, $id); } }