From 02dbf14c8ef751d4eb041ed925c2c1812d5eaf5f Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Mon, 10 May 2010 07:44:40 +0000 Subject: [PATCH] Work on: Issue 120, Issue 170, Issue 152, Issue 125, Issue 134, Issue 143 Broke some tests --- README.txt | 10 +-- min/.htaccess | 13 +++- min/README.txt | 8 +-- min/builder/_index.js | 15 ++-- min/builder/bm2.js | 14 ++++ min/builder/index.php | 83 ++++++++++++++------- min/config.php | 27 ++++--- min/index.php | 11 ++- min/lib/HTTP/ConditionalGet.php | 17 +++-- min/lib/JSMin.php | 10 ++- min/lib/Minify.php | 21 ++++-- min/lib/Minify/Controller/Base.php | 9 +++ min/lib/Minify/Controller/MinApp.php | 10 ++- min/lib/Minify/Controller/Page.php | 5 ++ min_unit_tests/.htaccess | 4 ++ min_unit_tests/test_Minify.php | 10 +-- min_unit_tests/test_environment.php | 103 ++++++++++++++++++--------- 17 files changed, 259 insertions(+), 111 deletions(-) create mode 100644 min/builder/bm2.js create mode 100644 min_unit_tests/.htaccess diff --git a/README.txt b/README.txt index 3899b99..43978d5 100644 --- a/README.txt +++ b/README.txt @@ -16,12 +16,14 @@ See UPGRADING.txt for instructions. INSTALLATION AND USAGE: 1. Place the /min/ directory as a child of your DOCUMENT_ROOT -directory: i.e. you will have: /home/user/www/public_html/min +directory: i.e. you will have: /home/user/www/min 2. Open http://yourdomain/min/ in a web browser. This will forward you to the Minify URI Builder application, which will help you quickly start using Minify to serve content on your site. +See the User Guide: http://code.google.com/p/minify/wiki/UserGuide + UNIT TESTING: @@ -36,12 +38,6 @@ components with more verbose output.) 3. Remove /min_unit_tests/ from your DOCUMENT_ROOT when you are done. -EXTRAS: - -The min_extras folder contains files for benchmarking using Apache ab on Windows -and a couple single-use tools. DO NOT place this on your production server. - - FILE ENCODINGS Minify *should* work fine with files encoded in UTF-8 or other 8-bit diff --git a/min/.htaccess b/min/.htaccess index 42f13eb..06c1161 100644 --- a/min/.htaccess +++ b/min/.htaccess @@ -1,4 +1,13 @@ RewriteEngine on -RewriteRule ^([a-z]=.*) index.php?$1 [L,NE] - \ No newline at end of file + +# You may need RewriteBase on some servers +#RewriteBase /min + +# rewrite URLs like "/min/f=..." to "/min/?f=..." +RewriteRule ^([bfg]=.*) index.php?$1 [L,NE] + + +# In case AddOutputFilterByType has been added +SetEnv no-gzip + diff --git a/min/README.txt b/min/README.txt index a7cf774..fab1bb0 100644 --- a/min/README.txt +++ b/min/README.txt @@ -66,14 +66,14 @@ to the /js and /themes/default directories, use: $min_serveOptions['minApp']['allowDirs'] = array('//js', '//themes/default'); -GROUPS: FASTER PERFORMANCE AND BETTER URLS +GROUPS: NICER URLS -For the best performance, edit groupsConfig.php to pre-specify groups of files +For nicer URLs, edit groupsConfig.php to pre-specify groups of files to be combined under preset keys. E.g., here's an example configuration in groupsConfig.php: -return array( - 'js' => array('//js/Class.js', '//js/email.js') +return array( + 'js' => array('//js/Class.js', '//js/email.js') ); This pre-selects the following files to be combined under the key "js": diff --git a/min/builder/_index.js b/min/builder/_index.js index bbaadb4..90c3afd 100644 --- a/min/builder/_index.js +++ b/min/builder/_index.js @@ -202,7 +202,7 @@ var MUB = { $('#sources').html(''); $('#add button').click(MUB.addButtonClick); // make easier to copy text out of - $('#uriHtml, #groupConfig').click(function () { + $('#uriHtml, #groupConfig, #symlinkOpt').click(function () { this.select(); }).focus(function () { this.select(); @@ -223,10 +223,9 @@ var MUB = { return false; }).attr({title:'Add file +'}); } else { - // copy bookmarklet code into href - var bmUri = location.pathname.replace(/\/[^\/]*$/, '/bm.js').substr(1); + // setup bookmarklet 1 $.ajax({ - url : '../?f=' + bmUri + url : '../?f=' + location.pathname.replace(/\/[^\/]*$/, '/bm.js').substr(1) ,success : function (code) { $('#bm')[0].href = code .replace('%BUILDER_URL%', location.href) @@ -237,6 +236,14 @@ var MUB = { $.browser.msie && $('#getBm p:last').append(' Sorry, not supported in MSIE!'); MUB.addButtonClick(); } + // setup bookmarklet 2 + $.ajax({ + url : '../?f=' + location.pathname.replace(/\/[^\/]*$/, '/bm2.js').substr(1) + ,success : function (code) { + $('#bm2')[0].href = code.replace(/\n/g, ' '); + } + ,dataType : 'text' + }); MUB.checkRewrite(); } }; diff --git a/min/builder/bm2.js b/min/builder/bm2.js new file mode 100644 index 0000000..236402e --- /dev/null +++ b/min/builder/bm2.js @@ -0,0 +1,14 @@ +javascript:(function(){ + var d = document + ,c = d.cookie + ,m = c.match(/\bminDebug=([^; ]+)/) + ,v = m ? decodeURIComponent(m[1]) : '' + ,p = prompt('Debug Minify URIs on "' + location.hostname + '" \ncontaining: (empty to disable)', v) + ; + if (p === null) return; + p = p.replace(/\s+/g, ''); + v = (p === '') + ? 'minDebug=; expires=Fri, 27 Jul 2001 02:47:11 UTC; path=/' + : 'minDebug=' + encodeURIComponent(p) + '; path=/'; + d.cookie = v; +})(); \ No newline at end of file diff --git a/min/builder/index.php b/min/builder/index.php index 18ec939..093954b 100644 --- a/min/builder/index.php +++ b/min/builder/index.php @@ -8,6 +8,20 @@ if (phpversion() < 5) { $encodeOutput = (function_exists('gzdeflate') && !ini_get('zlib.output_compression')); +// recommend $min_symlinks setting for Apache UserDir +$symlinkOption = ''; +if (0 === strpos($_SERVER["SERVER_SOFTWARE"], 'Apache/') + && preg_match('@^/\\~(\\w+)/@', $_SERVER['REQUEST_URI'], $m) +) { + $userDir = DIRECTORY_SEPARATOR . $m[1] . DIRECTORY_SEPARATOR; + if (false !== strpos(__FILE__, $userDir)) { + $sm = array(); + $sm["//~{$m[1]}"] = dirname(dirname(__FILE__)); + $array = str_replace('array (', 'array(', var_export($sm, 1)); + $symlinkOption = "\$min_symlinks = $array;"; + } +} + require dirname(__FILE__) . '/../config.php'; if (! $min_enableBuilder) { @@ -18,8 +32,8 @@ if (! $min_enableBuilder) { ob_start(); ?> - Minify URI Builder + + +
Note: It looks like you're running Minify in a user + directory. You may need the following option in /min/config.php to have URIs + correctly rewritten in CSS output: +
+
+ +

