getHeaders() public méthode

In the case of 304 responses, this array will only contain the response code header: array('_responseCode' => 'HTTP/1.0 304 Not Modified') Otherwise something like: array( 'Cache-Control' => 'max-age=0, public' ,'ETag' => '"foobar"' )
public getHeaders ( ) : array
Résultat array
function test_HTTP_ConditionalGet()
{
    global $thisDir;
    $lmTime = time() - 900;
    $gmtTime = gmdate('D, d M Y H:i:s \\G\\M\\T', $lmTime);
    $tests = array(array('desc' => 'client has valid If-Modified-Since', 'inm' => null, 'ims' => $gmtTime, 'exp' => array('Vary' => 'Accept-Encoding', 'Last-Modified' => $gmtTime, 'ETag' => "\"pri{$lmTime}\"", 'Cache-Control' => 'max-age=0, private', '_responseCode' => 'HTTP/1.0 304 Not Modified', 'isValid' => true)), array('desc' => 'client has valid If-Modified-Since with trailing semicolon', 'inm' => null, 'ims' => $gmtTime . ';', 'exp' => array('Vary' => 'Accept-Encoding', 'Last-Modified' => $gmtTime, 'ETag' => "\"pri{$lmTime}\"", 'Cache-Control' => 'max-age=0, private', '_responseCode' => 'HTTP/1.0 304 Not Modified', 'isValid' => true)), array('desc' => 'client has valid ETag (non-encoded version)', 'inm' => "\"badEtagFoo\", \"pri{$lmTime}\"", 'ims' => null, 'exp' => array('Vary' => 'Accept-Encoding', 'Last-Modified' => $gmtTime, 'ETag' => "\"pri{$lmTime}\"", 'Cache-Control' => 'max-age=0, private', '_responseCode' => 'HTTP/1.0 304 Not Modified', 'isValid' => true)), array('desc' => 'client has valid ETag (gzip version)', 'inm' => "\"badEtagFoo\", \"pri{$lmTime};gz\"", 'ims' => null, 'exp' => array('Vary' => 'Accept-Encoding', 'Last-Modified' => $gmtTime, 'ETag' => "\"pri{$lmTime};gz\"", 'Cache-Control' => 'max-age=0, private', '_responseCode' => 'HTTP/1.0 304 Not Modified', 'isValid' => true)), array('desc' => 'no conditional get', 'inm' => null, 'ims' => null, 'exp' => array('Vary' => 'Accept-Encoding', 'Last-Modified' => $gmtTime, 'ETag' => "\"pri{$lmTime};gz\"", 'Cache-Control' => 'max-age=0, private', 'isValid' => false)), array('desc' => 'client has invalid ETag', 'inm' => '"pri' . ($lmTime - 300) . '"', 'ims' => null, 'exp' => array('Vary' => 'Accept-Encoding', 'Last-Modified' => $gmtTime, 'ETag' => "\"pri{$lmTime};gz\"", 'Cache-Control' => 'max-age=0, private', 'isValid' => false)), array('desc' => 'client has invalid If-Modified-Since', 'inm' => null, 'ims' => gmdate('D, d M Y H:i:s \\G\\M\\T', $lmTime - 300), 'exp' => array('Vary' => 'Accept-Encoding', 'Last-Modified' => $gmtTime, 'ETag' => "\"pri{$lmTime};gz\"", 'Cache-Control' => 'max-age=0, private', 'isValid' => false)));
    foreach ($tests as $test) {
        // setup env
        if (null === $test['inm']) {
            unset($_SERVER['HTTP_IF_NONE_MATCH']);
        } else {
            $_SERVER['HTTP_IF_NONE_MATCH'] = get_magic_quotes_gpc() ? addslashes($test['inm']) : $test['inm'];
        }
        if (null === $test['ims']) {
            unset($_SERVER['HTTP_IF_MODIFIED_SINCE']);
        } else {
            $_SERVER['HTTP_IF_MODIFIED_SINCE'] = $test['ims'];
        }
        $exp = $test['exp'];
        $cg = new HTTP_ConditionalGet(array('lastModifiedTime' => $lmTime, 'encoding' => 'x-gzip'));
        $ret = $cg->getHeaders();
        $ret['isValid'] = $cg->cacheIsValid;
        $passed = assertTrue($exp == $ret, 'HTTP_ConditionalGet : ' . $test['desc']);
        if (__FILE__ === realpath($_SERVER['SCRIPT_FILENAME'])) {
            echo "\n--- INM = {$test['inm']} / IMS = {$test['ims']}\n";
            echo "Expected = " . preg_replace('/\\s+/', ' ', var_export($exp, 1)) . "\n";
            echo "Returned = " . preg_replace('/\\s+/', ' ', var_export($ret, 1)) . "\n\n";
        }
    }
}
Exemple #2
0
 /**
  * Serve a request for a minified file. 
  * 
  * Here are the available options and defaults in the base controller:
  * 
  * 'isPublic' : send "public" instead of "private" in Cache-Control 
  * headers, allowing shared caches to cache the output. (default true)
  * 
  * 'quiet' : set to true to have serve() return an array rather than sending
  * any headers/output (default false)
  * 
  * 'encodeOutput' : set to false to disable content encoding, and not send
  * the Vary header (default true)
  * 
  * 'encodeMethod' : generally you should let this be determined by 
  * HTTP_Encoder (leave null), but you can force a particular encoding
  * to be returned, by setting this to 'gzip' or '' (no encoding)
  * 
  * 'encodeLevel' : level of encoding compression (0 to 9, default 9)
  * 
  * 'contentTypeCharset' : appended to the Content-Type header sent. Set to a falsey
  * value to remove. (default 'utf-8')  
  * 
  * 'maxAge' : set this to the number of seconds the client should use its cache
  * before revalidating with the server. This sets Cache-Control: max-age and the
  * Expires header. Unlike the old 'setExpires' setting, this setting will NOT
  * prevent conditional GETs. Note this has nothing to do with server-side caching.
  * 
  * 'rewriteCssUris' : If true, serve() will automatically set the 'currentDir'
  * minifier option to enable URI rewriting in CSS files (default true)
  * 
  * 'bubbleCssImports' : If true, all @import declarations in combined CSS
  * files will be move to the top. Note this may alter effective CSS values
  * due to a change in order. (default false)
  * 
  * 'debug' : set to true to minify all sources with the 'Lines' controller, which
  * eases the debugging of combined files. This also prevents 304 responses.
  * @see Minify_Lines::minify()
  * 
  * '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:
  * <code>
  * // call customCssMinifier($css) for all CSS minification
  * $options['minifiers'][Minify::TYPE_CSS] = 'customCssMinifier';
  * 
  * // don't minify Javascript at all
  * $options['minifiers'][Minify::TYPE_JS] = '';
  * </code>
  * 
  * '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: 
  * <code>
  * // give CSS minifier array('optionName' => 'optionValue') as 2nd argument 
  * $options['minifierOptions'][Minify::TYPE_CSS]['optionName'] = 'optionValue';
  * </code>
  * 
  * 'contentType' : (optional) this is only needed if your file extension is not 
  * js/css/html. The given content-type will be sent regardless of source file
  * extension, so this should not be used in a Groups config with other
  * Javascript/CSS files.
  * 
  * Any controller options are documented in that controller's setupSources() method.
  * 
  * @param mixed $controller instance of subclass of Minify_Controller_Base or string
  * name of controller. E.g. 'Files'
  * 
  * @param array $options controller/serve options
  * 
  * @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).
  */
 public static function serve($controller, $options = array())
 {
     if (!self::$isDocRootSet && 0 === stripos(PHP_OS, 'win')) {
         self::setDocRoot();
     }
     if (is_string($controller)) {
         // make $controller into object
         $class = 'Minify_Controller_' . $controller;
         if (!class_exists($class, false)) {
             require_once "Minify/Controller/" . str_replace('_', '/', $controller) . ".php";
         }
         $controller = new $class();
         /* @var Minify_Controller_Base $controller */
     }
     // set up controller sources and mix remaining options with
     // controller defaults
     $options = $controller->setupSources($options);
     $options = $controller->analyzeSources($options);
     self::$_options = $controller->mixInDefaultOptions($options);
     // check request validity
     if (!$controller->sources) {
         // invalid request!
         if (!self::$_options['quiet']) {
             self::_errorExit(self::$_options['badRequestHeader'], self::URL_DEBUG);
         } else {
             list(, $statusCode) = explode(' ', self::$_options['badRequestHeader']);
             return array('success' => false, 'statusCode' => (int) $statusCode, 'content' => '', 'headers' => array());
         }
     }
     self::$_controller = $controller;
     if (self::$_options['debug']) {
         self::_setupDebug($controller->sources);
         self::$_options['maxAge'] = 0;
     }
     // determine encoding
     if (self::$_options['encodeOutput']) {
         $sendVary = true;
         if (self::$_options['encodeMethod'] !== null) {
             // controller specifically requested this
             $contentEncoding = self::$_options['encodeMethod'];
         } else {
             // sniff request header
             require_once 'HTTP/Encoder.php';
             // depending on what the client accepts, $contentEncoding may be
             // 'x-gzip' while our internal encodeMethod is 'gzip'. Calling
             // getAcceptedEncoding(false, false) leaves out compress and deflate as options.
             list(self::$_options['encodeMethod'], $contentEncoding) = HTTP_Encoder::getAcceptedEncoding(false, false);
             $sendVary = !HTTP_Encoder::isBuggyIe();
         }
     } else {
         self::$_options['encodeMethod'] = '';
         // identity (no encoding)
     }
     // check client cache
     require_once 'HTTP/ConditionalGet.php';
     $cgOptions = array('lastModifiedTime' => self::$_options['lastModifiedTime'], 'isPublic' => self::$_options['isPublic'], 'encoding' => self::$_options['encodeMethod']);
     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) {
         // client's cache is valid
         if (!self::$_options['quiet']) {
             $cg->sendHeaders();
             return;
         } else {
             return array('success' => true, 'statusCode' => 304, 'content' => '', 'headers' => $cg->getHeaders());
         }
     } else {
         // client will need output
         $headers = $cg->getHeaders();
         unset($cg);
     }
     if (self::$_options['contentType'] === self::TYPE_CSS && self::$_options['rewriteCssUris']) {
         foreach ($controller->sources as $key => $source) {
             if ($source->filepath && !isset($source->minifyOptions['currentDir']) && !isset($source->minifyOptions['prependRelativePath'])) {
                 $source->minifyOptions['currentDir'] = dirname($source->filepath);
             }
         }
     }
     // check server cache
     if (null !== self::$_cache && !self::$_options['debug']) {
         // 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
         // memory.
         $cacheId = self::_getCacheId();
         $fullCacheId = self::$_options['encodeMethod'] ? $cacheId . '.gz' : $cacheId;
         // check cache for valid entry
         $cacheIsReady = self::$_cache->isValid($fullCacheId, self::$_options['lastModifiedTime']);
         if ($cacheIsReady) {
             $cacheContentLength = self::$_cache->getSize($fullCacheId);
         } else {
             // generate & cache content
             try {
                 $content = self::_combineMinify();
             } catch (Exception $e) {
                 self::$_controller->log($e->getMessage());
                 if (!self::$_options['quiet']) {
                     self::_errorExit(self::$_options['errorHeader'], self::URL_DEBUG);
                 }
                 throw $e;
             }
             self::$_cache->store($cacheId, $content);
             if (function_exists('gzencode')) {
                 self::$_cache->store($cacheId . '.gz', gzencode($content, self::$_options['encodeLevel']));
             }
         }
     } else {
         // no cache
         $cacheIsReady = false;
         try {
             $content = self::_combineMinify();
         } catch (Exception $e) {
             self::$_controller->log($e->getMessage());
             if (!self::$_options['quiet']) {
                 self::_errorExit(self::$_options['errorHeader'], self::URL_DEBUG);
             }
             throw $e;
         }
     }
     if (!$cacheIsReady && self::$_options['encodeMethod']) {
         // still need to encode
         $content = gzencode($content, self::$_options['encodeLevel']);
     }
     // add headers
     $headers['Content-Length'] = $cacheIsReady ? $cacheContentLength : (function_exists('mb_strlen') && (int) ini_get('mbstring.func_overload') & 2 ? mb_strlen($content, '8bit') : strlen($content));
     $headers['Content-Type'] = self::$_options['contentTypeCharset'] ? self::$_options['contentType'] . '; charset=' . self::$_options['contentTypeCharset'] : self::$_options['contentType'];
     if (self::$_options['encodeMethod'] !== '') {
         $headers['Content-Encoding'] = $contentEncoding;
     }
     if (self::$_options['encodeOutput'] && $sendVary) {
         $headers['Vary'] = 'Accept-Encoding';
     }
     if (!self::$_options['quiet']) {
         // output headers & content
         foreach ($headers as $name => $val) {
             header($name . ': ' . $val);
         }
         if ($cacheIsReady) {
             self::$_cache->display($fullCacheId);
         } else {
             echo $content;
         }
     } else {
         return array('success' => true, 'statusCode' => 200, 'content' => $cacheIsReady ? self::$_cache->fetch($fullCacheId) : $content, 'headers' => $headers);
     }
 }
