So, da hier ja so viel von Cache in der letzten Zeit geredet wird, gebe ich euch auch gerne mal einen meiner Codes ab *g*
Vielleicht gibt es ja Verbesserungsvorschläge.
Vorraussetzung:
- CSS/Js Dateien in seperater Datei mit der Endung .css oder .js
- Der Ordner mit den beinhaltenden Datein ist schreibbar für Web-Server/PHP
- mod_rewrite
- PHP ab 5.2
Code:
Vorhandene .htaccess - Datei erweitern oder diese hochladen mit folgendem Inhalt:
RewriteEngine On
RewriteBase /
RewriteRule ^(.*)\.((js|css))$ gzip_js_css.php?file=$1&filetype=$2 [L]
Die Datei *.php in das Hauptverzeichnis kopieren.
Fertig!
Von nun an werden *.js und *.css Dateien - sofern vom Client unterstüzt -
komprimiert und mit Cache-Headern übertragen.
Um nicht bei jeder Anfrage die Datei neu zu komprimieren, wird diese zwischengespeichert.
Dazu muss darauf geachtet werden, dass das Verzeichnis mit der *.js / *.css beschreibbar ist.
Code:
.htaccess:
RewriteEngine On
RewriteBase /
RewriteRule ^(.*)\.((js|css))$ gzip_js_css.php?file=$1&filetype=$2 [L]
PHP-Code:
gzip_js_css.php :
<?php
if(isset($_GET['filetype']) && isset($_GET['file']) && $_SERVER['REQUEST_METHOD'] == 'GET')
{
define("REFRESH", "3 day");
define("COMPRESS_LEVEL", 6);
define("RECACHE", 1);
$filetype = $_GET['filetype'];
$filename = getcwd().'/'.$_GET['file'].'.'.$filetype;
/**
* 15.03.2008 01:01:45
* @author Michael Wegener, HttpCaching Class from the Net
* purpose: deliver .css and .js in compressed form
*
*/
class HttpCaching {
private $cacheControlDirectives = array();
private $ages = array('max-age' => -1, 's-maxage' => -1);
private $lastModified;
private $eTag;
function sendStatusAndHeaders($die) {
$isFresh = $_SERVER['REQUEST_METHOD'] == "GET" ? $this->isFresh() : false;
// Send back a 304?
if ($isFresh == true) {
header('HTTP/1.1 304 Not Modified');
}
$this->sendHeaders();
// Die if 304?
if ($isFresh == true && $die == true) {
exit();
}
return $isFresh;
}
function sendHeaders() {
// Expires corresponds to max-age
if ($this->ages['max-age'] >= 0) {
header('Expires: ' . self::formatDate(time() + $this->ages['max-age']), 1);
}
// Cache-Control
if (!is_array($this->cacheControlDirectives)) {
$this->cacheControlDirectives = array();
}
foreach($this->ages as $dir => $value) {
if ($value >= 0) {
array_push($this->cacheControlDirectives, "$dir=$value");
}
}
if (count($this->cacheControlDirectives) > 0) {
header('Cache-Control: ' .
implode(', ', $this->cacheControlDirectives), 1);
}
// At least one of ETags of Last-Modified will be sent for cacheability
if ($this->eTag) {
header('ETag: ' . $this->eTag);
}
if ($this->lastModified) {
$lm = $this->lastModified;
} else if (!$this->eTag) {
$lm = time();
}
if ($lm) {
header('Last-Modified: ' . self::formatDate($lm));
}
}
function isFresh() {
if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
!isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
// No information provided by the client
return false;
}
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
if (!$this->lastModified) {
return false;
}
// Split the If-Modified-Since (Netscape < v6 gets this wrong)
$ifModifiedSince = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE']);
// Turn the client request If-Modified-Since into a timestamp
$ifModifiedSince = strtotime($ifModifiedSince[0]);
// Compare timestamps (FIXME: make this test '!='?)
if ($this->lastModified > $ifModifiedSince) {
return false;
}
}
if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
if ($_SERVER['HTTP_IF_NONE_MATCH'] == '*') {
return true;
}
if (!$this->eTag) {
return false;
}
$etags = preg_split('/,\s*/', $_SERVER['HTTP_IF_NONE_MATCH']);
foreach($etags as $e) {
if ($this->etagMatch($e)) {
return true;
}
}
return false;
}
return true;
}
function etagMatch($etag) {
if (!$this->eTag) {
return false;
}
if ((self::isEtagWeak($this->eTag) || self::isEtagWeak($etag))
&&
($_SERVER['REQUEST_METHOD'] != "GET" || isset($_SERVER['HTTP_RANGE'])))
{
// Weak validation only works for non-subrange GET requests
return false;
}
if (self::etagValidator($this->eTag) == self::etagValidator($etag)) {
return true;
} else {
return false;
}
}
static function isEtagWeak($etag) {
return (substr_compare($etag, 'W/', 0, 2) == 0);
}
static function etagValidator($etag) {
if (self::isEtagWeak($etag)) {
return substr($etag, 2);
} else {
return $etag;
}
}
function getDuration($type) {
if ($type != "max-age" && $type != "s-maxage") {
throw new Exception("Invalid type");
}
return $this->ages[$type];
}
function setDuration($type, $time) {
if ($type != "max-age" && $type != "s-maxage") {
throw new Exception("Invalid type");
}
if (is_numeric($time)) {
$time = intval($time);
if ($time < 0) {
$time = -1;
}
} else {
if ($time == 'now') {
$time = 0;
} else {
$time = strtotime($time) - time();
}
if ($time < 0) {
throw new Exception("Bad interval specified to strtotime()");
}
}
return $this->ages[$type] = $time;
}
function freshFor($time) {
return $this->setDuration('max-age', $time);
}
function setCacheControlDirective($type, $set) {
if ($set == true) {
if (!in_array($type, $this->cacheControlDirectives)) {
array_push($this->cacheControlDirectives, $type);
}
} else {
$this->cacheControlDirectives = array_diff($this->cacheControlDirectives,
array($type));
}
}
function ccPublic($set) {
$this->setCacheControlDirective('public', $set);
}
function ccNoCache($set) {
$this->setCacheControlDirective('no-cache', $set);
}
function ccNoStore($set) {
$this->setCacheControlDirective('no-store', $set);
}
function ccMustRevalidate($set) {
$this->setCacheControlDirective('must-revalidate', $set);
}
function ccProxyRevalidate($set) {
$this->setCacheControlDirective('proxy-revalidate', $set);
}
function etag($value) {
$this->eTag = '"' . $value . '"';
}
function weakEtag($value) {
$this->etag($value);
$this->eTag = "W/" . $this->eTag;
}
function lastModified($value) {
$this->lastModified = $value;
}
function setLastModifiedFromFile($file) {
$finfo = stat($file);
if (!$finfo) {
return;
}
$this->lastModified($finfo['mtime']);
}
static function sendHeadersSpecifyingFreshness($time) {
$hc = new HttpCaching();
$hc->freshFor($time);
$hc->ccMustRevalidate(true);
$hc->sendHeaders();
}
static function formatDate($time) {
return gmdate("D, d M Y H:i:s", $time) . ' GMT';
}
}
/**
* Extends the HttpCaching Class from Michael Wegener
* thank you for your Class
*
* @author Christoph Zysik, ztk
* @version 0.1
*
*/
class ZtkHttpCaching extends HttpCaching
{
private $buffering = false;
private $buffertype= '';
private $error_msg;
private $content_type;
private $content = '';
private $filetype;
public function setFiletyep($filetype)
{
$this->filetype = $filetype;
switch($this->filetype) {
case 'js':
$this->content_type = 'application/x-javascript';
$this->error_msg = "alert('JS not found');";
break;
case 'css':
$this->content_type = 'text/css';
$this->error_msg = "body { background-color: red; padding:100px; }";
break;
default:
$this->content_type = 'text/plain';
$this->error_msg = 'non supported extension used';
break;
}
Header('Content-type: ' . $this->content_type);
}
public function setCompression()
{
if (stripos($_SERVER["HTTP_ACCEPT_ENCODING"],'x-gzip') !== false) {
$this->buffering = true;
$this->buffertype='gzencode';
Header("Content-encoding: x-gzip");
}
elseif (stripos($_SERVER["HTTP_ACCEPT_ENCODING"],'gzip') !== false) {
$this->buffering = true;
$this->buffertype='gzencode';
Header("Content-encoding: gzip");
}
elseif (stripos($_SERVER["HTTP_ACCEPT_ENCODING"],'deflate') !== false) {
$this->buffering = true;
$this->buffertype='gzdeflate';
Header("Content-encoding: deflate");
}
}
public function loadFile($file)
{
if( $this->buffering === true)
{
if ($this->buffertype != '' && is_file($file.$this->buffertype) && ((time() - filemtime($file.$this->buffertype))/(3600*RECACHE)) < 1.0) {
$this->content = file_get_contents($file.$this->buffertype);
}
elseif (is_file($file))
{
$this->content = file_get_contents($file);
$this->trimContent();
switch ($this->buffertype)
{
case 'gzencode':
$this->content = gzencode($this->content, COMPRESS_LEVEL);
break;
case 'gzdeflate':
$this->content = gzdeflate($this->content, COMPRESS_LEVEL);
break;
}
try {
file_put_contents($file.$this->buffertype, $this->content);
}
catch (Exception $e){}
}
else {
echo "/* File: $file not found */\n";
echo $this->error_msg;
exit;
}
}
if($this->content != '')
{
$this->etag(md5($this->content));
return $this->content;
}
else return $this->error_msg;
}
public function sendFile()
{
header('Content-Length: ' . strlen($this->content));
echo $this->content;
}
public function trimContent()
{
if ($this->filetype == 'css') {
$this->content = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $this->content);
$this->content = str_replace(array("\r\n", "\r", "\n", "\t"), '', $this->content);
$this->content = preg_replace('/\s\s+/', "\n", $this->content);
} else {
$this->content = preg_replace('/\ \ +/', " ", $this->content);
}
}
}
//print ((time() - filectime('css/style.cssgzencode'))/(3600*RECACHE));
$hc = new ZtkHttpCaching();
$hc->ccMustRevalidate(true);
$hc->setFiletyep($filetype);
$hc->setCompression();
$hc->setLastModifiedFromFile($filename);
$hc->freshFor(constant("REFRESH"));
$hc->loadfile($filename);
$hc->sendStatusAndHeaders(true);
$hc->sendFile();
} // if POST
else die('not supported');
?>