diff --git a/lib/Cache/Lite/File.php b/lib/Cache/Lite/File.php deleted file mode 100644 index 4fb89e2..0000000 --- a/lib/Cache/Lite/File.php +++ /dev/null @@ -1,925 +0,0 @@ - -* -* Nota : A chinese documentation (thanks to RainX ) is -* available at : -* http://rainx.phpmore.com/manual/cache_lite.html -* -* @package Cache_Lite -* @category Caching -* @version $Id: Lite.php,v 1.45 2006/06/03 08:10:33 fab Exp $ -* @author Fabien MARTY -*/ - -define('CACHE_LITE_ERROR_RETURN', 1); -define('CACHE_LITE_ERROR_DIE', 8); - -class Cache_Lite -{ - - // --- Private properties --- - - /** - * Directory where to put the cache files - * (make sure to add a trailing slash) - * - * @var string $_cacheDir - */ - var $_cacheDir = '/tmp/'; - - /** - * Enable / disable caching - * - * (can be very usefull for the debug of cached scripts) - * - * @var boolean $_caching - */ - var $_caching = true; - - /** - * Cache lifetime (in seconds) - * - * If null, the cache is valid forever. - * - * @var int $_lifeTime - */ - var $_lifeTime = 3600; - - /** - * Enable / disable fileLocking - * - * (can avoid cache corruption under bad circumstances) - * - * @var boolean $_fileLocking - */ - var $_fileLocking = true; - - /** - * Timestamp of the last valid cache - * - * @var int $_refreshTime - */ - var $_refreshTime; - - /** - * File name (with path) - * - * @var string $_file - */ - var $_file; - - /** - * File name (without path) - * - * @var string $_fileName - */ - var $_fileName; - - /** - * Enable / disable write control (the cache is read just after writing to detect corrupt entries) - * - * Enable write control will lightly slow the cache writing but not the cache reading - * Write control can detect some corrupt cache files but maybe it's not a perfect control - * - * @var boolean $_writeControl - */ - var $_writeControl = true; - - /** - * Enable / disable read control - * - * If enabled, a control key is embeded in cache file and this key is compared with the one - * calculated after the reading. - * - * @var boolean $_writeControl - */ - var $_readControl = true; - - /** - * Type of read control (only if read control is enabled) - * - * Available values are : - * 'md5' for a md5 hash control (best but slowest) - * 'crc32' for a crc32 hash control (lightly less safe but faster, better choice) - * 'strlen' for a length only test (fastest) - * - * @var boolean $_readControlType - */ - var $_readControlType = 'crc32'; - - /** - * Pear error mode (when raiseError is called) - * - * (see PEAR doc) - * - * @see setToDebug() - * @var int $_pearErrorMode - */ - var $_pearErrorMode = CACHE_LITE_ERROR_RETURN; - - /** - * Current cache id - * - * @var string $_id - */ - var $_id; - - /** - * Current cache group - * - * @var string $_group - */ - var $_group; - - /** - * Enable / Disable "Memory Caching" - * - * NB : There is no lifetime for memory caching ! - * - * @var boolean $_memoryCaching - */ - var $_memoryCaching = false; - - /** - * Enable / Disable "Only Memory Caching" - * (be carefull, memory caching is "beta quality") - * - * @var boolean $_onlyMemoryCaching - */ - var $_onlyMemoryCaching = false; - - /** - * Memory caching array - * - * @var array $_memoryCachingArray - */ - var $_memoryCachingArray = array(); - - /** - * Memory caching counter - * - * @var int $memoryCachingCounter - */ - var $_memoryCachingCounter = 0; - - /** - * Memory caching limit - * - * @var int $memoryCachingLimit - */ - var $_memoryCachingLimit = 1000; - - /** - * File Name protection - * - * if set to true, you can use any cache id or group name - * if set to false, it can be faster but cache ids and group names - * will be used directly in cache file names so be carefull with - * special characters... - * - * @var boolean $fileNameProtection - */ - var $_fileNameProtection = true; - - /** - * Enable / disable automatic serialization - * - * it can be used to save directly datas which aren't strings - * (but it's slower) - * - * @var boolean $_serialize - */ - var $_automaticSerialization = false; - - /** - * Disable / Tune the automatic cleaning process - * - * The automatic cleaning process destroy too old (for the given life time) - * cache files when a new cache file is written. - * 0 => no automatic cache cleaning - * 1 => systematic cache cleaning - * x (integer) > 1 => automatic cleaning randomly 1 times on x cache write - * - * @var int $_automaticCleaning - */ - var $_automaticCleaningFactor = 0; - - /** - * Nested directory level - * - * Set the hashed directory structure level. 0 means "no hashed directory - * structure", 1 means "one level of directory", 2 means "two levels"... - * This option can speed up Cache_Lite only when you have many thousands of - * cache file. Only specific benchs can help you to choose the perfect value - * for you. Maybe, 1 or 2 is a good start. - * - * @var int $_hashedDirectoryLevel - */ - var $_hashedDirectoryLevel = 0; - - /** - * Umask for hashed directory structure - * - * @var int $_hashedDirectoryUmask - */ - var $_hashedDirectoryUmask = 0700; - - /** - * API break for error handling in CACHE_LITE_ERROR_RETURN mode - * - * In CACHE_LITE_ERROR_RETURN mode, error handling was not good because - * for example save() method always returned a boolean (a PEAR_Error object - * would be better in CACHE_LITE_ERROR_RETURN mode). To correct this without - * breaking the API, this option (false by default) can change this handling. - * - * @var boolean - */ - var $_errorHandlingAPIBreak = false; - - // --- Public methods --- - - /** - * Constructor - * - * $options is an assoc. Available options are : - * $options = array( - * 'cacheDir' => directory where to put the cache files (string), - * 'caching' => enable / disable caching (boolean), - * 'lifeTime' => cache lifetime in seconds (int), - * 'fileLocking' => enable / disable fileLocking (boolean), - * 'writeControl' => enable / disable write control (boolean), - * 'readControl' => enable / disable read control (boolean), - * 'readControlType' => type of read control 'crc32', 'md5', 'strlen' (string), - * 'pearErrorMode' => pear error mode (when raiseError is called) (cf PEAR doc) (int), - * 'memoryCaching' => enable / disable memory caching (boolean), - * 'onlyMemoryCaching' => enable / disable only memory caching (boolean), - * 'memoryCachingLimit' => max nbr of records to store into memory caching (int), - * 'fileNameProtection' => enable / disable automatic file name protection (boolean), - * 'automaticSerialization' => enable / disable automatic serialization (boolean), - * 'automaticCleaningFactor' => distable / tune automatic cleaning process (int), - * 'hashedDirectoryLevel' => level of the hashed directory system (int), - * 'hashedDirectoryUmask' => umask for hashed directory structure (int), - * 'errorHandlingAPIBreak' => API break for better error handling ? (boolean) - * ); - * - * @param array $options options - * @access public - */ - function Cache_Lite($options = array(NULL)) - { - foreach($options as $key => $value) { - $this->setOption($key, $value); - } - } - - /** - * Generic way to set a Cache_Lite option - * - * see Cache_Lite constructor for available options - * - * @var string $name name of the option - * @var mixed $value value of the option - * @access public - */ - function setOption($name, $value) - { - $availableOptions = array('errorHandlingAPIBreak', 'hashedDirectoryUmask', 'hashedDirectoryLevel', 'automaticCleaningFactor', 'automaticSerialization', 'fileNameProtection', 'memoryCaching', 'onlyMemoryCaching', 'memoryCachingLimit', 'cacheDir', 'caching', 'lifeTime', 'fileLocking', 'writeControl', 'readControl', 'readControlType', 'pearErrorMode'); - if (in_array($name, $availableOptions)) { - $property = '_'.$name; - $this->$property = $value; - } - } - - /** - * Test if a cache is available and (if yes) return it - * - * @param string $id cache id - * @param string $group name of the cache group - * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested - * @return string data of the cache (else : false) - * @access public - */ - function get($id, $group = 'default', $doNotTestCacheValidity = false) - { - $this->_id = $id; - $this->_group = $group; - $data = false; - if ($this->_caching) { - $this->_setRefreshTime(); - $this->_setFileName($id, $group); - clearstatcache(); - if ($this->_memoryCaching) { - if (isset($this->_memoryCachingArray[$this->_file])) { - if ($this->_automaticSerialization) { - return unserialize($this->_memoryCachingArray[$this->_file]); - } - return $this->_memoryCachingArray[$this->_file]; - } - if ($this->_onlyMemoryCaching) { - return false; - } - } - if (($doNotTestCacheValidity) || (is_null($this->_refreshTime))) { - if (file_exists($this->_file)) { - $data = $this->_read(); - } - } else { - if ((file_exists($this->_file)) && (@filemtime($this->_file) > $this->_refreshTime)) { - $data = $this->_read(); - } - } - if (($data) and ($this->_memoryCaching)) { - $this->_memoryCacheAdd($data); - } - if (($this->_automaticSerialization) and (is_string($data))) { - $data = unserialize($data); - } - return $data; - } - return false; - } - - /** - * Save some data in a cache file - * - * @param string $data data to put in cache (can be another type than strings if automaticSerialization is on) - * @param string $id cache id - * @param string $group name of the cache group - * @return boolean true if no problem (else : false or a PEAR_Error object) - * @access public - */ - function save($data, $id = NULL, $group = 'default') - { - if ($this->_caching) { - if ($this->_automaticSerialization) { - $data = serialize($data); - } - if (isset($id)) { - $this->_setFileName($id, $group); - } - if ($this->_memoryCaching) { - $this->_memoryCacheAdd($data); - if ($this->_onlyMemoryCaching) { - return true; - } - } - if ($this->_automaticCleaningFactor>0) { - $rand = rand(1, $this->_automaticCleaningFactor); - if ($rand==1) { - $this->clean(false, 'old'); - } - } - if ($this->_writeControl) { - $res = $this->_writeAndControl($data); - if (is_bool($res)) { - if ($res) { - return true; - } - // if $res if false, we need to invalidate the cache - @touch($this->_file, time() - 2*abs($this->_lifeTime)); - return false; - } - } else { - $res = $this->_write($data); - } - if (is_object($res)) { - // $res is a PEAR_Error object - if (!($this->_errorHandlingAPIBreak)) { - return false; // we return false (old API) - } - } - return $res; - } - return false; - } - - /** - * Remove a cache file - * - * @param string $id cache id - * @param string $group name of the cache group - * @return boolean true if no problem - * @access public - */ - function remove($id, $group = 'default') - { - $this->_setFileName($id, $group); - if ($this->_memoryCaching) { - if (isset($this->_memoryCachingArray[$this->_file])) { - unset($this->_memoryCachingArray[$this->_file]); - $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1; - } - if ($this->_onlyMemoryCaching) { - return true; - } - } - return $this->_unlink($this->_file); - } - - /** - * Clean the cache - * - * if no group is specified all cache files will be destroyed - * else only cache files of the specified group will be destroyed - * - * @param string $group name of the cache group - * @param string $mode flush cache mode : 'old', 'ingroup', 'notingroup', - * 'callback_myFunction' - * @return boolean true if no problem - * @access public - */ - function clean($group = false, $mode = 'ingroup') - { - return $this->_cleanDir($this->_cacheDir, $group, $mode); - } - - /** - * Set to debug mode - * - * When an error is found, the script will stop and the message will be displayed - * (in debug mode only). - * - * @access public - */ - function setToDebug() - { - $this->setOption('pearErrorMode', CACHE_LITE_ERROR_DIE); - } - - /** - * Set a new life time - * - * @param int $newLifeTime new life time (in seconds) - * @access public - */ - function setLifeTime($newLifeTime) - { - $this->_lifeTime = $newLifeTime; - $this->_setRefreshTime(); - } - - /** - * Save the state of the caching memory array into a cache file cache - * - * @param string $id cache id - * @param string $group name of the cache group - * @access public - */ - function saveMemoryCachingState($id, $group = 'default') - { - if ($this->_caching) { - $array = array( - 'counter' => $this->_memoryCachingCounter, - 'array' => $this->_memoryCachingState - ); - $data = serialize($array); - $this->save($data, $id, $group); - } - } - - /** - * Load the state of the caching memory array from a given cache file cache - * - * @param string $id cache id - * @param string $group name of the cache group - * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested - * @access public - */ - function getMemoryCachingState($id, $group = 'default', $doNotTestCacheValidity = false) - { - if ($this->_caching) { - if ($data = $this->get($id, $group, $doNotTestCacheValidity)) { - $array = unserialize($data); - $this->_memoryCachingCounter = $array['counter']; - $this->_memoryCachingArray = $array['array']; - } - } - } - - /** - * Return the cache last modification time - * - * BE CAREFUL : THIS METHOD IS FOR HACKING ONLY ! - * - * @return int last modification time - */ - function lastModified() - { - return @filemtime($this->_file); - } - - /** - * Trigger a PEAR error - * - * To improve performances, the PEAR.php file is included dynamically. - * The file is so included only when an error is triggered. So, in most - * cases, the file isn't included and perfs are much better. - * - * @param string $msg error message - * @param int $code error code - * @access public - */ - function raiseError($msg, $code) - { - include_once('PEAR.php'); - return PEAR::raiseError($msg, $code, $this->_pearErrorMode); - } - - /** - * Extend the life of a valid cache file - * - * see http://pear.php.net/bugs/bug.php?id=6681 - * - * @access public - */ - function extendLife() - { - @touch($this->_file); - } - - // --- Private methods --- - - /** - * Compute & set the refresh time - * - * @access private - */ - function _setRefreshTime() - { - if (is_null($this->_lifeTime)) { - $this->_refreshTime = null; - } else { - $this->_refreshTime = time() - $this->_lifeTime; - } - } - - /** - * Remove a file - * - * @param string $file complete file path and name - * @return boolean true if no problem - * @access private - */ - function _unlink($file) - { - if (!@unlink($file)) { - return $this->raiseError('Cache_Lite : Unable to remove cache !', -3); - } - return true; - } - - /** - * Recursive function for cleaning cache file in the given directory - * - * @param string $dir directory complete path (with a trailing slash) - * @param string $group name of the cache group - * @param string $mode flush cache mode : 'old', 'ingroup', 'notingroup', - 'callback_myFunction' - * @return boolean true if no problem - * @access private - */ - function _cleanDir($dir, $group = false, $mode = 'ingroup') - { - if ($this->_fileNameProtection) { - $motif = ($group) ? 'cache_'.md5($group).'_' : 'cache_'; - } else { - $motif = ($group) ? 'cache_'.$group.'_' : 'cache_'; - } - if ($this->_memoryCaching) { - while (list($key, ) = each($this->_memoryCachingArray)) { - if (strpos($key, $motif, 0)) { - unset($this->_memoryCachingArray[$key]); - $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1; - } - } - if ($this->_onlyMemoryCaching) { - return true; - } - } - if (!($dh = opendir($dir))) { - return $this->raiseError('Cache_Lite : Unable to open cache directory !', -4); - } - $result = true; - while ($file = readdir($dh)) { - if (($file != '.') && ($file != '..')) { - if (substr($file, 0, 6)=='cache_') { - $file2 = $dir . $file; - if (is_file($file2)) { - switch (substr($mode, 0, 9)) { - case 'old': - // files older than lifeTime get deleted from cache - if (!is_null($this->_lifeTime)) { - if ((mktime() - @filemtime($file2)) > $this->_lifeTime) { - $result = ($result and ($this->_unlink($file2))); - } - } - break; - case 'notingrou': - if (!strpos($file2, $motif, 0)) { - $result = ($result and ($this->_unlink($file2))); - } - break; - case 'callback_': - $func = substr($mode, 9, strlen($mode) - 9); - if ($func($file2, $group)) { - $result = ($result and ($this->_unlink($file2))); - } - break; - case 'ingroup': - default: - if (strpos($file2, $motif, 0)) { - $result = ($result and ($this->_unlink($file2))); - } - break; - } - } - if ((is_dir($file2)) and ($this->_hashedDirectoryLevel>0)) { - $result = ($result and ($this->_cleanDir($file2 . '/', $group, $mode))); - } - } - } - } - return $result; - } - - /** - * Add some date in the memory caching array - * - * @param string $data data to cache - * @access private - */ - function _memoryCacheAdd($data) - { - $this->_memoryCachingArray[$this->_file] = $data; - if ($this->_memoryCachingCounter >= $this->_memoryCachingLimit) { - list($key, ) = each($this->_memoryCachingArray); - unset($this->_memoryCachingArray[$key]); - } else { - $this->_memoryCachingCounter = $this->_memoryCachingCounter + 1; - } - } - - /** - * Make a file name (with path) - * - * @param string $id cache id - * @param string $group name of the group - * @access private - */ - function _setFileName($id, $group) - { - - if ($this->_fileNameProtection) { - $suffix = 'cache_'.md5($group).'_'.md5($id); - } else { - $suffix = 'cache_'.$group.'_'.$id; - } - $root = $this->_cacheDir; - if ($this->_hashedDirectoryLevel>0) { - $hash = md5($suffix); - for ($i=0 ; $i<$this->_hashedDirectoryLevel ; $i++) { - $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/'; - } - } - $this->_fileName = $suffix; - $this->_file = $root.$suffix; - } - - /** - * Read the cache file and return the content - * - * @return string content of the cache file (else : false or a PEAR_Error object) - * @access private - */ - function _read() - { - $fp = @fopen($this->_file, "rb"); - if ($this->_fileLocking) @flock($fp, LOCK_SH); - if ($fp) { - clearstatcache(); - $length = @filesize($this->_file); - $mqr = get_magic_quotes_runtime(); - set_magic_quotes_runtime(0); - if ($this->_readControl) { - $hashControl = @fread($fp, 32); - $length = $length - 32; - } - if ($length) { - $data = @fread($fp, $length); - } else { - $data = ''; - } - set_magic_quotes_runtime($mqr); - if ($this->_fileLocking) @flock($fp, LOCK_UN); - @fclose($fp); - if ($this->_readControl) { - $hashData = $this->_hash($data, $this->_readControlType); - if ($hashData != $hashControl) { - if (!(is_null($this->_lifeTime))) { - @touch($this->_file, time() - 2*abs($this->_lifeTime)); - } else { - @unlink($this->_file); - } - return false; - } - } - return $data; - } - return $this->raiseError('Cache_Lite : Unable to read cache !', -2); - } - - /** - * Write the given data in the cache file - * - * @param string $data data to put in cache - * @return boolean true if ok (a PEAR_Error object else) - * @access private - */ - function _write($data) - { - if ($this->_hashedDirectoryLevel > 0) { - $hash = md5($this->_fileName); - $root = $this->_cacheDir; - for ($i=0 ; $i<$this->_hashedDirectoryLevel ; $i++) { - $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/'; - if (!(@is_dir($root))) { - @mkdir($root, $this->_hashedDirectoryUmask); - } - } - } - $fp = @fopen($this->_file, "wb"); - if ($fp) { - if ($this->_fileLocking) @flock($fp, LOCK_EX); - if ($this->_readControl) { - @fwrite($fp, $this->_hash($data, $this->_readControlType), 32); - } - $len = strlen($data); - @fwrite($fp, $data, $len); - if ($this->_fileLocking) @flock($fp, LOCK_UN); - @fclose($fp); - return true; - } - return $this->raiseError('Cache_Lite : Unable to write cache file : '.$this->_file, -1); - } - - /** - * Write the given data in the cache file and control it just after to avoir corrupted cache entries - * - * @param string $data data to put in cache - * @return boolean true if the test is ok (else : false or a PEAR_Error object) - * @access private - */ - function _writeAndControl($data) - { - $result = $this->_write($data); - if (is_object($result)) { - return $result; # We return the PEAR_Error object - } - $dataRead = $this->_read(); - if (is_object($dataRead)) { - return $result; # We return the PEAR_Error object - } - if ((is_bool($dataRead)) && (!$dataRead)) { - return false; - } - return ($dataRead==$data); - } - - /** - * Make a control key with the string containing datas - * - * @param string $data data - * @param string $controlType type of control 'md5', 'crc32' or 'strlen' - * @return string control key - * @access private - */ - function _hash($data, $controlType) - { - switch ($controlType) { - case 'md5': - return md5($data); - case 'crc32': - return sprintf('% 32d', crc32($data)); - case 'strlen': - return sprintf('% 32d', strlen($data)); - default: - return $this->raiseError('Unknown controlType ! (available values are only \'md5\', \'crc32\', \'strlen\')', -5); - } - } - -} - -/** -* This class extends Cache_Lite and offers a cache system driven by a master -* file or timestamp -* -* With this class, cache validity is only dependent of a given file or timestamp. -* Cache files are valid only if they are older than the master file or the given -* timestamp. It's a perfect way for caching templates results (if the template -* file is newer than the cache, cache must be rebuild...) or for config classes... -* -* If the cache is dependent on multiple files, supply the constructor's -* 'masterTime' option with the greatest of the files' mtimes. -* -* There are some examples in the 'docs/examples' file -* Technical choices are described in the 'docs/technical' file -* -* @package Cache_Lite -* @version $Id: File.php,v 1.3 2005/12/04 16:03:55 fab Exp $ -* @author Fabien MARTY -*/ - -// require_once('Cache/Lite.php'); - -class Cache_Lite_File extends Cache_Lite -{ - - // --- Private properties --- - - /** - * Complete path of the file used for controlling the cache lifetime - * - * @var string $_masterFile - */ - var $_masterFile = ''; - - /** - * Masterfile mtime - * - * @var int $_masterFile_mtime - */ - var $_masterFile_mtime = 0; - - // --- Public methods ---- - - /** - * Constructor - * - * $options is an assoc. To have a look at availables options, - * see the constructor of the Cache_Lite class in 'Cache_Lite.php' - * - * Comparing to Cache_Lite constructor, there are two more options: - * $options = array( - * (...) see Cache_Lite constructor - * 'masterFile' => complete path of the file used for controlling the cache lifetime(string) - * 'masterTime' => timestamp of last application change that would invalidate the cache(int). - * ); - * Supply only one of these. If 'masterFile' is supplied, 'masterTime' is - * ignored, otherwise 'masterTime' is required. - * - * @param array $options options - * @access public - */ - function Cache_Lite_File($options = array(NULL)) - { - $options['lifetime'] = 0; - $this->Cache_Lite($options); - if (isset($options['masterFile'])) { - $this->_masterFile = $options['masterFile']; - if (!($this->_masterFile_mtime = @filemtime($this->_masterFile))) { - return $this->raiseError('Cache_Lite_File : Unable to read masterFile : '.$this->_masterFile, -3); - } - } elseif (isset($options['masterTime'])) { - $this->_masterFile_mtime = $options['masterTime']; - } else { - return $this->raiseError('Cache_Lite_File : either masterFile or masterTime option must be set !'); - } - } - - /** - * Test if a cache is available and (if yes) return it - * - * The third param is just to suppress PHP5's E_STRICT warning - * - * @param string $id cache id - * @param string $group name of the cache group - * @return string data of the cache (or false if no cache available) - * @access public - */ - function get($id, $group = 'default', $thisValueIgnored = false) - { - if ($data = parent::get($id, $group, true)) { - if ($filemtime = $this->lastModified()) { - if ($filemtime > $this->_masterFile_mtime) { - return $data; - } - } - } - return false; - } -} diff --git a/lib/Cache/Lite/readme.txt b/lib/Cache/Lite/readme.txt deleted file mode 100644 index 360a2df..0000000 --- a/lib/Cache/Lite/readme.txt +++ /dev/null @@ -1,6 +0,0 @@ -File.php contains PEAR's Cache_Lite and a patched version of Cache_Lite_File. - -See: http://pear.php.net/bugs/bug.php?id=12179 - -Until the patch is accepted (hopefully), we'll include it here. - diff --git a/lib/Minify.php b/lib/Minify.php index 417511d..a14c769 100644 --- a/lib/Minify.php +++ b/lib/Minify.php @@ -36,24 +36,40 @@ class Minify { const TYPE_JS = 'application/x-javascript'; /** - * Specify a writeable directory for cache files. If not called, Minify - * will not use a disk cache and, for each 200 response, will need to - * recombine files, minify and encode the output. + * @see setCache() + * @param mixed $cache object with identical interface as Minify_Cache_File or + * a directory path. (default = '') + * @return null + * @deprecated + */ + public static function useServerCache($path = null) + { + self::setCache($path); + } + + /** + * Specify a cache object (with identical interface as Minify_Cache_File) or + * a path to use with Minify_Cache_File. + * + * If not called, Minify will not use a cache and, for each 200 response, will + * need to recombine files, minify and encode the output. * - * @param string $path Full directory path for cache files (should not end - * in directory separator character). If not provided, Minify will attempt to - * write to the path returned by sys_get_temp_dir(). + * @param mixed $cache object with identical interface as Minify_Cache_File or + * a directory path. (default = '') * * @return null */ - public static function useServerCache($path = null) { - if (null !== $path) { - self::$_cachePath = $path; + public static function setCache($cache = '') + { + if (is_string($cache)) { + require_once 'Minify/Cache/File.php'; + self::$_cache = new Minify_Cache_File($cache); } else { - require_once 'Solar/Dir.php'; - self::$_cachePath = rtrim(Solar_Dir::tmp(), DIRECTORY_SEPARATOR); + self::$_cache = $cache; } } + + private static $_cache = null; /** * Serve a request for a minified file. @@ -82,14 +98,23 @@ class Minify { * E.g. ($_SERVER['REQUEST_TIME'] + 86400 * 365) for 1yr * Note this has nothing to do with server-side caching. * - * 'perType' : this is an array of options to send to a particular minifier - * function using the content-type as key. E.g. To send the CSS minifier an - * option: + * 'minifiers' : to override Minify's default choice of minifier function for + * a particular content-type, specify your callback under the key of the + * content-type: * - * $options['perType'][Minify::TYPE_CSS]['optionName'] = 'optionValue'; + * // call customCssMinifier($css) for all CSS minification + * $options['minifiers'][Minify::TYPE_CSS] = 'customCssMinifier'; + * + * // don't minify Javascript at all + * $options['minifiers'][Minify::TYPE_JS] = ''; + * + * + * 'minifierOptions' : to send options to the minifier function, specify your options + * under the key of the content-type. E.g. To send the CSS minifier an option: + * + * // give CSS minifier array('optionName' => 'optionValue') as 2nd argument + * $options['minifierOptions'][Minify::TYPE_CSS]['optionName'] = 'optionValue'; * - * When the CSS minifier is called, the 2nd argument will be - * array('optionName' => 'optionValue'). * * Any controller options are documented in that controller's setupSources() method. * @@ -191,7 +216,7 @@ class Minify { self::$_options['encodeMethod'] = ''; // identity (no encoding) } - if (null !== self::$_cachePath) { + if (null !== self::$_cache) { // using cache // the goal is to use only the cache methods to sniff the length and // output the content, as they do not require ever loading the file into @@ -204,14 +229,15 @@ class Minify { : ''; $fullCacheId = $cacheId . $encodingExtension; // check cache for valid entry - $cacheContentLength = self::getCacheSize($fullCacheId, self::$_options['lastModifiedTime']); - $cacheIsReady = (false !== $cacheContentLength); - if (! $cacheIsReady) { + $cacheIsReady = self::$_cache->isValid($fullCacheId, self::$_options['lastModifiedTime']); + if ($cacheIsReady) { + $cacheContentLength = self::$_cache->getSize($fullCacheId); + } else { // generate & cache content $content = self::_combineMinify(); - self::writeCache($cacheId, $content); - self::writeCache($cacheId . '.zd', gzdeflate($content, self::$_options['encodeLevel'])); - self::writeCache($cacheId . '.zg', gzencode($content, self::$_options['encodeLevel'])); + self::$_cache->store($cacheId, $content); + self::$_cache->store($cacheId . '.zd', gzdeflate($content, self::$_options['encodeLevel'])); + self::$_cache->store($cacheId . '.zg', gzencode($content, self::$_options['encodeLevel'])); } } else { // no cache @@ -243,7 +269,7 @@ class Minify { header($name . ': ' . $val); } if ($cacheIsReady) { - self::outputCache($fullCacheId); + self::$_cache->display($fullCacheId); } else { echo $content; } @@ -252,18 +278,13 @@ class Minify { 'success' => true ,'statusCode' => 200 ,'content' => $cacheIsReady - ? self::getCache($fullCacheId) + ? self::$_cache->fetch($fullCacheId) : $content ,'headers' => $headers ); } } - /** - * @var mixed null if disk cache is not to be used - */ - private static $_cachePath = null; - /** * @var Minify_Controller active controller for current request */ @@ -290,8 +311,8 @@ class Minify { // allow the user to pass a particular array of options to each // minifier (designated by type). source objects may still override // these - $defaultOptions = isset(self::$_options['perType'][$type]) - ? self::$_options['perType'][$type] + $defaultOptions = isset(self::$_options['minifierOptions'][$type]) + ? self::$_options['minifierOptions'][$type] : array(); // if minifier not set, default is no minification. source objects // may still override this @@ -352,77 +373,7 @@ class Minify { return md5(serialize(array( Minify_Source::getDigest(self::$_controller->sources) ,self::$_options['minifiers'] - ,self::$_options['perType'] + ,self::$_options['minifierOptions'] ))); - } - - /** - * Write data to file and verify its contents - * - * @param string $file full file path - * - * @param string $data - * - * @return bool success - */ - protected static function _verifiedWrite($file, $data) - { - return (file_put_contents($file, $data, LOCK_EX) - && (md5($data) === md5_file($file)) - ); - } - - /** - * Write data to cache. - * - * @param string $id cache id (e.g. a filename) - * - * @param string $data - * - * @return bool success - */ - protected static function writeCache($id, $data) - { - return self::_verifiedWrite(self::$_cachePath . '/' . $id, $data); - } - - /** - * Get the size of a valid cache entry (if exists) - * - * @param string $id cache id (e.g. a filename) - * - * @param int $srcMtime mtime of the original source file(s) - * - * @return mixed number of bytes on success, false if file doesn't - * exist or is stale - */ - protected static function getCacheSize($id, $srcMtime) - { - $file = self::$_cachePath . '/' . $id; - return (file_exists($file) && (filemtime($file) >= $srcMtime)) - ? filesize($file) - : false; - } - - /** - * Send the cached content to output - * - * @param string $id cache id (e.g. a filename) - */ - protected static function outputCache($id) - { - readfile(self::$_cachePath . '/' . $id); - } - - /** - * Fetch the cached content - * - * @param string $id cache id (e.g. a filename) - * - * @return string - */ - protected static function getCache($id) - { - return file_get_contents(self::$_cachePath . '/' . $id); - } + } } diff --git a/lib/Minify/Cache/File.php b/lib/Minify/Cache/File.php new file mode 100644 index 0000000..939326b --- /dev/null +++ b/lib/Minify/Cache/File.php @@ -0,0 +1,98 @@ +_path = $path; + } + + /** + * Write data to cache. + * + * @param string $id cache id (e.g. a filename) + * + * @param string $data + * + * @return bool success + */ + public function store($id, $data) + { + return self::_verifiedWrite($this->_path . '/' . $id, $data); + } + + /** + * Get the size of a cache entry + * + * @param string $id cache id (e.g. a filename) + * + * @return int size in bytes + */ + public function getSize($id) + { + return filesize($this->_path . '/' . $id); + } + + /** + * Does a valid cache entry exist? + * + * @param string $id cache id (e.g. a filename) + * + * @param int $srcMtime mtime of the original source file(s) + * + * @return bool exists + */ + public function isValid($id, $srcMtime) + { + $file = $this->_path . '/' . $id; + return (file_exists($file) && (filemtime($file) >= $srcMtime)); + } + + /** + * Send the cached content to output + * + * @param string $id cache id (e.g. a filename) + */ + public function display($id) + { + readfile($this->_path . '/' . $id); + } + + /** + * Fetch the cached content + * + * @param string $id cache id (e.g. a filename) + * + * @return string + */ + public function fetch($id) + { + return file_get_contents($this->_path . '/' . $id); + } + + private $_path = null; + + /** + * Write data to file and verify its contents + * + * @param string $file path + * + * @param string $data + * + * @return bool success + */ + private static function _verifiedWrite($file, $data) + { + return (file_put_contents($file, $data, LOCK_EX) + && (md5($data) === md5_file($file)) + ); + } +} diff --git a/lib/Minify/Controller/Base.php b/lib/Minify/Controller/Base.php index 926aa17..dd290fc 100644 --- a/lib/Minify/Controller/Base.php +++ b/lib/Minify/Controller/Base.php @@ -43,7 +43,7 @@ abstract class Minify_Controller_Base { ,'encodeOutput' => true ,'encodeMethod' => null // determine later ,'encodeLevel' => 9 - ,'perType' => array() // no per-type minifier options + ,'minifierOptions' => array() // no minifier options ,'contentTypeCharset' => 'UTF-8' ,'setExpires' => null // use conditional GET ,'quiet' => false // serve() will send headers and output diff --git a/lib/Minify/Controller/Groups.php b/lib/Minify/Controller/Groups.php index 64c9ca5..2024230 100644 --- a/lib/Minify/Controller/Groups.php +++ b/lib/Minify/Controller/Groups.php @@ -46,6 +46,10 @@ class Minify_Controller_Groups extends Minify_Controller_Base { $groups = $options['groups']; unset($options['groups']); + if (! isset($_SERVER['PATH_INFO'])) { + // no PATH_INFO + return $options; + } $pi = substr($_SERVER['PATH_INFO'], 1); if (! isset($groups[$pi])) { // not a valid group diff --git a/lib/Minify/Controller/Version1.php b/lib/Minify/Controller/Version1.php index aa59f69..f0bb866 100644 --- a/lib/Minify/Controller/Version1.php +++ b/lib/Minify/Controller/Version1.php @@ -31,7 +31,7 @@ class Minify_Controller_Version1 extends Minify_Controller_Base { $cacheDir = defined('MINIFY_CACHE_DIR') ? MINIFY_CACHE_DIR : null; - Minify::useServerCache($cacheDir); + Minify::setCache($cacheDir); } $options['badRequestHeader'] = 'HTTP/1.0 404 Not Found'; $options['contentTypeCharset'] = MINIFY_ENCODING; diff --git a/web/ab_tests/minify/test_Files.php b/web/ab_tests/minify/test_Files.php index 07080f4..049f5b8 100644 --- a/web/ab_tests/minify/test_Files.php +++ b/web/ab_tests/minify/test_Files.php @@ -5,7 +5,7 @@ require '../../config.php'; require 'Minify.php'; // give an explicit path to avoid having to load Solar/Dir.php -Minify::useServerCache($minifyCachePath); +Minify::setCache($minifyCachePath); Minify::serve('Files', array( 'files' => array( diff --git a/web/ab_tests/minify/test_Groups.php b/web/ab_tests/minify/test_Groups.php new file mode 100644 index 0000000..cbee3ec --- /dev/null +++ b/web/ab_tests/minify/test_Groups.php @@ -0,0 +1,15 @@ + array( + 'test' => array(dirname(__FILE__) . '/before.js') + ) + ,'setExpires' => $_SERVER['REQUEST_TIME'] + 31536000 // 1 yr +)); diff --git a/web/ab_tests/test_all.bat b/web/ab_tests/test_all.bat index 6f1cd55..cc31561 100644 --- a/web/ab_tests/test_all.bat +++ b/web/ab_tests/test_all.bat @@ -1,36 +1,43 @@ @SET PATH=%PATH%;C:\xampp\apache\bin -@REM SET ABCALL=ab -d -S -c 100 -n 2000 -H "Accept-Encoding: deflate, gzip" http://localhost +::SET ABCALL=ab -d -S -c 100 -n 2000 -H "Accept-Encoding: deflate, gzip" http://localhost @SET ABCALL=ab -d -S -c 100 -n 2000 -H "Accept-Encoding: deflate, gzip" http://mc.dev/_3rd_party +::SET TXTVIEWER=notepad.exe +@SET TXTVIEWER="C:\Program Files\Notepad++\notepad++.exe" + @SET DELIM=TYPE _delimiter DEL results.txt -@REM baseline PHP +:: baseline PHP %ABCALL%/minify/web/ab_tests/ideal_php/before.php >> results.txt -%DELIM% >> results.txt +@%DELIM% >> results.txt -@REM 1.0 release +:: 1.0 release %ABCALL%/minify/web/ab_tests/v1.0/minify.php?files=before.js >> results.txt -%DELIM% >> results.txt +@%DELIM% >> results.txt -@REM Files controller +:: Files controller %ABCALL%/minify/web/ab_tests/minify/test_Files.php >> results.txt -%DELIM% >> results.txt +@%DELIM% >> results.txt -@REM Version1 controller +:: Groups controller +%ABCALL%/minify/web/ab_tests/minify/test_Groups.php/test >> results.txt +@%DELIM% >> results.txt + +:: Version1 controller %ABCALL%/minify/web/ab_tests/minify/test_Version1.php?files=before.js >> results.txt -%DELIM% >> results.txt +@%DELIM% >> results.txt -@REM mod_deflate +:: mod_deflate %ABCALL%/minify/web/ab_tests/mod_deflate/before.js >> results.txt -%DELIM% >> results.txt +@%DELIM% >> results.txt -@REM type-map +:: type-map %ABCALL%/minify/web/ab_tests/type-map/before.js.var >> results.txt -%DELIM% >> results.txt +@%DELIM% >> results.txt FINDSTR "Path: Length: Requests --" results.txt > results_summary.txt -notepad results_summary.txt \ No newline at end of file +START %TXTVIEWER% results_summary.txt \ No newline at end of file diff --git a/web/ab_tests/v1.0/minify.php b/web/ab_tests/v1.0/minify.php index 7835abd..f5ca8dd 100644 --- a/web/ab_tests/v1.0/minify.php +++ b/web/ab_tests/v1.0/minify.php @@ -498,4 +498,3 @@ class MinifyInvalidUrlException extends MinifyException {} if (realpath(__FILE__) == realpath($_SERVER['SCRIPT_FILENAME'])) { Minify::handleRequest(); } -?> \ No newline at end of file diff --git a/web/examples/1/m.php b/web/examples/1/m.php index 53e8af0..c9900b9 100644 --- a/web/examples/1/m.php +++ b/web/examples/1/m.php @@ -5,7 +5,7 @@ require '_groupsSources.php'; require 'Minify.php'; if ($minifyCachePath) { - Minify::useServerCache($minifyCachePath); + Minify::setCache($minifyCachePath); } Minify::serve('Groups', array( diff --git a/web/examples/2/m.php b/web/examples/2/m.php index 53e8af0..c9900b9 100644 --- a/web/examples/2/m.php +++ b/web/examples/2/m.php @@ -5,7 +5,7 @@ require '_groupsSources.php'; require 'Minify.php'; if ($minifyCachePath) { - Minify::useServerCache($minifyCachePath); + Minify::setCache($minifyCachePath); } Minify::serve('Groups', array( diff --git a/web/examples/3/m.php b/web/examples/3/m.php index ebec8b4..0bbcc66 100644 --- a/web/examples/3/m.php +++ b/web/examples/3/m.php @@ -27,7 +27,7 @@ if (isset($_GET['f'])) { require 'Minify.php'; if ($minifyCachePath) { - Minify::useServerCache($minifyCachePath); + Minify::setCache($minifyCachePath); } // The Files controller can serve an array of files, but here we just diff --git a/web/test/test_Minify.php b/web/test/test_Minify.php index f2afa33..a7a46e1 100644 --- a/web/test/test_Minify.php +++ b/web/test/test_Minify.php @@ -84,7 +84,7 @@ function test_Minify() // Test minifying CSS and responding with Etag/Last-Modified // needed to expose E_STRICT warning in Cache_Lite_File - Minify::useServerCache(); + Minify::setCache(); // don't allow conditional headers unset($_SERVER['HTTP_IF_NONE_MATCH'], $_SERVER['HTTP_IF_MODIFIED_SINCE']);