webpage caching - php

I am looking for a best solution for caching my webpages example http:/www.website.com/test.php?d=2011-11-01 which has url rewrite rule to become http:/www.website.com/testd-2011-11-01.html
the scripts below does not work for dynamic web page it give the same page regardless of the query.
<?php
$cachefile = "cache/".$reqfilename.".html";
$cachetime = 240 * 60; // 5 minutes
// Serve from the cache if it is younger than $cachetime
if (file_exists($cachefile) && (time() - $cachetime
< filemtime($cachefile)))
{
include($cachefile);
echo "<!-- Cached ".date('jS F Y H:i', filemtime($cachefile))."
-->";
exit;
}
ob_start(); // start the output buffer?>
my website content here
<?php
// open the cache file for writing
$fp = fopen($cachefile, 'w');
// save the contents of output buffer to the file
fwrite($fp, ob_get_contents());
// close the file
fclose($fp);
// Send the output to the browser
ob_end_flush(); ?>

If your URL looks like testd-2011-11-01.html, you have two possible solutions :
Use some RewriteRule, so that URL is rewritten to test.php?d=2011-11-01 ; and, then, your test.php script can deal with the cache generation / invalidation
Or use a cronjob, that will regenerate the testd-2011-11-01.html static file every X minutes.
The first solution is the one that's generally used, as it only requires you to setup a RewriteRule (and those are often available, even on cheap hosting services).
The second solution might be a bit better for performances (no PHP code is ever executed, except when the cronjob runs) ; but the difference is probably not that important, except if you have a very big website with an awful lot of users.