Exemple #3
0
 /**
  * Serve a request for a minified file.
  *
  * Here are the available options and defaults in the base controller:
  *
  * 'isPublic' : send "public" instead of "private" in Cache-Control
  * headers, allowing shared caches to cache the output. (default true)
  *
  * 'quiet' : set to true to have serve() return an array rather than sending
  * any headers/output (default false)
  *
  * 'encodeOutput' : set to false to disable content encoding, and not send
  * the Vary header (default true)
  *
  * 'encodeMethod' : generally you should let this be determined by
  * HTTP_Encoder (leave null), but you can force a particular encoding
  * to be returned, by setting this to 'gzip' or '' (no encoding)
  *
  * 'encodeLevel' : level of encoding compression (0 to 9, default 9)
  *
  * 'contentTypeCharset' : appended to the Content-Type header sent. Set to a falsey
  * value to remove. (default 'utf-8')
  *
  * 'maxAge' : set this to the number of seconds the client should use its cache
  * before revalidating with the server. This sets Cache-Control: max-age and the
  * Expires header. Unlike the old 'setExpires' setting, this setting will NOT
  * prevent conditional GETs. Note this has nothing to do with server-side caching.
  *
  * 'rewriteCssUris' : If true, serve() will automatically set the 'currentDir'
  * minifier option to enable URI rewriting in CSS files (default true)
  *
  * 'bubbleCssImports' : If true, all @import declarations in combined CSS
  * files will be move to the top. Note this may alter effective CSS values
  * due to a change in order. (default false)
  *
  * 'debug' : set to true to minify all sources with the 'Lines' controller, which
  * eases the debugging of combined files. This also prevents 304 responses.
  * @see Minify_Lines::minify()
  *
  * '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:
  * <code>
  * // call customCssMinifier($css) for all CSS minification
  * $options['minifiers'][Minify::TYPE_CSS] = 'customCssMinifier';
  *
  * // don't minify Javascript at all
  * $options['minifiers'][Minify::TYPE_JS] = '';
  * </code>
  *
  * '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:
  * <code>
  * // give CSS minifier array('optionName' => 'optionValue') as 2nd argument
  * $options['minifierOptions'][Minify::TYPE_CSS]['optionName'] = 'optionValue';
  * </code>
  *
  * 'contentType' : (optional) this is only needed if your file extension is not
  * js/css/html. The given content-type will be sent regardless of source file
  * extension, so this should not be used in a Groups config with other
  * Javascript/CSS files.
  *
  * Any controller options are documented in that controller's setupSources() method.
  *
  * @param mixed instance of subclass of Minify_Controller_Base or string name of
  * controller. E.g. 'Files'
  *
  * @param array $options controller/serve options
  *
  * @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).
  */
 public static function serve($controller, $options = array())
 {
     if (is_string($controller)) {
         // make $controller into object
         $class = 'Minify_Controller_' . $controller;
         if (!class_exists($class, false)) {
             require_once W3TC_LIB_MINIFY_DIR . "/Minify/Controller/" . str_replace('_', '/', $controller) . ".php";
         }
         $controller = new $class();
     }
     // set up controller sources and mix remaining options with
     // controller defaults
     $options = $controller->setupSources($options);
     $options = $controller->analyzeSources($options);
     self::$_options = $controller->mixInDefaultOptions($options);
     // check request validity
     if (!$controller->sources) {
         // invalid request!
         if (!self::$_options['quiet']) {
             header(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());
         }
     }
     self::$_controller = $controller;
     if (self::$_options['debug']) {
         self::_setupDebug($controller->sources);
         self::$_options['maxAge'] = 0;
     }
     // determine encoding
     if (self::$_options['encodeOutput']) {
         if (self::$_options['encodeMethod'] !== null) {
             // controller specifically requested this
             $contentEncoding = self::$_options['encodeMethod'];
         } else {
             // sniff request header
             require_once W3TC_LIB_MINIFY_DIR . '/HTTP/Encoder.php';
             // depending on what the client accepts, $contentEncoding may be
             // 'x-gzip' while our internal encodeMethod is 'gzip'. Calling
             // getAcceptedEncoding(false, false) leaves out compress and deflate as options.
             list(self::$_options['encodeMethod'], $contentEncoding) = HTTP_Encoder::getAcceptedEncoding(self::$_options['encodeOutput']);
         }
     } else {
         self::$_options['encodeMethod'] = '';
         // identity (no encoding)
     }
     // check client cache
     require_once W3TC_LIB_MINIFY_DIR . '/HTTP/ConditionalGet.php';
     $cgOptions = array('cacheHeaders' => self::$_options['cacheHeaders'], 'lastModifiedTime' => self::$_options['lastModifiedTime'], 'isPublic' => self::$_options['isPublic'], 'encoding' => self::$_options['encodeMethod']);
     if (self::$_options['maxAge'] > 0) {
         $cgOptions['maxAge'] = self::$_options['maxAge'];
     }
     $cg = new HTTP_ConditionalGet($cgOptions);
     if ($cg->cacheIsValid) {
         // client's cache is valid
         if (!self::$_options['quiet']) {
             $cg->sendHeaders();
             return;
         } else {
             return array('success' => true, 'statusCode' => 304, 'content' => '', 'headers' => $cg->getHeaders());
         }
     } else {
         // client will need output
         $headers = $cg->getHeaders();
         unset($cg);
     }
     if (self::$_options['contentType'] === self::TYPE_CSS) {
         reset($controller->sources);
         while (list($key, $source) = each($controller->sources)) {
             if (self::$_options['rewriteCssUris'] && $source->filepath && !isset($source->minifyOptions['currentDir']) && !isset($source->minifyOptions['prependRelativePath'])) {
                 $source->minifyOptions['currentDir'] = dirname($source->filepath);
             }
             $source->minifyOptions['processCssImports'] = self::$_options['processCssImports'];
         }
     }
     // check server cache
     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
         // memory.
         $cacheId = self::_getCacheId();
         $fullCacheId = self::$_options['encodeMethod'] ? $cacheId . '.' . self::$_options['encodeMethod'] : $cacheId;
         // check cache for valid entry
         $cacheIsReady = self::$_cache->isValid($fullCacheId, self::$_options['lastModifiedTime']);
         if ($cacheIsReady) {
             $cacheContentLength = self::$_cache->getSize($fullCacheId);
         } else {
             // generate & cache content
             $content = self::_combineMinify();
             self::$_cache->store($cacheId, $content);
             if (self::$_options['encodeOutput'] && function_exists('gzencode')) {
                 self::$_cache->store($cacheId . '.' . self::$_options['encodeMethod'], gzencode($content, self::$_options['encodeLevel']));
             }
         }
     } else {
         // no cache
         $cacheIsReady = false;
         $content = self::_combineMinify();
     }
     if (!$cacheIsReady) {
         switch (self::$_options['encodeMethod']) {
             case 'gzip':
                 $content = gzencode($content, self::$_options['encodeLevel']);
                 break;
             case 'deflate':
                 $content = gzdeflate($content, self::$_options['encodeLevel']);
                 break;
         }
         // still need to encode
     }
     // add headers
     $headers['Content-Length'] = $cacheIsReady ? $cacheContentLength : strlen($content);
     $headers['Content-Type'] = self::$_options['contentTypeCharset'] ? self::$_options['contentType'] . '; charset=' . self::$_options['contentTypeCharset'] : self::$_options['contentType'];
     if (self::$_options['encodeMethod'] !== '') {
         $headers['Content-Encoding'] = $contentEncoding;
     }
     if (self::$_options['encodeOutput']) {
         $headers['Vary'] = 'Accept-Encoding';
     }
     if (!self::$_options['quiet']) {
         // output headers & content
         foreach ($headers as $name => $val) {
             header($name . ': ' . $val);
         }
         if ($cacheIsReady) {
             self::$_cache->display($fullCacheId);
         } else {
             echo $content;
         }
     } else {
         return array('success' => true, 'statusCode' => 200, 'content' => $cacheIsReady ? self::$_cache->fetch($fullCacheId) : $content, 'headers' => $headers);
     }
 }
