mirror of
https://github.com/mrclay/minify.git
synced 2025-08-10 16:14:18 +02:00
Rewrote Minify::serve() to use built-in cache functions and send cached output with readfile() = huge perf increase. Also only returns array iff quiet is set. Removed a few options.
Encoder.php : added option to leave out compress Fixed unit tests for new Minify behavior and to show verbose when called directly on Windows.
This commit is contained in:
@@ -24,7 +24,12 @@
|
|||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
* For more control over headers, use getHeaders() and getData() and send your
|
* For more control over headers, use getHeaders() and getData() and send your
|
||||||
* own output.
|
* own output.
|
||||||
|
*
|
||||||
|
* Note: If you don't need header mgmt, use PHP's native gzencode, gzdeflate,
|
||||||
|
* and gzcompress functions for gzip, deflate, and compress-encoding
|
||||||
|
* respectively.
|
||||||
|
*
|
||||||
* @package Minify
|
* @package Minify
|
||||||
* @subpackage HTTP
|
* @subpackage HTTP
|
||||||
* @author Stephen Clay <steve@mrclay.org>
|
* @author Stephen Clay <steve@mrclay.org>
|
||||||
@@ -56,7 +61,8 @@ class HTTP_Encoder {
|
|||||||
*
|
*
|
||||||
* @return null
|
* @return null
|
||||||
*/
|
*/
|
||||||
public function __construct($spec) {
|
public function __construct($spec)
|
||||||
|
{
|
||||||
$this->_content = $spec['content'];
|
$this->_content = $spec['content'];
|
||||||
$this->_headers['Content-Length'] = (string)strlen($this->_content);
|
$this->_headers['Content-Length'] = (string)strlen($this->_content);
|
||||||
if (isset($spec['type'])) {
|
if (isset($spec['type'])) {
|
||||||
@@ -78,7 +84,8 @@ class HTTP_Encoder {
|
|||||||
*
|
*
|
||||||
* return string
|
* return string
|
||||||
*/
|
*/
|
||||||
public function getContent() {
|
public function getContent()
|
||||||
|
{
|
||||||
return $this->_content;
|
return $this->_content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +103,8 @@ class HTTP_Encoder {
|
|||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getHeaders() {
|
public function getHeaders()
|
||||||
|
{
|
||||||
return $this->_headers;
|
return $this->_headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +119,8 @@ class HTTP_Encoder {
|
|||||||
*
|
*
|
||||||
* @return null
|
* @return null
|
||||||
*/
|
*/
|
||||||
public function sendHeaders() {
|
public function sendHeaders()
|
||||||
|
{
|
||||||
foreach ($this->_headers as $name => $val) {
|
foreach ($this->_headers as $name => $val) {
|
||||||
header($name . ': ' . $val);
|
header($name . ': ' . $val);
|
||||||
}
|
}
|
||||||
@@ -128,7 +137,8 @@ class HTTP_Encoder {
|
|||||||
*
|
*
|
||||||
* @return null
|
* @return null
|
||||||
*/
|
*/
|
||||||
public function sendAll() {
|
public function sendAll()
|
||||||
|
{
|
||||||
$this->sendHeaders();
|
$this->sendHeaders();
|
||||||
echo $this->_content;
|
echo $this->_content;
|
||||||
}
|
}
|
||||||
@@ -143,12 +153,15 @@ class HTTP_Encoder {
|
|||||||
* A syntax-aware scan is done of the Accept-Encoding, so the method must
|
* A syntax-aware scan is done of the Accept-Encoding, so the method must
|
||||||
* be non 0. The methods are favored in order of deflate, gzip, then
|
* be non 0. The methods are favored in order of deflate, gzip, then
|
||||||
* compress. Yes, deflate is always smaller and faster!
|
* compress. Yes, deflate is always smaller and faster!
|
||||||
|
*
|
||||||
|
* @param bool $allowCompress allow the older compress encoding
|
||||||
*
|
*
|
||||||
* @return array two values, 1st is the actual encoding method, 2nd is the
|
* @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
|
* alias of that method to use in the Content-Encoding header (some browsers
|
||||||
* call gzip "x-gzip" etc.)
|
* call gzip "x-gzip" etc.)
|
||||||
*/
|
*/
|
||||||
public static function getAcceptedEncoding() {
|
public static function getAcceptedEncoding($allowCompress = true)
|
||||||
|
{
|
||||||
// @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
// @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
||||||
|
|
||||||
if (! isset($_SERVER['HTTP_ACCEPT_ENCODING'])
|
if (! isset($_SERVER['HTTP_ACCEPT_ENCODING'])
|
||||||
@@ -174,7 +187,7 @@ class HTTP_Encoder {
|
|||||||
,$m)) {
|
,$m)) {
|
||||||
return array('gzip', $m[1]);
|
return array('gzip', $m[1]);
|
||||||
}
|
}
|
||||||
if (preg_match(
|
if ($allowCompress && preg_match(
|
||||||
'@(?:^|,)\\s*((?:x-)?compress)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@'
|
'@(?:^|,)\\s*((?:x-)?compress)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@'
|
||||||
,$ae
|
,$ae
|
||||||
,$m)) {
|
,$m)) {
|
||||||
@@ -193,14 +206,15 @@ class HTTP_Encoder {
|
|||||||
* this fails, false is returned.
|
* this fails, false is returned.
|
||||||
*
|
*
|
||||||
* If successful, the Content-Length header is updated, and Content-Encoding
|
* If successful, the Content-Length header is updated, and Content-Encoding
|
||||||
* and Vary headers are added.
|
* and Vary headers are added.
|
||||||
*
|
*
|
||||||
* @param int $compressionLevel given to zlib functions. If not given, the
|
* @param int $compressionLevel given to zlib functions. If not given, the
|
||||||
* class default will be used.
|
* class default will be used.
|
||||||
*
|
*
|
||||||
* @return bool success true if the content was actually compressed
|
* @return bool success true if the content was actually compressed
|
||||||
*/
|
*/
|
||||||
public function encode($compressionLevel = null) {
|
public function encode($compressionLevel = null)
|
||||||
|
{
|
||||||
if (null === $compressionLevel) {
|
if (null === $compressionLevel) {
|
||||||
$compressionLevel = self::$compressionLevel;
|
$compressionLevel = self::$compressionLevel;
|
||||||
}
|
}
|
||||||
@@ -209,11 +223,11 @@ class HTTP_Encoder {
|
|||||||
|| !extension_loaded('zlib'))
|
|| !extension_loaded('zlib'))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ($this->_encodeMethod[0] === 'gzip') {
|
if ($this->_encodeMethod[0] === 'deflate') {
|
||||||
|
$encoded = gzdeflate($this->_content, $compressionLevel);
|
||||||
|
} elseif ($this->_encodeMethod[0] === 'gzip') {
|
||||||
$encoded = gzencode($this->_content, $compressionLevel);
|
$encoded = gzencode($this->_content, $compressionLevel);
|
||||||
} elseif ($this->_encodeMethod[0] === 'deflate') {
|
|
||||||
$encoded = gzdeflate($this->_content, $compressionLevel);
|
|
||||||
} else {
|
} else {
|
||||||
$encoded = gzcompress($this->_content, $compressionLevel);
|
$encoded = gzcompress($this->_content, $compressionLevel);
|
||||||
}
|
}
|
||||||
@@ -225,8 +239,8 @@ class HTTP_Encoder {
|
|||||||
$this->_headers['Vary'] = 'Accept-Encoding';
|
$this->_headers['Vary'] = 'Accept-Encoding';
|
||||||
$this->_content = $encoded;
|
$this->_content = $encoded;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $_content = '';
|
protected $_content = '';
|
||||||
protected $_headers = array();
|
protected $_headers = array();
|
||||||
protected $_encodeMethod = array('', '');
|
protected $_encodeMethod = array('', '');
|
||||||
|
331
lib/Minify.php
331
lib/Minify.php
@@ -34,14 +34,6 @@ class Minify {
|
|||||||
// Apache default and what Yahoo! uses..
|
// Apache default and what Yahoo! uses..
|
||||||
const TYPE_JS = 'application/x-javascript';
|
const TYPE_JS = 'application/x-javascript';
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool Should the un-encoded version be cached?
|
|
||||||
*
|
|
||||||
* True results in more cache files, but lower PHP load if different
|
|
||||||
* encodings are commonly requested.
|
|
||||||
*/
|
|
||||||
public static $cacheUnencodedVersion = true;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify a writeable directory for cache files. If not called, Minify
|
* 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
|
* will not use a disk cache and, for each 200 response, will need to
|
||||||
@@ -65,34 +57,37 @@ class Minify {
|
|||||||
/**
|
/**
|
||||||
* Serve a request for a minified file.
|
* Serve a request for a minified file.
|
||||||
*
|
*
|
||||||
* @param mixed instance of subclass of Minify_Controller_Base or string name of controller. E.g. 'Files'
|
* @param mixed instance of subclass of Minify_Controller_Base or string name of
|
||||||
|
* controller. E.g. 'Files'
|
||||||
*
|
*
|
||||||
* @param array $options controller/serve options
|
* @param array $options controller/serve options
|
||||||
*
|
*
|
||||||
* @return array success, statusCode, content, and headers generated
|
* @return mixed null, or, if the 'quiet' option is set to true, an array
|
||||||
|
* with keys "success" (bool), "statusCode" (int), "content" (string), and
|
||||||
|
* "headers" (array).
|
||||||
*
|
*
|
||||||
* Here are the available options and defaults in the base controller:
|
* Here are the available options and defaults in the base controller:
|
||||||
*
|
*
|
||||||
* 'isPublic' : send "public" instead of "private" in Cache-Control
|
* 'isPublic' : send "public" instead of "private" in Cache-Control
|
||||||
* headers, allowing shared caches to cache the output. (default true)
|
* headers, allowing shared caches to cache the output. (default true)
|
||||||
*
|
*
|
||||||
* 'quiet' : set to true to have no content/headers sent (default false)
|
* 'quiet' : set to true to have serve() return an array rather than sending
|
||||||
|
* any headers/output (default false)
|
||||||
*
|
*
|
||||||
* 'encodeOutput' : to disable content encoding, set this to false (default true)
|
* 'encodeOutput' : to disable content encoding, set this to false (default true)
|
||||||
*
|
*
|
||||||
* 'encodeMethod' : generally you should let this be determined by
|
* 'encodeMethod' : generally you should let this be determined by
|
||||||
* HTTP_Encoder (leave null), but you can force a particular encoding
|
* HTTP_Encoder (leave null), but you can force a particular encoding
|
||||||
* to be returned, by setting this to 'gzip', 'deflate', 'compress', or ''
|
* to be returned, by setting this to 'gzip', 'deflate', or '' (no encoding)
|
||||||
* (no encoding)
|
|
||||||
*
|
*
|
||||||
* 'encodeLevel' : level of encoding compression (0 to 9, default 9)
|
* 'encodeLevel' : level of encoding compression (0 to 9, default 9)
|
||||||
*
|
*
|
||||||
* 'contentTypeCharset' : if given, this will be appended to the Content-Type
|
* 'contentTypeCharset' : if given, this will be appended to the Content-Type
|
||||||
* header sent (needed mainly for HTML docs)
|
* header sent (needed mainly for HTML docs)
|
||||||
*
|
*
|
||||||
* 'setExpires' : set this to a timestamp or GMT date to have Minify send
|
* 'setExpires' : set this to a timestamp to have Minify send an HTTP Expires
|
||||||
* an HTTP Expires header instead of checking for conditional GET (default null).
|
* header instead of checking for conditional GET (default null).
|
||||||
* E.g. (time() + 86400 * 365) for 1yr
|
* E.g. ($_SERVER['REQUEST_TIME'] + 86400 * 365) for 1yr
|
||||||
* Note this has nothing to do with server-side caching.
|
* Note this has nothing to do with server-side caching.
|
||||||
*
|
*
|
||||||
* 'perType' : this is an array of options to send to a particular minifier
|
* 'perType' : this is an array of options to send to a particular minifier
|
||||||
@@ -128,45 +123,57 @@ class Minify {
|
|||||||
if (! self::$_options['quiet']) {
|
if (! self::$_options['quiet']) {
|
||||||
header(self::$_options['badRequestHeader']);
|
header(self::$_options['badRequestHeader']);
|
||||||
echo self::$_options['badRequestHeader'];
|
echo self::$_options['badRequestHeader'];
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
list(,$statusCode) = explode(' ', self::$_options['badRequestHeader']);
|
||||||
|
return array(
|
||||||
|
'success' => false
|
||||||
|
,'statusCode' => (int)$statusCode
|
||||||
|
,'content' => ''
|
||||||
|
,'headers' => array()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
list(,$statusCode) = explode(' ', self::$_options['badRequestHeader']);
|
|
||||||
return array(
|
|
||||||
'success' => false
|
|
||||||
,'statusCode' => (int)$statusCode
|
|
||||||
,'content' => ''
|
|
||||||
,'headers' => array()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self::$_controller = $controller;
|
self::$_controller = $controller;
|
||||||
|
|
||||||
$cgOptions = array(
|
if (null === self::$_options['setExpires']) {
|
||||||
'lastModifiedTime' => self::$_options['lastModifiedTime']
|
// conditional GET
|
||||||
,'isPublic' => self::$_options['isPublic']
|
require_once 'HTTP/ConditionalGet.php';
|
||||||
);
|
$cg = new HTTP_ConditionalGet(array(
|
||||||
if (null !== self::$_options['setExpires']) {
|
'lastModifiedTime' => self::$_options['lastModifiedTime']
|
||||||
$cgOptions['setExpires'] = self::$_options['setExpires'];
|
,'isPublic' => self::$_options['isPublic']
|
||||||
}
|
));
|
||||||
|
if ($cg->cacheIsValid) {
|
||||||
// check client cache
|
// client's cache is valid
|
||||||
require_once 'HTTP/ConditionalGet.php';
|
if (! self::$_options['quiet']) {
|
||||||
$cg = new HTTP_ConditionalGet($cgOptions);
|
$cg->sendHeaders();
|
||||||
if ($cg->cacheIsValid) {
|
return;
|
||||||
// client's cache is valid
|
} else {
|
||||||
if (! self::$_options['quiet']) {
|
return array(
|
||||||
$cg->sendHeaders();
|
'success' => true
|
||||||
|
,'statusCode' => 304
|
||||||
|
,'content' => ''
|
||||||
|
,'headers' => array()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// client will need output
|
||||||
|
$headers = $cg->getHeaders();
|
||||||
|
unset($cg);
|
||||||
}
|
}
|
||||||
return array(
|
} else {
|
||||||
'success' => true
|
// don't need conditional GET
|
||||||
,'statusCode' => 304
|
$privacy = self::$_options['isPublic']
|
||||||
,'content' => ''
|
? 'public'
|
||||||
,'headers' => array()
|
: 'private';
|
||||||
|
$headers = array(
|
||||||
|
'Cache-Control' => $privacy . ', max-age='
|
||||||
|
. (self::$_options['setExpires'] - $_SERVER['REQUEST_TIME'])
|
||||||
|
,'Expires' => gmdate('D, d M Y H:i:s \G\M\T', self::$_options['setExpires'])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// client will need output
|
|
||||||
$headers = $cg->getHeaders();
|
|
||||||
unset($cg);
|
|
||||||
|
|
||||||
// determine encoding
|
// determine encoding
|
||||||
if (self::$_options['encodeOutput']) {
|
if (self::$_options['encodeOutput']) {
|
||||||
if (self::$_options['encodeMethod'] !== null) {
|
if (self::$_options['encodeMethod'] !== null) {
|
||||||
@@ -176,26 +183,52 @@ class Minify {
|
|||||||
// sniff request header
|
// sniff request header
|
||||||
require_once 'HTTP/Encoder.php';
|
require_once 'HTTP/Encoder.php';
|
||||||
// depending on what the client accepts, $contentEncoding may be
|
// depending on what the client accepts, $contentEncoding may be
|
||||||
// 'x-gzip' while our internal encodeMethod is 'gzip'
|
// 'x-gzip' while our internal encodeMethod is 'gzip'. Calling
|
||||||
list(self::$_options['encodeMethod'], $contentEncoding) = HTTP_Encoder::getAcceptedEncoding();
|
// getAcceptedEncoding() with false leaves out compress as an option.
|
||||||
|
list(self::$_options['encodeMethod'], $contentEncoding) = HTTP_Encoder::getAcceptedEncoding(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self::$_options['encodeMethod'] = ''; // identity (no encoding)
|
self::$_options['encodeMethod'] = ''; // identity (no encoding)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null !== self::$_cachePath) {
|
if (null !== self::$_cachePath) {
|
||||||
self::_setupCache();
|
// using cache
|
||||||
// fetch content from cache file(s).
|
// the goal is to use only the cache methods to sniff the length and
|
||||||
$content = self::_fetchContent(self::$_options['encodeMethod']);
|
// output the content, as they do not require ever loading the file into
|
||||||
self::$_cache = null;
|
// memory.
|
||||||
|
$cacheId = self::_getCacheId();
|
||||||
|
$encodingExtension = self::$_options['encodeMethod']
|
||||||
|
? ('deflate' === self::$_options['encodeMethod']
|
||||||
|
? '.zd'
|
||||||
|
: '.zg')
|
||||||
|
: '';
|
||||||
|
$fullCacheId = $cacheId . $encodingExtension;
|
||||||
|
// check cache for valid entry
|
||||||
|
$cacheContentLength = self::getCacheSize($fullCacheId, self::$_options['lastModifiedTime']);
|
||||||
|
$cacheIsReady = (false !== $cacheContentLength);
|
||||||
|
if (! $cacheIsReady) {
|
||||||
|
// 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']));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// no cache, just combine, minify, encode
|
// no cache
|
||||||
|
$cacheIsReady = false;
|
||||||
$content = self::_combineMinify();
|
$content = self::_combineMinify();
|
||||||
$content = self::_encode($content);
|
|
||||||
}
|
}
|
||||||
|
if (! $cacheIsReady && self::$_options['encodeMethod']) {
|
||||||
// add headers to those from ConditionalGet
|
// still need to encode
|
||||||
//$headers['Content-Length'] = strlen($content);
|
$content = ('deflate' === self::$_options['encodeMethod'])
|
||||||
|
? gzdeflate($content, self::$_options['encodeLevel'])
|
||||||
|
: gzencode($content, self::$_options['encodeLevel']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add headers
|
||||||
|
$headers['Content-Length'] = $cacheIsReady
|
||||||
|
? $cacheContentLength
|
||||||
|
: strlen($content);
|
||||||
$headers['Content-Type'] = (null !== self::$_options['contentTypeCharset'])
|
$headers['Content-Type'] = (null !== self::$_options['contentTypeCharset'])
|
||||||
? self::$_options['contentType'] . '; charset=' . self::$_options['contentTypeCharset']
|
? self::$_options['contentType'] . '; charset=' . self::$_options['contentTypeCharset']
|
||||||
: self::$_options['contentType'];
|
: self::$_options['contentType'];
|
||||||
@@ -209,20 +242,27 @@ class Minify {
|
|||||||
foreach ($headers as $name => $val) {
|
foreach ($headers as $name => $val) {
|
||||||
header($name . ': ' . $val);
|
header($name . ': ' . $val);
|
||||||
}
|
}
|
||||||
echo $content;
|
if ($cacheIsReady) {
|
||||||
|
self::outputCache($fullCacheId);
|
||||||
|
} else {
|
||||||
|
echo $content;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return array(
|
||||||
|
'success' => true
|
||||||
|
,'statusCode' => 200
|
||||||
|
,'content' => $cacheIsReady
|
||||||
|
? self::getCache($fullCacheId)
|
||||||
|
: $content
|
||||||
|
,'headers' => $headers
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return array(
|
|
||||||
'success' => true
|
|
||||||
,'statusCode' => 200
|
|
||||||
,'content' => $content
|
|
||||||
,'headers' => $headers
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var mixed null if disk cache is not to be used
|
* @var mixed null if disk cache is not to be used
|
||||||
*/
|
*/
|
||||||
protected static $_cachePath = null;
|
private static $_cachePath = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Minify_Controller active controller for current request
|
* @var Minify_Controller active controller for current request
|
||||||
@@ -234,70 +274,6 @@ class Minify {
|
|||||||
*/
|
*/
|
||||||
protected static $_options = null;
|
protected static $_options = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Cache_Lite_File cache obj for current request
|
|
||||||
*/
|
|
||||||
protected static $_cache = null;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch encoded content from cache (or generate and store it).
|
|
||||||
*
|
|
||||||
* If self::$cacheUnencodedVersion is true and encoded content must be
|
|
||||||
* generated, this function will call itself recursively to fetch (or
|
|
||||||
* generate) the minified content. Otherwise, it will always recombine
|
|
||||||
* and reminify files to generate different encodings.
|
|
||||||
*
|
|
||||||
* @param string $encodeMethod
|
|
||||||
*
|
|
||||||
* @return string minified, encoded content
|
|
||||||
*/
|
|
||||||
protected static function _fetchContent($encodeMethod)
|
|
||||||
{
|
|
||||||
$cacheId = self::_getCacheId(self::$_controller->sources, self::$_options)
|
|
||||||
. $encodeMethod;
|
|
||||||
$content = self::$_cache->get($cacheId, 'Minify');
|
|
||||||
if (false === $content) {
|
|
||||||
// must generate
|
|
||||||
if ($encodeMethod === '') {
|
|
||||||
// generate identity cache to store
|
|
||||||
$content = self::_combineMinify();
|
|
||||||
} else {
|
|
||||||
// fetch identity cache & encode it to store
|
|
||||||
if (self::$cacheUnencodedVersion) {
|
|
||||||
// double layer cache
|
|
||||||
$content = self::_fetchContent('');
|
|
||||||
} else {
|
|
||||||
// recombine
|
|
||||||
$content = self::_combineMinify();
|
|
||||||
}
|
|
||||||
$content = self::_encode($content);
|
|
||||||
}
|
|
||||||
self::$_cache->save($content, $cacheId, 'Minify');
|
|
||||||
}
|
|
||||||
return $content;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set self::$_cache to a new instance of Cache_Lite_File (patched 2007-10-03)
|
|
||||||
*
|
|
||||||
* @return null
|
|
||||||
*/
|
|
||||||
protected static function _setupCache() {
|
|
||||||
// until the patch is rolled into PEAR, we'll provide the
|
|
||||||
// class in our package
|
|
||||||
require_once dirname(__FILE__) . '/Cache/Lite/File.php';
|
|
||||||
|
|
||||||
self::$_cache = new Cache_Lite_File(array(
|
|
||||||
'cacheDir' => self::$_cachePath . '/'
|
|
||||||
,'fileNameProtection' => false
|
|
||||||
|
|
||||||
// currently only available in patched Cache_Lite_File
|
|
||||||
,'masterTime' => self::$_options['lastModifiedTime']
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Combines sources and minifies the result.
|
* Combines sources and minifies the result.
|
||||||
*
|
*
|
||||||
@@ -365,29 +341,6 @@ class Minify {
|
|||||||
return $content;
|
return $content;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies HTTP encoding
|
|
||||||
*
|
|
||||||
* @param string $content
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected static function _encode($content)
|
|
||||||
{
|
|
||||||
if (self::$_options['encodeMethod'] === ''
|
|
||||||
|| ! self::$_options['encodeOutput']) {
|
|
||||||
// "identity" encoding
|
|
||||||
return $content;
|
|
||||||
}
|
|
||||||
require_once 'HTTP/Encoder.php';
|
|
||||||
$encoder = new HTTP_Encoder(array(
|
|
||||||
'content' => $content
|
|
||||||
,'method' => self::$_options['encodeMethod']
|
|
||||||
));
|
|
||||||
$encoder->encode(self::$_options['encodeLevel']);
|
|
||||||
return $encoder->getContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a unique cache id for for this request.
|
* Make a unique cache id for for this request.
|
||||||
*
|
*
|
||||||
@@ -402,4 +355,74 @@ class Minify {
|
|||||||
,self::$_options['perType']
|
,self::$_options['perType']
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,5 +20,6 @@ header('Expires: '. gmdate('D, d M Y H:i:s \G\M\T', $_SERVER['REQUEST_TIME'] + (
|
|||||||
header('Content-Type: application/x-javascript; charset=utf-8');
|
header('Content-Type: application/x-javascript; charset=utf-8');
|
||||||
header('Content-Encoding: deflate');
|
header('Content-Encoding: deflate');
|
||||||
header('Content-Length: ' . filesize($cached));
|
header('Content-Length: ' . filesize($cached));
|
||||||
|
header('Vary: Accept-Encoding');
|
||||||
|
|
||||||
readfile($cached);
|
readfile($cached);
|
@@ -14,5 +14,5 @@ Minify::serve('Files', array(
|
|||||||
'files' => array(
|
'files' => array(
|
||||||
dirname(__FILE__) . '/before.js'
|
dirname(__FILE__) . '/before.js'
|
||||||
)
|
)
|
||||||
,'setExpires' => 31536000 // 1 yr
|
,'setExpires' => $_SERVER['REQUEST_TIME'] + 31536000 // 1 yr
|
||||||
));
|
));
|
||||||
|
@@ -13,31 +13,31 @@ Document Path: /_3rd_party/minify/web/ab_tests/ideal_php/before.php
|
|||||||
Document Length: 15993 bytes
|
Document Length: 15993 bytes
|
||||||
|
|
||||||
Concurrency Level: 100
|
Concurrency Level: 100
|
||||||
Time taken for tests: 4.531250 seconds
|
Time taken for tests: 4.437500 seconds
|
||||||
Complete requests: 2000
|
Complete requests: 2000
|
||||||
Failed requests: 0
|
Failed requests: 0
|
||||||
Write errors: 0
|
Write errors: 0
|
||||||
Total transferred: 32802000 bytes
|
Total transferred: 32848000 bytes
|
||||||
HTML transferred: 31986000 bytes
|
HTML transferred: 31986000 bytes
|
||||||
Requests per second: 441.38 [#/sec] (mean)
|
Requests per second: 450.70 [#/sec] (mean)
|
||||||
Time per request: 226.563 [ms] (mean)
|
Time per request: 221.875 [ms] (mean)
|
||||||
Time per request: 2.266 [ms] (mean, across all concurrent requests)
|
Time per request: 2.219 [ms] (mean, across all concurrent requests)
|
||||||
Transfer rate: 7069.35 [Kbytes/sec] received
|
Transfer rate: 7228.85 [Kbytes/sec] received
|
||||||
|
|
||||||
Connection Times (ms)
|
Connection Times (ms)
|
||||||
min mean[+/-sd] median max
|
min mean[+/-sd] median max
|
||||||
Connect: 0 0 2.7 0 31
|
Connect: 0 0 2.6 0 15
|
||||||
Processing: 62 217 50.0 218 671
|
Processing: 31 214 37.5 218 359
|
||||||
Waiting: 31 216 50.7 218 671
|
Waiting: 31 209 32.7 218 281
|
||||||
Total: 62 218 49.9 218 671
|
Total: 31 215 37.5 218 359
|
||||||
|
|
||||||
Percentage of the requests served within a certain time (ms)
|
Percentage of the requests served within a certain time (ms)
|
||||||
50% 218
|
50% 218
|
||||||
66% 218
|
66% 218
|
||||||
75% 218
|
75% 234
|
||||||
80% 234
|
80% 234
|
||||||
90% 234
|
90% 234
|
||||||
95% 265
|
95% 250
|
||||||
98% 296
|
98% 296
|
||||||
99% 500
|
99% 343
|
||||||
100% 671 (longest request)
|
100% 359 (longest request)
|
||||||
|
@@ -29,7 +29,7 @@ function test_CSS()
|
|||||||
$minOutput = Minify_CSS::minify($src, $options);
|
$minOutput = Minify_CSS::minify($src, $options);
|
||||||
$passed = assertTrue($minExpected === $minOutput, 'Minify_CSS : ' . $item);
|
$passed = assertTrue($minExpected === $minOutput, 'Minify_CSS : ' . $item);
|
||||||
|
|
||||||
if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) {
|
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
|
||||||
echo "\n---Output: " .strlen($minOutput). " bytes\n\n{$minOutput}\n\n";
|
echo "\n---Output: " .strlen($minOutput). " bytes\n\n{$minOutput}\n\n";
|
||||||
if (!$passed) {
|
if (!$passed) {
|
||||||
echo "---Expected: " .strlen($minExpected). " bytes\n\n{$minExpected}\n\n";
|
echo "---Expected: " .strlen($minExpected). " bytes\n\n{$minExpected}\n\n";
|
||||||
|
@@ -19,7 +19,7 @@ function test_HTML()
|
|||||||
|
|
||||||
$passed = assertTrue($minExpected === $minOutput, 'Minify_HTML');
|
$passed = assertTrue($minExpected === $minOutput, 'Minify_HTML');
|
||||||
|
|
||||||
if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) {
|
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
|
||||||
echo "\n---Output: " .strlen($minOutput). " bytes\n\n{$minOutput}\n\n";
|
echo "\n---Output: " .strlen($minOutput). " bytes\n\n{$minOutput}\n\n";
|
||||||
echo "---Expected: " .strlen($minExpected). " bytes\n\n{$minExpected}\n\n";
|
echo "---Expected: " .strlen($minExpected). " bytes\n\n{$minExpected}\n\n";
|
||||||
echo "---Source: " .strlen($src). " bytes\n\n{$src}\n\n\n";
|
echo "---Source: " .strlen($src). " bytes\n\n{$src}\n\n\n";
|
||||||
|
@@ -97,7 +97,7 @@ function test_HTTP_ConditionalGet()
|
|||||||
|
|
||||||
$passed = assertTrue($exp == $ret, 'HTTP_ConditionalGet : ' . $test['desc']);
|
$passed = assertTrue($exp == $ret, 'HTTP_ConditionalGet : ' . $test['desc']);
|
||||||
|
|
||||||
if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) {
|
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
|
||||||
echo "\n--- INM = {$test['inm']} / IMS = {$test['ims']}\n";
|
echo "\n--- INM = {$test['inm']} / IMS = {$test['ims']}\n";
|
||||||
echo "Expected = " . preg_replace('/\\s+/', ' ', var_export($exp, 1)) . "\n";
|
echo "Expected = " . preg_replace('/\\s+/', ' ', var_export($exp, 1)) . "\n";
|
||||||
echo "Returned = " . preg_replace('/\\s+/', ' ', var_export($ret, 1)) . "\n\n";
|
echo "Returned = " . preg_replace('/\\s+/', ' ', var_export($ret, 1)) . "\n\n";
|
||||||
|
@@ -59,7 +59,7 @@ function test_HTTP_Encoder()
|
|||||||
$ret = HTTP_Encoder::getAcceptedEncoding();
|
$ret = HTTP_Encoder::getAcceptedEncoding();
|
||||||
$passed = assertTrue($exp == $ret, 'HTTP_Encoder : ' . $test['desc']);
|
$passed = assertTrue($exp == $ret, 'HTTP_Encoder : ' . $test['desc']);
|
||||||
|
|
||||||
if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) {
|
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
|
||||||
echo "\n--- AE | UA = {$test['ae']} | {$test['ua']}\n";
|
echo "\n--- AE | UA = {$test['ae']} | {$test['ua']}\n";
|
||||||
echo "Expected = " . preg_replace('/\\s+/', ' ', var_export($exp, 1)) . "\n";
|
echo "Expected = " . preg_replace('/\\s+/', ' ', var_export($exp, 1)) . "\n";
|
||||||
echo "Returned = " . preg_replace('/\\s+/', ' ', var_export($ret, 1)) . "\n\n";
|
echo "Returned = " . preg_replace('/\\s+/', ' ', var_export($ret, 1)) . "\n\n";
|
||||||
@@ -90,7 +90,7 @@ function test_HTTP_Encoder()
|
|||||||
|
|
||||||
$passed = assertTrue($ret == $test['exp'], $desc);
|
$passed = assertTrue($ret == $test['exp'], $desc);
|
||||||
|
|
||||||
if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) {
|
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
|
||||||
echo "\n--- {$test['method']}: expected bytes: "
|
echo "\n--- {$test['method']}: expected bytes: "
|
||||||
, "{$test['exp']}. Returned: {$ret}\n\n";
|
, "{$test['exp']}. Returned: {$ret}\n\n";
|
||||||
}
|
}
|
||||||
|
@@ -13,7 +13,7 @@ function test_Javascript()
|
|||||||
|
|
||||||
$passed = assertTrue($minExpected == $minOutput, 'Minify_Javascript');
|
$passed = assertTrue($minExpected == $minOutput, 'Minify_Javascript');
|
||||||
|
|
||||||
if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) {
|
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
|
||||||
echo "\n---Output: " .strlen($minOutput). " bytes\n\n{$minOutput}\n\n";
|
echo "\n---Output: " .strlen($minOutput). " bytes\n\n{$minOutput}\n\n";
|
||||||
echo "---Expected: " .strlen($minExpected). " bytes\n\n{$minExpected}\n\n";
|
echo "---Expected: " .strlen($minExpected). " bytes\n\n{$minExpected}\n\n";
|
||||||
echo "---Source: " .strlen($src). " bytes\n\n{$src}\n\n\n";
|
echo "---Source: " .strlen($src). " bytes\n\n{$src}\n\n\n";
|
||||||
@@ -27,7 +27,7 @@ function test_Javascript()
|
|||||||
|
|
||||||
$passed = assertTrue($minExpected == $minOutput, 'Minify_Javascript');
|
$passed = assertTrue($minExpected == $minOutput, 'Minify_Javascript');
|
||||||
|
|
||||||
if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) {
|
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
|
||||||
echo "\n---Output: " .strlen($minOutput). " bytes\n\n{$minOutput}\n\n";
|
echo "\n---Output: " .strlen($minOutput). " bytes\n\n{$minOutput}\n\n";
|
||||||
echo "---Expected: " .strlen($minExpected). " bytes\n\n{$minExpected}\n\n";
|
echo "---Expected: " .strlen($minExpected). " bytes\n\n{$minExpected}\n\n";
|
||||||
echo "---Source: " .strlen($src). " bytes\n\n{$src}\n\n\n";
|
echo "---Source: " .strlen($src). " bytes\n\n{$src}\n\n\n";
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
// currently these only test serve() when passed the 'quiet' options
|
||||||
|
|
||||||
require_once '_inc.php';
|
require_once '_inc.php';
|
||||||
require_once 'Minify.php';
|
require_once 'Minify.php';
|
||||||
|
|
||||||
@@ -32,7 +34,7 @@ function test_Minify()
|
|||||||
,'encodeOutput' => false
|
,'encodeOutput' => false
|
||||||
));
|
));
|
||||||
$passed = assertTrue($expected === $output, 'Minify : 304 response');
|
$passed = assertTrue($expected === $output, 'Minify : 304 response');
|
||||||
if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) {
|
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
|
||||||
echo "\nOutput: " .var_export($output, 1). "\n\n";
|
echo "\nOutput: " .var_export($output, 1). "\n\n";
|
||||||
if (! $passed) {
|
if (! $passed) {
|
||||||
echo "\n\n\n\n---Expected: " .var_export($expected, 1). "\n\n";
|
echo "\n\n\n\n---Expected: " .var_export($expected, 1). "\n\n";
|
||||||
@@ -40,22 +42,24 @@ function test_Minify()
|
|||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(
|
assertTrue(
|
||||||
! class_exists('Cache_Lite_File', false)
|
//! class_exists('Cache_Lite_File', false)
|
||||||
&& ! class_exists('HTTP_Encoder', false)
|
! class_exists('HTTP_Encoder', false)
|
||||||
&& ! class_exists('Minify_CSS', false)
|
&& ! class_exists('Minify_CSS', false)
|
||||||
,'File.php, Encoder.php, CSS.php not loaded'
|
,'Encoder.php, CSS.php not loaded'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Test minifying JS and serving with Expires header
|
// Test minifying JS and serving with Expires header
|
||||||
|
|
||||||
|
$content = preg_replace('/\\r\\n?/', "\n", file_get_contents($minifyTestPath . '/minified.js'));
|
||||||
$expected = array(
|
$expected = array(
|
||||||
'success' => true
|
'success' => true
|
||||||
,'statusCode' => 200
|
,'statusCode' => 200
|
||||||
// Minify_Javascript always converts to \n line endings
|
// Minify_Javascript always converts to \n line endings
|
||||||
,'content' => preg_replace('/\\r\\n?/', "\n", file_get_contents($minifyTestPath . '/minified.js'))
|
,'content' => $content
|
||||||
,'headers' => array (
|
,'headers' => array (
|
||||||
'Cache-Control' => 'public',
|
'Cache-Control' => 'public, max-age=86400',
|
||||||
'Expires' => gmdate('D, d M Y H:i:s \G\M\T', $tomorrow),
|
'Expires' => gmdate('D, d M Y H:i:s \G\M\T', $tomorrow),
|
||||||
|
'Content-Length' => strlen($content),
|
||||||
'Content-Type' => 'application/x-javascript',
|
'Content-Type' => 'application/x-javascript',
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -70,7 +74,7 @@ function test_Minify()
|
|||||||
));
|
));
|
||||||
$passed = assertTrue($expected === $output, 'Minify : JS and Expires');
|
$passed = assertTrue($expected === $output, 'Minify : JS and Expires');
|
||||||
|
|
||||||
if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) {
|
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
|
||||||
echo "\nOutput: " .var_export($output, 1). "\n\n";
|
echo "\nOutput: " .var_export($output, 1). "\n\n";
|
||||||
if (! $passed) {
|
if (! $passed) {
|
||||||
echo "\n\n\n\n---Expected: " .var_export($expected, 1). "\n\n";
|
echo "\n\n\n\n---Expected: " .var_export($expected, 1). "\n\n";
|
||||||
@@ -93,8 +97,9 @@ function test_Minify()
|
|||||||
'Last-Modified' => gmdate('D, d M Y H:i:s \G\M\T', $lastModified),
|
'Last-Modified' => gmdate('D, d M Y H:i:s \G\M\T', $lastModified),
|
||||||
'ETag' => "\"{$lastModified}pub\"",
|
'ETag' => "\"{$lastModified}pub\"",
|
||||||
'Cache-Control' => 'max-age=0, public, must-revalidate',
|
'Cache-Control' => 'max-age=0, public, must-revalidate',
|
||||||
|
'Content-Length' => filesize($minifyTestPath . '/minified.css'),
|
||||||
'Content-Type' => 'text/css',
|
'Content-Type' => 'text/css',
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$output = Minify::serve('Files', array(
|
$output = Minify::serve('Files', array(
|
||||||
'files' => array(
|
'files' => array(
|
||||||
@@ -107,7 +112,7 @@ function test_Minify()
|
|||||||
));
|
));
|
||||||
|
|
||||||
$passed = assertTrue($expected === $output, 'Minify : CSS and Etag/Last-Modified');
|
$passed = assertTrue($expected === $output, 'Minify : CSS and Etag/Last-Modified');
|
||||||
if (__FILE__ === $_SERVER['SCRIPT_FILENAME']) {
|
if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
|
||||||
echo "\nOutput: " .var_export($output, 1). "\n\n";
|
echo "\nOutput: " .var_export($output, 1). "\n\n";
|
||||||
if (! $passed) {
|
if (! $passed) {
|
||||||
echo "\n\n\n\n---Expected: " .var_export($expected, 1). "\n\n";
|
echo "\n\n\n\n---Expected: " .var_export($expected, 1). "\n\n";
|
||||||
|
Reference in New Issue
Block a user