mirror of
https://github.com/mrclay/minify.git
synced 2025-08-22 21:52:58 +02:00
Improved/added tests, deflate preferred over gzip, improved ConditionalGet docs
This commit is contained in:
@@ -3,31 +3,46 @@
|
||||
/**
|
||||
* Implement conditional GET via a timestamp or hash of content
|
||||
*
|
||||
* E.g. Content from DB with update time:
|
||||
* <code>
|
||||
* // easiest usage
|
||||
* list($updateTime, $content) = getDbUpdateAndContent();
|
||||
* $cg = new HTTP_ConditionalGet(array(
|
||||
* 'lastModifiedTime' => filemtime(__FILE__)
|
||||
* 'lastModifiedTime' => $updateTime
|
||||
* ));
|
||||
* $cg->sendHeaders();
|
||||
* if ($cg->cacheIsValid) {
|
||||
* exit(); // done
|
||||
* }
|
||||
* // echo content
|
||||
* </code>
|
||||
*
|
||||
*
|
||||
* <code>
|
||||
* // better to add content length once it's known
|
||||
* $cg = new HTTP_ConditionalGet(array(
|
||||
* 'lastModifiedTime' => filemtime(__FILE__)
|
||||
* ));
|
||||
* if ($cg->cacheIsValid) {
|
||||
* $cg->sendHeaders();
|
||||
* exit();
|
||||
* }
|
||||
* $content = get_content();
|
||||
* $cg->setContentLength(strlen($content));
|
||||
* echo $content;
|
||||
* </code>
|
||||
*
|
||||
* E.g. Content from DB with no update time:
|
||||
* <code>
|
||||
* $content = getContentFromDB();
|
||||
* $cg = new HTTP_ConditionalGet(array(
|
||||
* 'contentHash' => md5($content)
|
||||
* ));
|
||||
* $cg->sendHeaders();
|
||||
* if ($cg->cacheIsValid) {
|
||||
* exit();
|
||||
* }
|
||||
* echo $content;
|
||||
* </code>
|
||||
*
|
||||
* E.g. Static content with some static includes:
|
||||
* <code>
|
||||
* // before content
|
||||
* $cg = new HTTP_ConditionalGet(array(
|
||||
* 'lastUpdateTime' => max(
|
||||
* filemtime(__FILE__)
|
||||
* ,filemtime('/path/to/header.inc')
|
||||
* ,filemtime('/path/to/footer.inc')
|
||||
* )
|
||||
* ));
|
||||
* $cg->sendHeaders();
|
||||
* if ($cg->cacheIsValid) {
|
||||
* exit();
|
||||
* }
|
||||
* </code>
|
||||
*/
|
||||
class HTTP_ConditionalGet {
|
||||
@@ -76,7 +91,7 @@ class HTTP_ConditionalGet {
|
||||
// allow far-expires header
|
||||
if (isset($spec['setExpires'])) {
|
||||
if (is_numeric($spec['setExpires'])) {
|
||||
$spec['setExpires'] = self::gmtdate($spec['setExpires']);
|
||||
$spec['setExpires'] = self::gmtDate($spec['setExpires']);
|
||||
}
|
||||
$this->_headers = array(
|
||||
'Cache-Control' => $scope
|
||||
@@ -159,6 +174,21 @@ class HTTP_ConditionalGet {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a GMT formatted date for use in HTTP headers
|
||||
*
|
||||
* <code>
|
||||
* header('Expires: ' . HTTP_ConditionalGet::gmtdate($time));
|
||||
* </code>
|
||||
*
|
||||
* @param int $time unix timestamp
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function gmtDate($time) {
|
||||
return gmdate('D, d M Y H:i:s \G\M\T', $time);
|
||||
}
|
||||
|
||||
protected $_headers = array();
|
||||
protected $_lmTime = null;
|
||||
protected $_etag = null;
|
||||
@@ -172,7 +202,7 @@ class HTTP_ConditionalGet {
|
||||
|
||||
protected function _setLastModified($time) {
|
||||
$this->_lmTime = (int)$time;
|
||||
$this->_headers['Last-Modified'] = self::gmtdate($time);
|
||||
$this->_headers['Last-Modified'] = self::gmtDate($time);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,10 +251,6 @@ class HTTP_ConditionalGet {
|
||||
// IE has tacked on extra data to this header, strip it
|
||||
$ifModifiedSince = substr($ifModifiedSince, 0, $semicolon);
|
||||
}
|
||||
return ($ifModifiedSince == self::gmtdate($this->_lmTime));
|
||||
}
|
||||
|
||||
protected static function gmtdate($ts) {
|
||||
return gmdate('D, d M Y H:i:s \G\M\T', $ts);
|
||||
return ($ifModifiedSince == self::gmtDate($this->_lmTime));
|
||||
}
|
||||
}
|
||||
|
@@ -52,9 +52,7 @@ to verify headers and content being sent.</p>
|
||||
<dt>Safari</dt>
|
||||
<dd>ETag validation is unsupported, but Safari supports HTTP/1.0 validation via
|
||||
If-Modified-Since headers as long as the cache is explicitly marked
|
||||
"public" or "private". ConditionalGet can send one of these
|
||||
values determined by cookies/session data, but it's best to explicitly
|
||||
set the option 'isPublic' to true or false.</dd>
|
||||
"public" or "private" ("private" is default in ConditionalGet).</dd>
|
||||
</dl>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -54,15 +54,12 @@ class HTTP_Encoder {
|
||||
if (isset($spec['type'])) {
|
||||
$this->_headers['Content-Type'] = $spec['type'];
|
||||
}
|
||||
if (self::$_clientEncodeMethod === null) {
|
||||
self::$_clientEncodeMethod = self::getAcceptedEncoding();
|
||||
}
|
||||
if (isset($spec['method'])
|
||||
&& in_array($spec['method'], array('gzip', 'deflate', 'compress', '')))
|
||||
{
|
||||
$this->_encodeMethod = array($spec['method'], $spec['method']);
|
||||
} else {
|
||||
$this->_encodeMethod = self::$_clientEncodeMethod;
|
||||
$this->_encodeMethod = self::getAcceptedEncoding();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,32 +133,41 @@ class HTTP_Encoder {
|
||||
* this will return ('', ''), the "identity" encoding.
|
||||
*
|
||||
* A syntax-aware scan is done of the Accept-Encoding, so the method must
|
||||
* be non 0. The methods are favored in order of gzip, deflate, then
|
||||
* compress.
|
||||
*
|
||||
* Note: this value is cached internally for the entire PHP execution
|
||||
* be non 0. The methods are favored in order of deflate, gzip, then
|
||||
* compress. Yes, deflate is always smaller and faster!
|
||||
*
|
||||
* @return array two values, 1st is the actual encoding method, 2nd is the
|
||||
* alias of that method to use in the Content-Encoding header (some browsers
|
||||
* call gzip "x-gzip" etc.)
|
||||
*/
|
||||
public static function getAcceptedEncoding() {
|
||||
if (self::$_clientEncodeMethod !== null) {
|
||||
return self::$_clientEncodeMethod;
|
||||
}
|
||||
if (! isset($_SERVER['HTTP_ACCEPT_ENCODING'])
|
||||
|| self::_isBuggyIe())
|
||||
{
|
||||
return array('', '');
|
||||
}
|
||||
// @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
||||
// test for (x-)gzip, if q is specified, can't be "0"
|
||||
if (preg_match('@(?:^|,)\s*((?:x-)?gzip)\s*(?:$|,|;\s*q=(?:0\.|1))@', $_SERVER['HTTP_ACCEPT_ENCODING'], $m)) {
|
||||
return array('gzip', $m[1]);
|
||||
}
|
||||
if (preg_match('@(?:^|,)\s*deflate\s*(?:$|,|;\s*q=(?:0\.|1))@', $_SERVER['HTTP_ACCEPT_ENCODING'])) {
|
||||
// faster test for most common "gzip, deflate"
|
||||
if (preg_match('@(?:,| )deflate$@', $_SERVER['HTTP_ACCEPT_ENCODING'])
|
||||
|| preg_match(
|
||||
'@(?:^|,)\\s*deflate\\s*(?:$|,|;\\s*q=(?:0\\.|1))@'
|
||||
,$_SERVER['HTTP_ACCEPT_ENCODING'])
|
||||
) {
|
||||
return array('deflate', 'deflate');
|
||||
}
|
||||
if (preg_match('@(?:^|,)\s*((?:x-)?compress)\s*(?:$|,|;\s*q=(?:0\.|1))@', $_SERVER['HTTP_ACCEPT_ENCODING'], $m)) {
|
||||
if (preg_match(
|
||||
'@(?:^|,)\\s*((?:x-)?gzip)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@'
|
||||
,$_SERVER['HTTP_ACCEPT_ENCODING']
|
||||
,$m)
|
||||
) {
|
||||
return array('gzip', $m[1]);
|
||||
}
|
||||
if (preg_match(
|
||||
'@(?:^|,)\\s*((?:x-)?compress)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@'
|
||||
,$_SERVER['HTTP_ACCEPT_ENCODING']
|
||||
,$m)
|
||||
) {
|
||||
return array('compress', $m[1]);
|
||||
}
|
||||
return array('', '');
|
||||
@@ -211,21 +217,27 @@ class HTTP_Encoder {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static $_clientEncodeMethod = null;
|
||||
protected $content = '';
|
||||
protected $headers = array();
|
||||
protected $encodeMethod = array('', '');
|
||||
protected $_content = '';
|
||||
protected $_headers = array();
|
||||
protected $_encodeMethod = array('', '');
|
||||
|
||||
/**
|
||||
* Is the browser an IE version earlier than 6 SP2?
|
||||
*/
|
||||
protected static function _isBuggyIe()
|
||||
{
|
||||
if (strstr($_SERVER['HTTP_USER_AGENT'], 'Opera')
|
||||
|| !preg_match('/^Mozilla\/4\.0 \(compatible; MSIE ([0-9]\.[0-9])/i', $_SERVER['HTTP_USER_AGENT'], $m))
|
||||
{
|
||||
if (false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Opera')
|
||||
|| !preg_match(
|
||||
'/^Mozilla\/4\.0 \(compatible; MSIE ([0-9]\.[0-9])/i'
|
||||
,$_SERVER['HTTP_USER_AGENT']
|
||||
,$m
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
$version = floatval($m[1]);
|
||||
if ($version < 6) return true;
|
||||
if ($version == 6 && !strstr($_SERVER['HTTP_USER_AGENT'], 'SV1')) {
|
||||
if ($version == 6 && false === strpos($_SERVER['HTTP_USER_AGENT'], 'SV1')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@@ -46,8 +46,7 @@ window.onload = function(){
|
||||
document.getElementById("js").className = "green";
|
||||
};
|
||||
';
|
||||
$type = 'text/javascript';
|
||||
|
||||
$type = 'application/x-javascript';
|
||||
}
|
||||
|
||||
$he = new HTTP_Encoder(array(
|
||||
|
@@ -16,7 +16,7 @@
|
||||
* @package Minify
|
||||
* @author Ryan Grove <ryan@wonko.com>
|
||||
* @author Stephen Clay <steve@mrclay.org>
|
||||
* @copyright 2007 Ryan Grove. All rights reserved.
|
||||
* @copyright 2008 Ryan Grove, Stephen Clay. All rights reserved.
|
||||
* @license http://opensource.org/licenses/bsd-license.php New BSD License
|
||||
* @version 1.9.0
|
||||
* @link http://code.google.com/p/minify/
|
||||
@@ -27,8 +27,10 @@ require_once 'Minify/Source.php';
|
||||
class Minify {
|
||||
|
||||
const TYPE_CSS = 'text/css';
|
||||
const TYPE_JS = 'application/x-javascript';
|
||||
const TYPE_HTML = 'text/html';
|
||||
// there is some debate over the ideal JS Content-Type, but this is the
|
||||
// Apache default and what Yahoo! uses..
|
||||
const TYPE_JS = 'application/x-javascript';
|
||||
|
||||
/**
|
||||
* @var bool Should the un-encoded version be cached?
|
||||
|
Reference in New Issue
Block a user