Exemple #4
0
 /**
  * Serve a request for a minified file. 
  * 
  * Here are the available options and defaults:
  * 
  * 'isPublic' : send "public" instead of "private" in Cache-Control 
  * headers, allowing shared caches to cache the output. (default true)
  * 
  * 'quiet' : set to true to have serve() return an array rather than sending
  * any headers/output (default false)
  * 
  * 'encodeOutput' : set to false to disable content encoding, and not send
  * the Vary header (default true)
  * 
  * 'encodeMethod' : generally you should let this be determined by 
  * HTTP_Encoder (leave null), but you can force a particular encoding
  * to be returned, by setting this to 'gzip' or '' (no encoding)
  * 
  * 'encodeLevel' : level of encoding compression (0 to 9, default 9)
  * 
  * 'contentTypeCharset' : appended to the Content-Type header sent. Set to a falsey
  * value to remove. (default 'utf-8')  
  * 
  * 'maxAge' : set this to the number of seconds the client should use its cache
  * before revalidating with the server. This sets Cache-Control: max-age and the
  * Expires header. Unlike the old 'setExpires' setting, this setting will NOT
  * prevent conditional GETs. Note this has nothing to do with server-side caching.
  * 
  * 'rewriteCssUris' : If true, serve() will automatically set the 'currentDir'
  * minifier option to enable URI rewriting in CSS files (default true)
  * 
  * 'bubbleCssImports' : If true, all @import declarations in combined CSS
  * files will be move to the top. Note this may alter effective CSS values
  * due to a change in order. (default false)
  * 
  * 'debug' : set to true to minify all sources with the 'Lines' controller, which
  * eases the debugging of combined files. This also prevents 304 responses.
  * @see Minify_Lines::minify()
  *
  * 'concatOnly' : set to true to disable minification and simply concatenate the files.
  * For JS, no minifier will be used. For CSS, only URI rewriting is still performed.
  * 
  * '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:
  * <code>
  * // call customCssMinifier($css) for all CSS minification
  * $options['minifiers'][Minify::TYPE_CSS] = 'customCssMinifier';
  * 
  * // don't minify Javascript at all
  * $options['minifiers'][Minify::TYPE_JS] = '';
  * </code>
  * 
  * '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: 
  * <code>
  * // give CSS minifier array('optionName' => 'optionValue') as 2nd argument 
  * $options['minifierOptions'][Minify::TYPE_CSS]['optionName'] = 'optionValue';
  * </code>
  * 
  * 'contentType' : (optional) this is only needed if your file extension is not 
  * js/css/html. The given content-type will be sent regardless of source file
  * extension, so this should not be used in a Groups config with other
  * Javascript/CSS files.
  *
  * 'importWarning' : serve() will check CSS files for @import declarations that
  * appear too late in the combined stylesheet. If found, serve() will prepend
  * the output with this warning. To disable this, set this option to empty string.
  * 
  * Any controller options are documented in that controller's createConfiguration() method.
  * 
  * @param Minify_ControllerInterface $controller instance of subclass of Minify_Controller_Base
  * 
  * @param array                      $options    controller/serve options
  * 
  * @return null|array if the 'quiet' option is set to true, an array
  * with keys "success" (bool), "statusCode" (int), "content" (string), and
  * "headers" (array).
  *
  * @throws Exception
  */
 public function serve(Minify_ControllerInterface $controller, $options = array())
 {
     $options = array_merge($this->getDefaultOptions(), $options);
     $config = $controller->createConfiguration($options);
     $this->sources = $config->getSources();
     $this->selectionId = $config->getSelectionId();
     $this->options = $this->analyzeSources($config->getOptions());
     // check request validity
     if (!$this->sources) {
         // invalid request!
         if (!$this->options['quiet']) {
             $this->errorExit($this->options['badRequestHeader'], self::URL_DEBUG);
         } else {
             list(, $statusCode) = explode(' ', $this->options['badRequestHeader']);
             return array('success' => false, 'statusCode' => (int) $statusCode, 'content' => '', 'headers' => array());
         }
     }
     $this->controller = $controller;
     if ($this->options['debug']) {
         $this->setupDebug();
         $this->options['maxAge'] = 0;
     }
     // determine encoding
     if ($this->options['encodeOutput']) {
         $sendVary = true;
         if ($this->options['encodeMethod'] !== null) {
             // controller specifically requested this
             $contentEncoding = $this->options['encodeMethod'];
         } else {
             // sniff request header
             // depending on what the client accepts, $contentEncoding may be
             // 'x-gzip' while our internal encodeMethod is 'gzip'. Calling
             // getAcceptedEncoding(false, false) leaves out compress and deflate as options.
             list($this->options['encodeMethod'], $contentEncoding) = HTTP_Encoder::getAcceptedEncoding(false, false);
             $sendVary = !HTTP_Encoder::isBuggyIe();
         }
     } else {
         $this->options['encodeMethod'] = '';
         // identity (no encoding)
     }
     // check client cache
     $cgOptions = array('lastModifiedTime' => $this->options['lastModifiedTime'], 'isPublic' => $this->options['isPublic'], 'encoding' => $this->options['encodeMethod']);
     if ($this->options['maxAge'] > 0) {
         $cgOptions['maxAge'] = $this->options['maxAge'];
     } elseif ($this->options['debug']) {
         $cgOptions['invalidate'] = true;
     }
     $cg = new HTTP_ConditionalGet($cgOptions);
     if ($cg->cacheIsValid) {
         // client's cache is valid
         if (!$this->options['quiet']) {
             $cg->sendHeaders();
             return;
         } else {
             return array('success' => true, 'statusCode' => 304, 'content' => '', 'headers' => $cg->getHeaders());
         }
     } else {
         // client will need output
         $headers = $cg->getHeaders();
         unset($cg);
     }
     if ($this->options['contentType'] === self::TYPE_CSS && $this->options['rewriteCssUris']) {
         $this->setupUriRewrites();
     }
     if ($this->options['concatOnly']) {
         $this->options['minifiers'][self::TYPE_JS] = false;
         foreach ($this->sources as $key => $source) {
             if ($this->options['contentType'] === self::TYPE_JS) {
                 $source->setMinifier("");
             } elseif ($this->options['contentType'] === self::TYPE_CSS) {
                 $source->setMinifier(array('Minify_CSSmin', 'minify'));
                 $sourceOpts = $source->getMinifierOptions();
                 $sourceOpts['compress'] = false;
                 $source->setMinifierOptions($sourceOpts);
             }
         }
     }
     // check server cache
     if (!$this->options['debug']) {
         // 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
         // memory.
         $cacheId = $this->_getCacheId();
         $fullCacheId = $this->options['encodeMethod'] ? $cacheId . '.gz' : $cacheId;
         // check cache for valid entry
         $cacheIsReady = $this->cache->isValid($fullCacheId, $this->options['lastModifiedTime']);
         if ($cacheIsReady) {
             $cacheContentLength = $this->cache->getSize($fullCacheId);
         } else {
             // generate & cache content
             try {
                 $content = $this->combineMinify();
             } catch (Exception $e) {
                 $this->controller->log($e->getMessage());
                 if (!$this->options['quiet']) {
                     $this->errorExit($this->options['errorHeader'], self::URL_DEBUG);
                 }
                 throw $e;
             }
             $this->cache->store($cacheId, $content);
             if (function_exists('gzencode') && $this->options['encodeMethod']) {
                 $this->cache->store($cacheId . '.gz', gzencode($content, $this->options['encodeLevel']));
             }
         }
     } else {
         // no cache
         $cacheIsReady = false;
         try {
             $content = $this->combineMinify();
         } catch (Exception $e) {
             $this->controller->log($e->getMessage());
             if (!$this->options['quiet']) {
                 $this->errorExit($this->options['errorHeader'], self::URL_DEBUG);
             }
             throw $e;
         }
     }
     if (!$cacheIsReady && $this->options['encodeMethod']) {
         // still need to encode
         $content = gzencode($content, $this->options['encodeLevel']);
     }
     // add headers
     if ($cacheIsReady) {
         $headers['Content-Length'] = $cacheContentLength;
     } else {
         if (function_exists('mb_strlen') && (int) ini_get('mbstring.func_overload') & 2) {
             $headers['Content-Length'] = mb_strlen($content, '8bit');
         } else {
             $headers['Content-Length'] = strlen($content);
         }
     }
     $headers['Content-Type'] = $this->options['contentType'];
     if ($this->options['contentTypeCharset']) {
         $headers['Content-Type'] .= '; charset=' . $this->options['contentTypeCharset'];
     }
     if ($this->options['encodeMethod'] !== '') {
         $headers['Content-Encoding'] = $contentEncoding;
     }
     if ($this->options['encodeOutput'] && $sendVary) {
         $headers['Vary'] = 'Accept-Encoding';
     }
     if (!$this->options['quiet']) {
         // output headers & content
         foreach ($headers as $name => $val) {
             header($name . ': ' . $val);
         }
         if ($cacheIsReady) {
             $this->cache->display($fullCacheId);
         } else {
             echo $content;
         }
     } else {
         return array('success' => true, 'statusCode' => 200, 'content' => $cacheIsReady ? $this->cache->fetch($fullCacheId) : $content, 'headers' => $headers);
     }
 }
 private static function _checkClientCache()
 {
     require_once 'HTTP/ConditionalGet.php';
     $cgOptions = array('lastModifiedTime' => 0, 'isPublic' => true, 'encoding' => '', 'maxAge' => 1800);
     $cg = new HTTP_ConditionalGet($cgOptions);
     if (MINCO_CLIENT_CACHE && $cg->cacheIsValid) {
         // client's cache is valid
         $cg->sendHeaders();
         return true;
     } else {
         // client will need output
         self::$_headers = $cg->getHeaders();
         unset($cg);
     }
     return false;
 }