Uh Oh. Minify was unable to - serve the Javascript for this app. Enable - FirePHP debugging and request the Minify URL directly. + serve the Javascript for this app. To troubleshoot this, + enable FirePHP debugging + and request the Minify URL directly. Hopefully the + FirePHP console will report the cause of the error.

-

Note: Please set $min_cachePath -in /min/config.php to improve performance.

+

Note: You can set $min_cachePath +in /min/config.php to slightly improve performance.

Note: Your webserver does not seem to @@ -111,14 +135,21 @@ in your list, and move any others to the top of the first file in your list

If you desire, you can use Minify URIs in imports and they will not be touched by Minify. E.g. @import "/min/?g=css2";

+

Debug Mode

+

When /min/config.php has $min_allowDebugFlag = true; + you can get debug output by appending &debug to a Minify URL, or + by sending the cookie minDebug=<match>, where minDebug=<match> + should match the Minify URIs you'd like to debug. This bookmarklet will allow you to + set this cookie.

+

Minify Debug (right-click, add to bookmarks)

+
-

Need help? Search or post to the Minify discussion list.

-

This app is minified :) view -source

+

Need help? Check the wiki, + or post to the discussion + list.

+

This app is minified :)

@@ -163,9 +194,22 @@ $(function () { ob_get_contents() +// setup Minify +set_include_path(dirname(__FILE__) . '/../lib' . PATH_SEPARATOR . get_include_path()); +require 'Minify.php'; +if (0 === stripos(PHP_OS, 'win')) { + Minify::setDocRoot(); // we may be on IIS +} +Minify::setCache( + isset($min_cachePath) ? $min_cachePath : '' + ,$min_cacheFileLocking +); +Minify::$uploaderHoursBehind = $min_uploaderHoursBehind; + +Minify::serve('Page', array( + 'content' => $content ,'id' => __FILE__ ,'lastModifiedTime' => max( // regenerate cache if either of these change @@ -174,17 +218,4 @@ $serveOpts = array( ) ,'minifyAll' => true ,'encodeOutput' => $encodeOutput -); -ob_end_clean(); - -set_include_path(dirname(__FILE__) . '/../lib' . PATH_SEPARATOR . get_include_path()); - -require 'Minify.php'; - -if (0 === stripos(PHP_OS, 'win')) { - Minify::setDocRoot(); // we may be on IIS -} -Minify::setCache(isset($min_cachePath) ? $min_cachePath : null); -Minify::$uploaderHoursBehind = $min_uploaderHoursBehind; - -Minify::serve('Page', $serveOpts); +)); diff --git a/min/config.php b/min/config.php index 927d2fe..7d7be0a 100644 --- a/min/config.php +++ b/min/config.php @@ -7,22 +7,12 @@ */ -/** - * In 'debug' mode, Minify can combine files with no minification and - * add comments to indicate line #s of the original files. - * - * To allow debugging, set this option to true and add "&debug=1" to - * a URI. E.g. /min/?f=script1.js,script2.js&debug=1 - */ -$min_allowDebugFlag = false; - - /** * Set to true to log messages to FirePHP (Firefox Firebug addon). * Set to false for no error logging (Minify may be slightly faster). * @link http://www.firephp.org/ * - * If you want to use a custom error logger, set this to your logger + * If you want to use a custom error logger, set this to your logger * instance. Your object should have a method log(string $message). * * @todo cache system does not have error logging yet. @@ -30,6 +20,21 @@ $min_allowDebugFlag = false; $min_errorLogger = false; +/** + * To allow debugging, you must set this option to true. + * + * Once true, you can send the cookie minDebug to request debug mode output. The + * cookie value should match the URIs you'd like to debug. E.g. to debug + * /min/f=file1.js send the cookie minDebug=file1.js + * You can manually enable debugging by appending "&debug" to a URI. + * E.g. /min/?f=script1.js,script2.js&debug + * + * In 'debug' mode, Minify combines files with no minification and adds comments + * to indicate line #s of the original files. + */ +$min_allowDebugFlag = false; + + /** * Allow use of the Minify URI Builder app. If you no longer need * this, set to false. diff --git a/min/index.php b/min/index.php index 288cbf3..352313f 100644 --- a/min/index.php +++ b/min/index.php @@ -35,8 +35,15 @@ foreach ($min_symlinks as $uri => $target) { $min_serveOptions['minApp']['allowDirs'][] = $target; } -if ($min_allowDebugFlag && isset($_GET['debug'])) { - $min_serveOptions['debug'] = true; +if ($min_allowDebugFlag) { + if (! empty($_COOKIE['minDebug']) + && false !== strpos($_SERVER['REQUEST_URI'], $_COOKIE['minDebug'])) { + $min_serveOptions['debug'] = true; + } + // allow GET to override + if (isset($_GET['debug'])) { + $min_serveOptions['debug'] = true; + } } if ($min_errorLogger) { diff --git a/min/lib/HTTP/ConditionalGet.php b/min/lib/HTTP/ConditionalGet.php index 4a0225a..26e413e 100644 --- a/min/lib/HTTP/ConditionalGet.php +++ b/min/lib/HTTP/ConditionalGet.php @@ -75,9 +75,8 @@ class HTTP_ConditionalGet { /** * @param array $spec options * - * 'isPublic': (bool) if true, the Cache-Control header will contain - * "public", allowing proxies to cache the content. Otherwise "private" will - * be sent, allowing only browser caching. (default false) + * 'isPublic': (bool) if false, the Cache-Control header will contain + * "private", allowing only browser caching. (default false) * * 'lastModifiedTime': (int) if given, both ETag AND Last-Modified headers * will be sent with content. This is recommended. @@ -150,7 +149,10 @@ class HTTP_ConditionalGet { } elseif (isset($spec['contentHash'])) { // Use the hash as the ETag $this->_setEtag($spec['contentHash'] . $etagAppend, $scope); } - $this->_headers['Cache-Control'] = "max-age={$maxAge}, {$scope}"; + $privacy = ($scope === 'private') + ? ', private' + : ''; + $this->_headers['Cache-Control'] = "max-age={$maxAge}{$privacy}"; // invalidate cache if disabled, otherwise check $this->cacheIsValid = (isset($spec['invalidate']) && $spec['invalidate']) ? false @@ -332,11 +334,8 @@ class HTTP_ConditionalGet { if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { return false; } - $ifModifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE']; - if (false !== ($semicolon = strrpos($ifModifiedSince, ';'))) { - // IE has tacked on extra data to this header, strip it - $ifModifiedSince = substr($ifModifiedSince, 0, $semicolon); - } + // strip off IE's extra data (semicolon) + list($ifModifiedSince) = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE'], 2); if (strtotime($ifModifiedSince) >= $this->_lmTime) { // Apache 2.2's behavior. If there was no ETag match, send the // non-encoded version of the ETag value. diff --git a/min/lib/JSMin.php b/min/lib/JSMin.php index 74d3422..288a0e2 100644 --- a/min/lib/JSMin.php +++ b/min/lib/JSMin.php @@ -171,7 +171,8 @@ class JSMin { } if (ord($this->a) <= self::ORD_LF) { throw new JSMin_UnterminatedStringException( - "Unterminated String: {$str}"); + "JSMin: Unterminated String at byte " + . $this->inputIndex . ": {$str}"); } $str .= $this->a; if ($this->a === '\\') { @@ -198,7 +199,8 @@ class JSMin { $pattern .= $this->a; } elseif (ord($this->a) <= self::ORD_LF) { throw new JSMin_UnterminatedRegExpException( - "Unterminated RegExp: {$pattern}"); + "JSMin: Unterminated RegExp at byte " + . $this->inputIndex .": {$pattern}"); } $this->output .= $this->a; } @@ -310,7 +312,9 @@ class JSMin { return ' '; } } elseif ($get === null) { - throw new JSMin_UnterminatedCommentException("Unterminated Comment: /*{$comment}"); + throw new JSMin_UnterminatedCommentException( + "JSMin: Unterminated comment at byte " + . $this->inputIndex . ": /*{$comment}"); } $comment .= $get; } diff --git a/min/lib/Minify.php b/min/lib/Minify.php index ba40d7e..5cb2405 100644 --- a/min/lib/Minify.php +++ b/min/lib/Minify.php @@ -227,6 +227,8 @@ class Minify { ); if (self::$_options['maxAge'] > 0) { $cgOptions['maxAge'] = self::$_options['maxAge']; + } elseif (self::$_options['debug']) { + $cgOptions['invalidate'] = true; } $cg = new HTTP_ConditionalGet($cgOptions); if ($cg->cacheIsValid) { @@ -267,7 +269,7 @@ class Minify { // 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 // memory. - $cacheId = 'minify_' . self::_getCacheId(); + $cacheId = self::_getCacheId(); $fullCacheId = (self::$_options['encodeMethod']) ? $cacheId . '.gz' : $cacheId; @@ -489,7 +491,12 @@ class Minify { if ($minifier) { self::$_controller->loadMinifier($minifier); // get source content and minify it - $pieces[] = call_user_func($minifier, $source->getContent(), $options); + try { + $pieces[] = call_user_func($minifier, $source->getContent(), $options); + } catch (Exception $e) { + throw new Exception("Exception in " . $source->getId() . + ": " . $e->getMessage()); + } } else { $pieces[] = $source->getContent(); } @@ -515,17 +522,23 @@ class Minify { * * Any settings that could affect output are taken into consideration * + * @param string $prefix + * * @return string */ - protected static function _getCacheId() + protected static function _getCacheId($prefix = 'minify') { - return md5(serialize(array( + $name = preg_replace('/[^a-zA-Z0-9\\.=_,]/', '', self::$_controller->selectionId); + $name = preg_replace('/\\.+/', '.', $name); + $name = substr($name, 0, 250 - 34 - strlen($prefix)); + $md5 = md5(serialize(array( Minify_Source::getDigest(self::$_controller->sources) ,self::$_options['minifiers'] ,self::$_options['minifierOptions'] ,self::$_options['postprocessor'] ,self::$_options['bubbleCssImports'] ))); + return "{$prefix}_{$name}_{$md5}"; } /** diff --git a/min/lib/Minify/Controller/Base.php b/min/lib/Minify/Controller/Base.php index 736cf59..1e59ed5 100644 --- a/min/lib/Minify/Controller/Base.php +++ b/min/lib/Minify/Controller/Base.php @@ -144,6 +144,15 @@ abstract class Minify_Controller_Base { */ public $sources = array(); + /** + * The setupSources() method may choose to set this, making it easier to + * recognize a particular set of sources/settings in the cache folder. It + * will be filtered and truncated to make the final cache id <= 250 bytes. + * + * @var string short name to place inside cache id + */ + public $selectionId = ''; + /** * Mix in default controller options with user-given options * diff --git a/min/lib/Minify/Controller/MinApp.php b/min/lib/Minify/Controller/MinApp.php index f3c38f4..98b4e2c 100644 --- a/min/lib/Minify/Controller/MinApp.php +++ b/min/lib/Minify/Controller/MinApp.php @@ -40,7 +40,8 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { $this->log("A group configuration for \"{$_GET['g']}\" was not set"); return $options; } - + + $this->selectionId = "g=" . $_GET['g']; $files = $cOptions['groups'][$_GET['g']]; // if $files is a single object, casting will break it if (is_object($files)) { @@ -70,7 +71,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { // respond to. Ideally there should be only one way to reference a file. if (// verify at least one file, files are single comma separated, // and are all same extension - ! preg_match('/^[^,]+\\.(css|js)(?:,[^,]+\\.\\1)*$/', $_GET['f']) + ! preg_match('/^[^,]+\\.(css|js)(?:,[^,]+\\.\\1)*$/', $_GET['f'], $m) // no "//" || strpos($_GET['f'], '//') !== false // no "\" @@ -81,11 +82,13 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { $this->log("GET param 'f' invalid (see MinApp.php line 63)"); return $options; } + $ext = ".{$m[1]}"; $files = explode(',', $_GET['f']); if ($files != array_unique($files)) { $this->log("Duplicate files specified"); return $options; } + if (isset($_GET['b'])) { // check for validity if (preg_match('@^[^/]+(?:/[^/]+)*$@', $_GET['b']) @@ -104,6 +107,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { foreach ((array)$cOptions['allowDirs'] as $allowDir) { $allowDirs[] = realpath(str_replace('//', $_SERVER['DOCUMENT_ROOT'] . '/', $allowDir)); } + $basenames = array(); // just for cache id foreach ($files as $file) { $path = $_SERVER['DOCUMENT_ROOT'] . $base . $file; $file = realpath($path); @@ -115,8 +119,10 @@ class Minify_Controller_MinApp extends Minify_Controller_Base { return $options; } else { $sources[] = $this->_getFileSource($file, $cOptions); + $basenames[] = basename($file, $ext); } } + $this->selectionId = implode(',', $basenames) . $ext; } if ($sources) { $this->sources = $sources; diff --git a/min/lib/Minify/Controller/Page.php b/min/lib/Minify/Controller/Page.php index fa4599a..de471e1 100644 --- a/min/lib/Minify/Controller/Page.php +++ b/min/lib/Minify/Controller/Page.php @@ -40,14 +40,19 @@ class Minify_Controller_Page extends Minify_Controller_Base { $sourceSpec = array( 'filepath' => $options['file'] ); + $f = $options['file']; } else { // strip controller options $sourceSpec = array( 'content' => $options['content'] ,'id' => $options['id'] ); + $f = $options['id']; unset($options['content'], $options['id']); } + // something like "builder,index.php" or "directory,file.html" + $this->selectionId = strtr(substr($f, 1 + strlen(dirname(dirname($f)))), '/\\', ',,'); + if (isset($options['minifyAll'])) { // this will be the 2nd argument passed to Minify_HTML::minify() $sourceSpec['minifyOptions'] = array( diff --git a/min_unit_tests/.htaccess b/min_unit_tests/.htaccess new file mode 100644 index 0000000..4e05c4c --- /dev/null +++ b/min_unit_tests/.htaccess @@ -0,0 +1,4 @@ + +# In case AddOutputFilterByType has been added +SetEnv no-gzip + diff --git a/min_unit_tests/test_Minify.php b/min_unit_tests/test_Minify.php index 9a4e6ec..95e5d64 100644 --- a/min_unit_tests/test_Minify.php +++ b/min_unit_tests/test_Minify.php @@ -29,7 +29,7 @@ function test_Minify() 'Vary' => 'Accept-Encoding', 'Last-Modified' => gmdate('D, d M Y H:i:s \G\M\T', $lastModified), 'ETag' => "\"pub{$lastModified}\"", - 'Cache-Control' => 'max-age=1800, public', + 'Cache-Control' => 'max-age=1800', '_responseCode' => 'HTTP/1.0 304 Not Modified', ) ); @@ -49,7 +49,7 @@ function test_Minify() assertTrue( ! class_exists('Minify_CSS', false) - && ! class_exists('Minify_Cache', false) + && ! class_exists('Minify_Cache_File', false) ,'Minify : cache, and minifier classes aren\'t loaded for 304s' ); @@ -70,11 +70,13 @@ function test_Minify() 'Vary' => 'Accept-Encoding', 'Last-Modified' => gmdate('D, d M Y H:i:s \G\M\T', $lastModified), 'ETag' => "\"pub{$lastModified}\"", - 'Cache-Control' => 'max-age=86400, public', + 'Cache-Control' => 'max-age=86400', 'Content-Length' => strlen($content), 'Content-Type' => 'application/x-javascript; charset=utf-8', ) ); + unset($_SERVER['HTTP_IF_NONE_MATCH']); + unset($_SERVER['HTTP_IF_MODIFIED_SINCE']); $output = Minify::serve('Files', array( 'files' => array( $minifyTestPath . '/email.js' @@ -185,7 +187,7 @@ function test_Minify() 'Vary' => 'Accept-Encoding', 'Last-Modified' => gmdate('D, d M Y H:i:s \G\M\T', $lastModified), 'ETag' => "\"pub{$lastModified}\"", - 'Cache-Control' => 'max-age=0, public', + 'Cache-Control' => 'max-age=0', 'Content-Length' => strlen($expectedContent), 'Content-Type' => 'text/css; charset=utf-8', ) diff --git a/min_unit_tests/test_environment.php b/min_unit_tests/test_environment.php index 53f0d23..3d4162c 100644 --- a/min_unit_tests/test_environment.php +++ b/min_unit_tests/test_environment.php @@ -1,5 +1,7 @@ array( - 'method' => "GET", - 'header' => "Accept-Encoding: deflate, gzip\r\n" - ) - ))); - - $meta = stream_get_meta_data($fp); - - $passed = true; - foreach ($meta['wrapper_data'] as $i => $header) { - if ((preg_match('@^Content-Length: (\\d+)$@i', $header, $m) && $m[1] !== '6') - || preg_match('@^Content-Encoding:@i', $header, $m) - ) { - $passed = false; - break; - } - } - $streamContents = stream_get_contents($fp); - if ($passed && $streamContents !== 'World!') { - $passed = false; - } - assertTrue( - $passed - ,'environment : PHP/server does not auto-HTTP-encode content' + + $testJs = _test_environment_getHello($thisUrl . '?hello=js'); + $passed = assertTrue( + $testJs['length'] == 6 + ,'environment : PHP/server should not auto-encode application/x-javascript output' ); - fclose($fp); - - if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { - if (! $passed) { - echo "\nReturned content should be 6 bytes and not HTTP encoded.\n" - . "Headers returned by: {$thisUrl}?hello=1\n\n"; - var_export($meta['wrapper_data']); + + $testCss = _test_environment_getHello($thisUrl . '?hello=css'); + $passed = $passed && assertTrue( + $testCss['length'] == 6 + ,'environment : PHP/server should not auto-encode text/css output' + ); + + $testHtml = _test_environment_getHello($thisUrl . '?hello=html'); + $passed = $passed && assertTrue( + $testHtml['length'] == 6 + ,'environment : PHP/server should not auto-encode text/html output' + ); + + if (! $passed) { + $testFake = _test_environment_getHello($thisUrl . '?hello=faketype'); + if ($testFake['length'] == 6) { + echo "!NOTE: environment : Server does not auto-encode arbitrary types. This\n" + . " may indicate that the auto-encoding is caused by Apache's \n" + . " AddOutputFilterByType."; } } } +function _test_environment_getHello($url) +{ + $fp = fopen($url, 'r', false, stream_context_create(array( + 'http' => array( + 'method' => "GET", + 'timeout' => '10', + 'header' => "Accept-Encoding: deflate, gzip\r\n", + ) + ))); + $meta = stream_get_meta_data($fp); + $encoding = ''; + $length = 0; + foreach ($meta['wrapper_data'] as $i => $header) { + if (preg_match('@^Content-Length:\\s*(\\d+)$@i', $header, $m)) { + $length = $m[1]; + } elseif (preg_match('@^Content-Encoding:\\s*(\\S+)$@i', $header, $m)) { + if ($m[1] !== 'identity') { + $encoding = $m[1]; + } + } + } + $streamContents = stream_get_contents($fp); + fclose($fp); + + if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) { + if ($length != 6) { + echo "\nReturned content should be 6 bytes and not HTTP encoded.\n" + . "Headers returned by: {$url}\n\n"; + var_export($meta['wrapper_data']); + echo "\n\n"; + } + } + + return array( + 'length' => $length + ,'encoding' => $encoding + ,'bytes' => $streamContents + ); +} + test_environment();