Dynamic Cacher
My own simple cache service written in PHP. More a proof of concept than anything else.
<?php
/**
* My own simple cache service written in PHP. More a proof of concept than anything else.
*
* @author Phill Sparks <me@phills.me.uk>
* @license http://creativecommons.org/licenses/by-sa/2.0/uk/ Creative Commons Attribution-ShareAlike 2.0 UK: England & Wales
*/
set_time_limit(3600);
global $fp, $headers;
$fp = null; $headers = array();
$url = 'http://milk-hub.net/'.($path = trim($_SERVER['REQUEST_URI'], '/'));
if ($path == '') $path = 'index';
$nfopath = dirname($path).'/._'.basename($path);
if ($path[0] == '.' || $path == 'index.php') {
header('HTTP/1.0 403 '.statusmessage(403));
echo '403 '. statusmessage(403);
exit;
}
else if (is_dir($path)) {
header('HTTP/1.0 403 '.statusmessage(403));
echo '403 '. statusmessage(403);
exit;
}
// Make dir for file
if (!is_dir(dirname($path))) mkdir(dirname($path), 0755, true);
// Do the fetch
$date = (file_exists($path) && file_exists($nfopath)) ? filemtime($path) : false;
if ($date < time() - 60*60*24 || $_GET['nocache']) {
$date = $date ? date('r', $date) : false;
if (file_exists($path)) unlink($path);
if (file_exists($nfopath)) unlink($nfopath);
if ($fp = fopen($path, 'wb')) {
ignore_user_abort(true); // Even if the user stops fetching the file we need to finish caching it!
$info = download($url, $date);
$info['headers'] = $headers;
file_put_contents($nfopath, serialize($info));
fclose($fp);
chmod($path, 0644);
}
else {
header('HTTP/1.0 500 '.statusmessage(500));
echo '500 '. statusmessage(500);
exit;
}
}
else {
$info = unserialize(file_get_contents($nfopath));
header('HTTP/1.0 '.$info['http_code'].' '.statusmessage($info['http_code']));
if (is_array($info['headers'])) {
foreach ($info['headers'] as $header) {
header($header);
}
}
else {
header('Content-type: '.$info['content_type']);
header('Content-Length: '.$info['download_content_length']);
header('Last-Modified: '.date('r', filemtime($path)));
header('Content-Disposition: inline; filename='.basename($path));
}
readfile($path);
}
$fsize = filesize($path);
if ($info['http_code'] < 200 || $info['http_code'] >= 400 || $info['download_content_length'] != $fsize || $fsize == 0) {
if (file_exists($path)) unlink($path);
if (file_exists($nfopath)) unlink($nfopath);
}
exit;
function download($url, $date) {
global $fp;
$ch = curl_init($url);
$header = array(
'Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
'Cache-Control: max-age=0',
'Connection: keep-alive',
'Keep-Alive: 300',
'Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7',
'Accept-Language: en-us,en;q=0.5',
'Pragma: ', // browsers keep this blank.
);
if ($date) {
$header[] = 'If-Modified-Since: '.$date;
}
curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate');
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_REFERER, $_SERVER['HTTP_REFERER']);
// curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_USERAGENT, 'CacheBot/1.0 (+http://cache.milk-hub.net)');
curl_setopt($ch, CURLOPT_HEADERFUNCTION, 'read_header');
curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'read_body');
curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
return $info;
}
function read_header($ch, $data) {
global $headers;
list($field, $value) = explode(':', $data, 2);
switch ($field) {
case 'Content-type':
case 'Content-Disposition':
case 'Content-Length':
case 'Last-Modified':
header($data);
$headers[] = $data;
break;
}
return strlen($data);
}
function read_body($ch, $data) {
global $fp;
fwrite($fp, $data);
echo $data; flush();
return strlen($data);
}
function statusmessage($statuscode) {
switch($statuscode) {
case 100:
return 'Continue';
case 101:
return 'Switching Protocols';
case 200:
return 'OK';
case 201:
return 'Created';
case 202:
return 'Accepted';
case 203:
return 'Non-Authoritative Information';
case 204:
return 'No Content';
case 205:
return 'Reset Content';
case 206:
return 'Partial Content';
case 300:
return 'Multiple Choices';
case 301:
return 'Moved Permanently';
case 302:
return 'Found';
case 303:
return 'See Other';
case 304:
return 'Not Modified';
case 305:
return 'Use Proxy';
case 306:
return 'No Longer Used';
case 307:
return 'Temporary Redirect';
case 400:
return 'Bad Request';
case 401:
return 'Not Authorised';
case 402:
return 'Payment Required';
case 403:
return 'Forbidden';
case 404:
return 'Not Found';
case 405:
return 'Method Not Allowed';
case 406:
return 'Not Acceptable';
case 407:
return 'Proxy Authentication Required';
case 408:
return 'Request Timeout';
case 409:
return 'Conflict';
case 410:
return 'Gone';
case 411:
return 'Length Required';
case 412:
return 'Precondition Failed';
case 413:
return 'Request Entity Too Large';
case 414:
return 'Request URI Too Long';
case 415:
return 'Unsupported Media Type';
case 416:
return 'Requested Range Not Satisfiable';
case 417:
return 'Expectation Failed';
case 500:
return 'Internal Server Error';
case 501:
return 'Not Implemented';
case 502:
return 'Bad Gateway';
case 503:
return 'Service Unavailable';
case 504:
return 'Gateway Timeout';
case 505:
return 'HTTP Version Not Supported ';
default:
return 'Unknown';
}
}