From 917ffedadebf5c79c533517de5768666a7568515 Mon Sep 17 00:00:00 2001 From: secretr Date: Fri, 2 Oct 2009 13:46:26 +0000 Subject: [PATCH] Great Javascript server & browser cache control - more performance, less server CPU and site traffic --- e107_files/e_jslib.php | 265 ++++++++++++++++++-------------- e107_handlers/js_manager.php | 67 ++++++-- e107_handlers/jslib_handler.php | 120 ++++++++++----- 3 files changed, 282 insertions(+), 170 deletions(-) diff --git a/e107_files/e_jslib.php b/e107_files/e_jslib.php index f14605804..da9da2aa9 100644 --- a/e107_files/e_jslib.php +++ b/e107_files/e_jslib.php @@ -1,137 +1,168 @@ - 1, 'no_online' => 1, 'no_menus' => 1, 'no_prunetmp' => 1); - $_E107['minimal'] = true; - +//$_E107 = array('no_forceuserupdate' => 1, 'no_online' => 1, 'no_menus' => 1, 'no_prunetmp' => 1); +$_E107['minimal'] = true; + //admin or front-end call - if(strpos($_SERVER['QUERY_STRING'], '_admin') !== FALSE) - { - define('ADMIN_AREA', true); //force admin area - } - else - { - define('USER_AREA', true); //force user area - } - +if (strpos($_SERVER['QUERY_STRING'], '_admin') !== FALSE) +{ + define('ADMIN_AREA', true); //force admin area +} +else +{ + define('USER_AREA', true); //force user area +} + //call jslib handler, render content - require_once("../class2.php"); - require_once(e_HANDLER.'jslib_handler.php'); - $jslib = new e_jslib(); - $jslib->core_run(); - - exit; +require_once ("../class2.php"); +require_once (e_HANDLER.'jslib_handler.php'); +$jslib = new e_jslib(); +$jslib->core_run(); + +exit; + +// +// FUNCTIONS required for retrieveing cache without e107 API +// - /** - * FUNCTIONS required for retrieveing cache without e107 API - * - */ - - /** - * Output cache file contents if available (doesn't require e107 API) - * - */ - function e_jslib_cache_out() { - $encoding = e_jslib_browser_enc(); - $cacheFile = e_jslib_is_cache($encoding); - - if($cacheFile) { - while (@ob_end_clean()); // kill all output buffering for better performance - - header("Last-modified: " . gmdate("D, d M Y H:i:s",mktime(0,0,0,15,2,2004)) . " GMT"); - header('Content-type: text/javascript', TRUE); - if($encoding) - header('Content-Encoding: '.$encoding); - - echo @file_get_contents($cacheFile); - //TODO - log - //@file_put_contents('cache/e_jslib_log', "----------\ncache used - ".$cacheFile."\n\n", FILE_APPEND); - exit; - } - } - - /** - * Check jslib cache (doesn't require e107 API) - * - * @param string $encoding browser accepted encoding - * @return mixed cache filename on success or false otherwise - */ - function e_jslib_is_cache($encoding) { - - $cacheFile = e_jslib_cache_file($encoding); - $mAge = 24 * 60; +/** + * Output cache file contents if available (doesn't require e107 API) + * + * @return void + */ +function e_jslib_cache_out() +{ + $encoding = e_jslib_browser_enc(); //NOTE - should be called first + $cacheFile = e_jslib_is_cache($encoding); + + if ($cacheFile) + { + if (function_exists('date_default_timezone_set')) + { + date_default_timezone_set('UTC'); + } - if(is_file($cacheFile) && is_readable($cacheFile)) { + // last modification time + $lmodified = filemtime($cacheFile); + + // not modified - send 304 and exit + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $lmodified) + { + header("HTTP/1.1 304 Not Modified", true); + exit; + } + + // send last modified date + header('Cache-Control: must-revalidate'); + header('Last-modified: '.gmdate('r', $lmodified), true); + + // send content type and encoding + header('Content-type: text/javascript', true); + if ($encoding) + { + header('Content-Encoding: '.$encoding, true); + } + + // Expire header - 1 year + $time = time()+ 365 * 86400; + header('Expires: '.gmdate('r', $time), true); + + //kill any output buffering - better performance + while (@ob_end_clean()); + + echo @file_get_contents($cacheFile); + //TODO - debug + //@file_put_contents('cache/e_jslib_log', "----------\ncache used - ".$cacheFile."\n\n", FILE_APPEND); + exit; + } +} - if ((@filemtime($cacheFile) + ($mAge * 60)) < time()) { - unlink($cacheFile); - return false; - } - - return $cacheFile; - } - - return false; - } - - /** - * Detect browser accepted encoding (doesn't require e107 API) - * - * @return string encoding - */ - function e_jslib_browser_enc() { - - //double-compression fix - thanks Topper - if( headers_sent() || ini_get('zlib.output_compression') || !isset($_SERVER["HTTP_ACCEPT_ENCODING"]) ){ - - $encoding = ''; - } elseif ( strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'x-gzip') !== false ){ - - $encoding = 'x-gzip'; - } elseif ( strpos($_SERVER["HTTP_ACCEPT_ENCODING"],'gzip') !== false ){ - - $encoding = 'gzip'; - } else { - - $encoding = ''; - } - - return $encoding; - } - - /** - * Creates cache filename (doesn't require e107 API) - * - * @param string $encoding - * @return string cache filename - */ - function e_jslib_cache_file($encoding='') { - - $cacheDir = './cache/'; - $hash = $_SERVER['QUERY_STRING'] ? md5($_SERVER['QUERY_STRING']) : 'nomd5'; - $cacheFile = $cacheDir.'S_e_jslib'.($encoding ? '_'.$encoding : '').'_'.$hash.'.cache.php'; - - return $cacheFile; - } +/** + * Check jslib cache (doesn't require e107 API) + * + * @param string $encoding browser accepted encoding + * @return string cache filename on success or empty string otherwise + */ +function e_jslib_is_cache($encoding) +{ + $cacheFile = e_jslib_cache_filename($encoding); + if (is_file($cacheFile) && is_readable($cacheFile)) + { + return $cacheFile; + } + + return ''; +} + +/** + * Detect browser accepted encoding (doesn't require e107 API) + * It'll always return empty string if '_nogzip' found in QUERY_STRING + * + * @return string encoding + */ +function e_jslib_browser_enc() +{ + //NEW - option to disable completely gzip compression + if(strpos($_SERVER['QUERY_STRING'], '_nogzip') !== false) + { + return ''; + } + //double-compression fix - thanks Topper + if (headers_sent() || ini_get('zlib.output_compression') || !isset($_SERVER["HTTP_ACCEPT_ENCODING"])) + { + $encoding = ''; + } + elseif (strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'x-gzip') !== false) + { + $encoding = 'x-gzip'; + } + elseif (strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'gzip') !== false) + { + $encoding = 'gzip'; + } + else + { + $encoding = ''; + } + + return $encoding; +} + +/** + * Creates cache filename (doesn't require e107 API) + * + * @param string $encoding + * @return string cache filename + */ +function e_jslib_cache_filename($encoding = '') +{ + + $cacheDir = './cache/'; + $hash = $_SERVER['QUERY_STRING'] && $_SERVER['QUERY_STRING'] !== '_nogzip' ? md5(str_replace('_nogzip', '', $_SERVER['QUERY_STRING'])) : 'nomd5'; + $cacheFile = $cacheDir.'S_e_jslib'.($encoding ? '_'.$encoding : '').'_'.$hash.'.cache.php'; + + return $cacheFile; +} ?> \ No newline at end of file diff --git a/e107_handlers/js_manager.php b/e107_handlers/js_manager.php index 69443056f..7e6d2cabc 100644 --- a/e107_handlers/js_manager.php +++ b/e107_handlers/js_manager.php @@ -7,8 +7,8 @@ * GNU General Public License (http://gnu.org). * * $Source: /cvs_backup/e107_0.8/e107_handlers/js_manager.php,v $ - * $Revision: 1.3 $ - * $Date: 2009-10-01 15:05:41 $ + * $Revision: 1.4 $ + * $Date: 2009-10-02 13:46:25 $ * $Author: secretr $ * */ @@ -85,7 +85,12 @@ class e_jsmanager * * @var integer */ - protected $_cache_id = 0; + protected $_browser_cache_id = 0; + + /** + * @var array + */ + protected $_lastModified = array(); /** * Singleton instance @@ -496,21 +501,21 @@ class e_jsmanager switch($mod) { - case 'core': - $this->renderFile($this->_e_jslib_core, $external, 'Core libraries'); + case 'core': //e_jslib + $this->setLastModfied($mod, $this->renderFile($this->_e_jslib_core, $external, 'Core libraries')); $this->_e_jslib_core = array(); break; - case 'plugin': + case 'plugin': //e_jslib foreach($this->_e_jslib_plugin as $plugname => $paths) { - $this->renderFile($paths, $external, $plugname.' libraries'); + $this->setLastModfied($mod, $this->renderFile($paths, $external, $plugname.' libraries')); } $this->_e_jslib_plugin = array(); break; - case 'theme': - $this->renderFile($this->_e_jslib_theme, $external, 'Theme libraries'); + case 'theme': //e_jslib + $this->setLastModfied($mod, $this->renderFile($this->_e_jslib_theme, $external, 'Theme libraries')); $this->_e_jslib_theme = array(); break; @@ -576,7 +581,7 @@ class e_jsmanager * @param string $label added as comment if non-empty * @return void */ - public function renderFile($file_path_array, $external = false, $label = '') + public function renderFile($file_path_array, $external = false, $label = '', $checkModified = true) { if(empty($file_path_array)) { @@ -588,6 +593,8 @@ class e_jsmanager { echo "\n"; } + + $lmodified = 0; foreach ($file_path_array as $path) { if (substr($path, - 4) == '.php') @@ -598,7 +605,10 @@ class e_jsmanager echo "\n"; continue; } - include_once($tp->replaceConstants($path, '')); + + $path = $tp->replaceConstants($path, ''); + if($checkModified) $lmodified = max($lmodified, filemtime($path)); + include_once($path); echo "\n"; } else @@ -609,10 +619,15 @@ class e_jsmanager echo "\n"; continue; } - echo file_get_contents($tp->replaceConstants($path, '')); + + $path = $tp->replaceConstants($path, ''); + if($checkModified) $lmodified = max($lmodified, filemtime($path)); + echo file_get_contents($path); echo "\n"; } } + + return $lmodified; } /** @@ -692,7 +707,7 @@ class e_jsmanager */ public function getCacheId() { - return $this->_cache_id; + return $this->_browser_cache_id; } /** @@ -702,7 +717,31 @@ class e_jsmanager */ public function setCacheId($cacheid) { - $this->_cache_id = $cacheid; + $this->_browser_cache_id = intval($cacheid); return $this; } + + /** + * Set last modification timestamp for given namespace + * + * @param string $what + * @param integer $when [optional] + * @return e_jsmanager + */ + public function setLastModfied($what, $when = 0) + { + $this->_lastModified[$what] = $when; + return $this; + } + + /** + * Get last modification timestamp for given namespace + * + * @param string $what + * @return integer + */ + public function getLastModfied($what) + { + return (isset($this->_lastModified[$what]) ? $this->_lastModified[$what] : 0); + } } diff --git a/e107_handlers/jslib_handler.php b/e107_handlers/jslib_handler.php index 43db09e62..20e4fcfa5 100644 --- a/e107_handlers/jslib_handler.php +++ b/e107_handlers/jslib_handler.php @@ -7,8 +7,8 @@ * GNU General Public License (http://gnu.org). * * $Source: /cvs_backup/e107_0.8/e107_handlers/jslib_handler.php,v $ - * $Revision: 1.6 $ - * $Date: 2009-09-29 17:40:55 $ + * $Revision: 1.7 $ + * $Date: 2009-10-02 13:46:25 $ * $Author: secretr $ * */ @@ -26,13 +26,13 @@ class e_jslib * Collect & output all available JS libraries (requires e107 API) * FIXME * - cache jslib in a pref on plugin/theme install only (plugin.xml, theme.xml) - * - the structure of the cached pref array? - * - kill all dupps + * - [done - e_jslib_*] the structure of the cached pref array? + * - [done - js manager] kill all dupps * - jslib settings - Administration area (compression on/off, admin log on/off * manual control for included JS - really not sure about this, * Force Browser Cache refresh - timestamp added to the url hash) * - how and when to add JS lans for core libraries? - * - separate methods for collecting & storing JS files (to be used in install/update routines) and output the JS content + * - [done - js manager] separate methods for collecting & storing JS files (to be used in install/update routines) and output the JS content */ function core_run() { @@ -40,18 +40,54 @@ class e_jslib ob_start(); ob_implicit_flush(0); - - header("Last-modified: " . gmdate("D, d M Y H:i:s",mktime(0,0,0,15,2,2004)) . " GMT"); - header('Content-type: text/javascript', TRUE); $e_jsmanager = e107::getJs(); + + $lmodified = array(); $e_jsmanager->renderJs('core', null, false); + $lmodified[] = $e_jsmanager->getLastModfied('core'); + $e_jsmanager->renderJs('plugin', null, false); + $lmodified[] = $e_jsmanager->getLastModfied('plugin'); + $e_jsmanager->renderJs('theme', null, false); + $lmodified[] = $e_jsmanager->getLastModfied('theme'); + $lmodified[] = $e_jsmanager->getCacheId(); //e107::getPref('e_jslib_browser_cache', 0) + // last modification time for loaded files + $lmodified = max($lmodified); + + if (function_exists('date_default_timezone_set')) + { + date_default_timezone_set('UTC'); + } + + // If-Modified check only if cache disabled + // if cache is enabled, cache file modification date is set to $lmodified + if(!e107::getPref('syscachestatus')) + { + // not modified - send 304 and exit + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $lmodified) + { + header("HTTP/1.1 304 Not Modified", true); + exit; + } + } + + // send last modified date + header('Cache-Control: must-revalidate'); + header('Last-modified: '.gmdate('r', $lmodified), true); + + // send content type + header('Content-type: text/javascript', true); + + // Expire header - 1 year + $time = time()+ 365 * 86400; + header('Expires: '.gmdate('r', $time), true); + //Output - $this->content_out(); + $this->content_out($lmodified); /* //array - uses the same format as $core_jslib if (!isset($THEME_CORE_JSLIB) || ! is_array($THEME_CORE_JSLIB)) @@ -152,7 +188,7 @@ class e_jslib * Output buffered content (requires e107 API) * */ - function content_out() + function content_out($lmodified) { global $pref, $admin_log; @@ -172,7 +208,7 @@ class e_jslib $gzdata .= pack("V", $crc) . pack("V", $size); $gsize = strlen($gzdata); - $this->set_cache($gzdata, $encoding); + $this->set_cache($gzdata, $encoding, $lmodified); header('Content-Encoding: ' . $encoding); //header('Content-Length: '.$gsize); @@ -184,7 +220,7 @@ class e_jslib else { //header('Content-Length: '.strlen($contents)); - $this->set_cache($contents); + $this->set_cache($contents, '', $lmodified); print($contents); //TODO - log/debug //@file_put_contents('cache/e_jslib_log', "----------\nno cache used - raw\n\n", FILE_APPEND); @@ -198,17 +234,17 @@ class e_jslib * * @param string $contents * @param string $encoding browser accepted encoding + * @param integer $lmodified last modfied time */ - function set_cache($contents, $encoding = '') + function set_cache($contents, $encoding = '', $lmodified = 0) { - global $pref; - - if (varsettrue($pref['syscachestatus'])) + if (e107::getPref('syscachestatus')) { - $cacheFile = $this->cache_file($encoding); + $cacheFile = $this->cache_filename($encoding); + if(!$lmodified) $lmodified = time(); @file_put_contents($cacheFile, $contents); @chmod($cacheFile, 0775); - @touch($cacheFile); + @touch($cacheFile, $lmodified); } } @@ -219,37 +255,43 @@ class e_jslib */ function browser_enc() { - //double-compression fix (thanks Topper), remove possible php warning - if ( headers_sent() || ini_get('zlib.output_compression') || !isset($_SERVER["HTTP_ACCEPT_ENCODING"]) ) - { - $encoding = ''; - } - elseif (strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'x-gzip') !== false) - { - $encoding = 'x-gzip'; - } - elseif (strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'gzip') !== false) - { - $encoding = 'gzip'; - } - else - { - $encoding = ''; - } - return $encoding; + //NEW - option to disable completely gzip compression + if(strpos($_SERVER['QUERY_STRING'], '_nogzip')) + { + return ''; + } + //double-compression fix - thanks Topper + if (headers_sent() || ini_get('zlib.output_compression') || !isset($_SERVER["HTTP_ACCEPT_ENCODING"])) + { + $encoding = ''; + } + elseif (strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'x-gzip') !== false) + { + $encoding = 'x-gzip'; + } + elseif (strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'gzip') !== false) + { + $encoding = 'gzip'; + } + else + { + $encoding = ''; + } + + return $encoding; } /** * Create cache filename (doesn't require e107 API) * * @param string $encoding - * @param string $cacheStr + * @param string $cacheStr defaults to 'S_e_jslib' * @return string cache filename */ - function cache_file($encoding = '', $cacheStr = 'S_e_jslib') + function cache_filename($encoding = '', $cacheStr = 'S_e_jslib') { $cacheDir = 'cache/'; - $hash = $_SERVER['QUERY_STRING'] ? md5($_SERVER['QUERY_STRING']) : 'nomd5'; + $hash = $_SERVER['QUERY_STRING'] && $_SERVER['QUERY_STRING'] !== '_nogzip' ? md5(str_replace('_nogzip', '', $_SERVER['QUERY_STRING'])) : 'nomd5'; $cacheFile = $cacheDir . $cacheStr . ($encoding ? '_' . $encoding : '') . '_' . $hash . '.cache.php'; return $cacheFile;