Something like this could work:
class SimpleCache {
private $_cacheDir = '/path/to/cache';
private $_cacheTime = 240*60;
public function isCached( $id ) {
$cacheFilename = $this->_cache . "/" . $id . "_*";
$files = glob($cacheFilename, GLOB_NOSORT);
if( $files && !empty($files) ) {
// There should always be one file in the array
$filename = $files[0];
$params = explode("_", $filename);
$cacheTime = strtok($params[1], '.');
// Check if the cached file is too old
if( time() - $params[1] > $this->_cacheTime ) {
#unlink($filename);
}
else {
return $filename;
}
}
return false;
}
public function cache( $id, $data ) {
$filename = $this->_cache . "/" . $id. "_" . time() . ".cache";
if( !($fp = #fopen($filename, "w")) ) {
return false;
}
if( !#fwrite($fp, $data) ) {
#fclose($fp);
return false;
}
#fclose($fp);
return true;
}
}
$cache = new SimpleCache();
if( !($buffer = $cache->isCached($reqfilename)) ) {
// Produce the contects of the file and save them in the $buffer variable
$cache->cache($reqfilename, $buffer);
}
echo $buffer;
But you could use memcached, APC, and more advanced caching techniques if you are up to it

You can use HTTP caching. You send headers telling the client (browser) to cache the whole page for a certain period of time.
// 5 minutes
header('Cache-Control: max-age=300');
If you have control over your hosting environment, you can also add a reverse proxy like varnish or nginx in front of your webserver. This proxy will then cache these requests for you, making the cached version shared between all visitors of your site.
See also the HTTP/1.1 specification.

Related

Automatically create cache file with php

I have been using a basic caching system on my site based on this link
It has so far worked well for everthing I want to do.
$cachefile = 'cache/'. basename($_SERVER['QUERY_STRING']) . '.html';
$cachetime = 1440 * 60;
if (file_exists($cachefile) && (time() - $cachetime < filemtime($cachefile))) {
include($cachefile);
echo "<!-- Cached ".date('jS F Y H:i', filemtime($cachefile))." -->";
exit;
}
ob_start();
// My html/php code here
$fp = fopen($cachefile, 'w'); // open the cache file for writing
fwrite($fp, ob_get_contents()); // save the contents of output buffer to the file
fclose($fp); // close
ob_end_flush(); // Send to browser
However I have a couple of pages with more detailed mysql queries, I have spent a fair bit of time optimising it however it still takes about 10 secs to run when I query it in mysql and even longer on the website. And sometimes it seems to time out as I get the below message.
The proxy server received an invalid response from an upstream server.
The proxy server could not handle the requestGET http://www.example.com
Reason: Error reading from remote server
This isn't a huge issue as because I am using the caching system above only the first person to click on it for the day gets the delay and the rest of the time the users get the cached page so it is actually quite fast for them.
I want to save myself from having to be the first person each day to go to the page and automate this process so at 17:00 (on the server) each day the file gets written to the cache.
How would I best achieve this?
I suggest you to use Php Speedy or this may help:
<?php
function getUrl () {
if (!isset($_SERVER['REQUEST_URI'])) {
$url = $_SERVER['REQUEST_URI'];
} else {
$url = $_SERVER['SCRIPT_NAME'];
$url .= (!empty($_SERVER['QUERY_STRING']))? '?' . $_SERVER[ 'QUERY_STRING' ] : '';
}
return $url;
}
//getUrl gets the queried page with query string
function cache ($buffer) { //page's content is $buffer
$url = getUrl();
$filename = md5($url) . '.cache';
$data = time() . '¦' . $buffer;
$filew = fopen("cache/" . $filename, 'w');
fwrite($filew, $data);
fclose($filew);
return $buffer;
}
function display () {
$url = getUrl();
$filename = md5($url) . '.cache';
if (!file_exists("/cache/" . $filename)) {
return false;
}
$filer = fopen("cache/" . $filename, 'r');
$data = fread($filer, filesize("cache/" . $filename));
fclose($filer);
$content = explode('¦', $data, 2);
if (count($content)!= 2 OR !is_numeric($content['0'])) {
return false;
}
if (time()-(100) > $content['0']) { // 100 is the cache time here!!!
return false;
}
echo $content['1'];
die();
}
// Display cache (if any)
display(); // if it is displayed, die function will end the program here.
// if no cache, callback cache
ob_start ('cache');
?>
Just include this script anywhere you need caching and set a cron job for running it automated.

Fault-tolerant file_get_contents

I have a website with the following architecture:
End user ---> Server A (PHP) ---> Server B (ASP.NET & Database)
web file_get_contents
browser
Server A is a simple web server, mostly serving static HTML pages. However, some content is dynamic, and this content is fetched from Server B. Example:
someDynamicPageOnServerA.php:
<html>
...static stuff...
<?php echo file_get_contents("http://serverB/somePage.aspx?someParameter"); ?>
...more static stuff...
</html>
This works fine. However, if server B is down (maintainance, unexpected crash, etc.), those dynamic pages on server A will fail. Thus, I'd like to
cache the last result of file_get_contents and
show this result if file_get_contents timeouted.
Now, it shouldn't be too hard to implement something like this; however, this seems to be a common scenario and I'd like to avoid re-inventing the wheel. Is there some PHP library or built-in feature that helps which such a scenario?
i would do something like this:
function GetServerStatus($site, $port){
$fp = #fsockopen($site, $port, $errno, $errstr, 2);
if (!$fp) {
return false;
} else {
return true;
}
}
$tempfile = '/some/temp/file/path.txt';
if(GetServerStatus('ServerB',80)){
$content = file_get_contents("http://serverB/somePage.aspx?someParameter");
file_put_contents($tempfile,$content);
echo $content;
}else{
echo file_get_contents($tempfile);
}
You could check the modified time of the file and only request it when it is different, otherwise load the local copy. Also, there is a cache pseudo-example on the PHP website in the comments for filemtime ( from: http://php.net/manual/en/function.filemtime.php ):
<?php
$cache_file = 'URI to cache file';
$cache_life = '120'; //caching time, in seconds
$filemtime = #filemtime($cache_file); // returns FALSE if file does not exist
if (!$filemtime or (time() - $filemtime >= $cache_life)){
ob_start();
resource_consuming_function();
file_put_contents($cache_file,ob_get_flush());
}else{
readfile($cache_file);
}
?>
I accepted dom's answer, since it was the most helpful one. I ended up using a slightly different approach, since I wanted to account for the situation where the server is reachable via port 80 but some other problem prevents it from serving the requested information.
function GetCachedText($url, $cachefile, $timeout) {
$context = stream_context_create(array(
'http' => array('timeout' => $timeout))); // set (short) timeout
$contents = file_get_contents($url, false, $context);
$status = explode(" ", $http_response_header[0]); // e.g. HTTP/1.1 200 OK
if ($contents === false || $status[1] != "200") {
$contents = file_get_contents($cachefile); // load from cache
} else {
file_put_contents($cachefile, $contents); // update cache
}
return $contents;
}

Please help to improve my pHp cache ?

i have read a bit around in the internet about the php cache.
at the moment i am using, this system to cache my pages:
This is putted on the start of the page
<?php
// Settings
$cachedir = 'cache/'; // Directory to cache files in (keep outside web root)
$cachetime = 600; // Seconds to cache files for
$cacheext = 'html'; // Extension to give cached files (usually cache, htm, txt)
// Ignore List
$ignore_list = array(
'addedbytes.com/rss.php',
'addedbytes.com/search/'
);
// Script
$page = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; // Requested page
$cachefile = $cachedir . md5($page) . '.' . $cacheext; // Cache file to either load or create
$ignore_page = false;
for ($i = 0; $i < count($ignore_list); $i++) {
$ignore_page = (strpos($page, $ignore_list[$i]) !== false) ? true : $ignore_page;
}
$cachefile_created = ((#file_exists($cachefile)) and ($ignore_page === false)) ? #filemtime($cachefile) : 0;
#clearstatcache();
// Show file from cache if still valid
if (time() - $cachetime < $cachefile_created) {
//ob_start('ob_gzhandler');
#readfile($cachefile);
//ob_end_flush();
exit();
}
// If we're still here, we need to generate a cache file
ob_start();
?>
MY HTML CODE Goes here .............
and the code below is at the footer of my page.
<?php
// Now the script has run, generate a new cache file
$fp = #fopen($cachefile, 'w');
// save the contents of output buffer to the file
#fwrite($fp, ob_get_contents());
#fclose($fp);
ob_end_flush();
?>
There are some things that i need and this code dont have them :
gzip
the expired cache is not autodeleted after it expire.
Also wanted to ask, if this code is secure to use , if some one can suggest a better one or something to improve the current code it will be just great
Thank you fro reading this post.
Best Regards
Meo
….
// Show file from cache if still valid
if (time() - $cachetime < $cachefile_created) {
//ob_start('ob_gzhandler');
echo gzuncompress(file_get_contents($cachefile));
//ob_end_flush();
exit();
} else {
if(file_exists($cachefile) && is_writable($cachefile)) unlink($cachefile)
}
….
and
// Now the script has run, generate a new cache file
$fp = #fopen($cachefile, 'w');
// save the contents of output buffer to the file
#fwrite($fp, gzcompress(ob_get_contents(), 9));
#fclose($fp);
ob_end_flush();
?>
Use ob_start("ob_gzhandler"); to initiate gzipped buffering (it'll take care of determining if the client can actually accept/wants gzipped data and adjust things accordingly).
To delete the cached files:
if (time() - $cachetime < $cachefile_created) {
#readfile($cachefile);
//ob_end_flush();
exit();
} else {
unlink($cachefile);
exit();
}
But there can be delay or maybe error when the file is being written and someone requests for that page. You should use flock to overcome such problems as mentioned at Error during file write in simple PHP caching
Something like this at the end of page
<?php
$fp = #fopen($cachefile, 'w');
if (flock($fp, LOCK_EX | LOCK_NB)) {
fwrite($fp, gzcompress(ob_get_contents(), 9));
flock($fp, LOCK_UN);
fclose($fp);
}
ob_end_flush(); ?>

How to cache dynamic PHP page

How to cache PHP page which has mysql query. Any example will be great and helpful.
I am using phpFastCache ( for shared hosting, if you don't want to touch php.ini and root to setup memcached). Check out the Example Menu. They have full detail example, and very easy.
First you set with phpFastCache::set and then get with phpFastCache::get - DONE!
Example: Reduce Database Calls
Your website have 10,000 visitors who are online, and your dynamic page have to send 10,000 same queries to database on every page load. With phpFastCache, your page only send 1 query to DB, and use the cache to serve 9,999 other visitors.
<?php
// In your config file
include("php_fast_cache.php");
phpFastCache::$storage = "auto";
// you can set it to files, apc, memcache, memcached, pdo, or wincache
// I like auto
// In your Class, Functions, PHP Pages
// try to get from Cache first.
$products = phpFastCache::get("products_page");
if($products == null) {
$products = YOUR DB QUERIES || GET_PRODUCTS_FUNCTION;
// set products in to cache in 600 seconds = 5 minutes
phpFastCache::set("products_page",$products,600);
}
OUTPUT or RETURN your $products
?>
My preference is to use a caching reverse proxy, like Varnish.
As far as a pure PHP solution, you could have some code at the end of your script that caches the final output, and code at the beginning that checks to see if the page is cached. If the page was found in cache, send it and exit rather than running the queries again.
<?php
function cache_file() {
// something to (hopefully) uniquely identify the resource
$cache_key = md5($_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . $_SERVER['QUERY_STRING']);
$cache_dir = '/tmp/phpcache';
return $cache_dir . '/' . $cache_key;
}
// if we have a cache file, deliver it
if( is_file( $cache_file = cache_file() ) ) {
readfile( $cache_file );
exit;
}
// cache via output buffering, with callback
ob_start( 'cache_output' );
//
// expensive processing happens here, along with page output.
//
function cache_output( $content ) {
file_put_contents( cache_file(), $content );
return $content;
}
Obviously this needs lots of customization for your setup, including cache expiration, a $cache_key that meets your needs, and error detection so bad pages aren't cached.
memcache your html out and then do something like this:
$memcache = memcache_connect('localhost', 11211);
$page = $memcache->get('homepage');
if($page == ""){
$mtime = microtime();
$page = get_home();
$mtime = explode(" ",$mtime);
$mtime = $mtime[1] + $mtime[0];
$endtime = $mtime;
$totaltime = ($endtime - $starttime);
memcache_set($memcache, 'homepage', $page, 0, 30);
$page .= "\n<!-- Duly stored ($totaltime) -->";
}
else{
$mtime = microtime();
$mtime = explode(" ",$mtime);
$mtime = $mtime[1] + $mtime[0];
$endtime = $mtime;
$totaltime = ($endtime - $starttime);
$page .= "\n<!-- served from memcache ($totaltime) -->";
}
die($page);
<?php
//settings
$cache_ext = '.html'; //file extension
$cache_time = 3600; //Cache file expires afere these seconds (1 hour = 3600 sec)
$cache_folder = 'cache/'; //folder to store Cache files
$ignore_pages = array('', '');
$dynamic_url = 'http://'.$_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . $_SERVER['QUERY_STRING']; // requested dynamic page (full url)
$cache_file = $cache_folder.md5($dynamic_url).$cache_ext; // construct a cache file
$ignore = (in_array($dynamic_url,$ignore_pages))?true:false; //check if url is in ignore list
if (!$ignore && file_exists($cache_file) && time() - $cache_time < filemtime($cache_file)) { //check Cache exist and it's not expired.
ob_start('ob_gzhandler'); //Turn on output buffering, "ob_gzhandler" for the compressed page with gzip.
readfile($cache_file); //read Cache file
echo '<!-- cached page - '.date('l jS \of F Y h:i:s A', filemtime($cache_file)).', Page : '.$dynamic_url.' -->';
ob_end_flush(); //Flush and turn off output buffering
exit(); //no need to proceed further, exit the flow.
}
//Turn on output buffering with gzip compression.
ob_start('ob_gzhandler');
######## Your Website Content Starts Below #########
?>
<!DOCTYPE html>
<html>
<head>
<title>Page to Cache</title>
</head>
<body>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer ut tellus libero.
</body>
</html>
<?php
######## Your Website Content Ends here #########
if (!is_dir($cache_folder)) { //create a new folder if we need to
mkdir($cache_folder);
}
if(!$ignore){
$fp = fopen($cache_file, 'w'); //open file for writing
fwrite($fp, ob_get_contents()); //write contents of the output buffer in Cache file
fclose($fp); //Close file pointer
}
ob_end_flush(); //Flush and turn off output buffering
?>
The important thing, often overlooked when there is discussion about caching, is process synchronization to avoid thread race (see: https://en.wikipedia.org/wiki/Race_condition).
Typical caching scenario in PHP without synchronization looks like this: if you don't have resource in the cache, or resource is expired, it must be created and put into cache. First thread/process that happens to encounter such condition is trying to create resource, and during that time, other threads will also create the resource, which leads to thread race, cache slamming and performance downspike.
Problem is magnified by number of concurrent threads and workload created by resource creation task. On busy systems it may lead to serious problems.
There is very few caching systems for PHP that takes it into consideration synchronization.
One of them is php-no-slam-cache: https://github.com/tztztztz/php-no-slam-cache

How to implement a Yahoo currency cache?

I have a Yahoo currency script in my site but they are taking too much time to load and are slowing my site. How can I cache them and refreshing cache every 3600 minutes?
You need some place to store these results. MySQL is a popular choice, but if the data does not need to stick around or have historical values, using memcache would be easier. Depending on your host, both of these options may be available.
The idea is:
create some sort of cache dir and set the defined cache age
then, at the very beginning of your function, check for cache
if it exists, check it's age.
If within range, get it
if cache too old
use live data and set that data into cache file.
Something like this should do the trick:
define(CACHE_DIR, 'E:/xampp/xampp/htdocs/tmp');
define(CACHE_AGE, 3600);
/**
* Adds data to the cache, if the cache key doesn't aleady exist.
* #param string $path the path to cache file (not dir)
* #return false if there is no cache file or the cache file is older that CACHE_AGE. It return cache data if file exists and within CACHE_AGE
*/
function get_cache_value($path){
if(file_exists($path)){
$now = time();
$file_age = filemtime($path);
if(($now - $file_age) < CACHE_AGE){
return file_get_contents($path);
} else {
return false;
}
} else {
return false;
}
}
function set_cache_value($path, $value){
return file_put_contents($path, $value);
}
function kv_euro () {
$path = CACHE_DIR . '/euro.txt';
$kveuro = get_cache_value($path);
if(false !== $kveuro){
echo "\nFROM CACHE\n";
return round($kveuro, 2);
} else {
echo "\nFROM LIVE\n";
$from = 'EUR'; /*change it to your required currencies */
$to = 'ALL';
$url = 'http://finance.yahoo.com/d/quotes.csv?e=.csv&f=sl1d1t1&s='. $from . $to .'=X';
$handle = #fopen($url, 'r');
if ($handle) {
$result = fgets($handle, 4096);
fclose($handle);
}
$allData = explode(',',$result); /* Get all the contents to an array */
$kveuro = $allData[1];
set_cache_value($path, $kveuro);
return $kveuro;
}
}
Also, rather than fgets which reads file line by line rather slower and since you are not manipulating a line, you should consider using file_get_contents function instead.

Categories