What is the best way to cache files in php? - php

i'm using Smarty with my php code and i like to cache some of website pages so i used the following code :
// TOP of script
ob_start(); // start the output buffer
$cachefile ="cache/cachefile.html";
// normal PHP script
$smarty->display('somefile.tpl.html') ;
$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 the file
ob_end_flush(); // Send the output to the browser
but when i print ob_get_contents() at end of the php file it's empty ! and actually the created cache file is also empty ! so how could i cache the files in php when i using smarty i know i can use smarty cache but it isn't work for me for some reason .
in addition please give me information about APC cache . how to use it? is it worth using in my case , i thin it's just for caching database queries , i read the php manual about it but i can't get anything :)
tanks .

I've mashed up some of the code from the documentation (located here) for a more complete example of the smarty cache. Also, I'm not sure what you were using in your example, but you should be using smarty's methods to manipulate the cache.
require('Smarty.class.php');
$smarty = new Smarty;
// 1 Means use the cache time defined in this file,
// 2 means use cache time defined in the cache itself
$smarty->caching = 2;
// set the cache_lifetime for index.tpl to 5 minutes
$smarty->cache_lifetime = 300;
// Check if a cache exists for this file, if one doesn't exist assign variables etc
if(!$smarty->is_cached('index.tpl')) {
$contents = get_database_contents();
$smarty->assign($contents);
}
// Display the output of index.tpl, will be from cache if one exists
$smarty->display('index.tpl');
// set the cache_lifetime for home.tpl to 1 hour
$smarty->cache_lifetime = 3600;
// Check if a cache exists for this file, if one doesn't exist assign variables etc
if(!$smarty->is_cached('home.tpl')) {
$contents = get_database_contents();
$smarty->assign($contents);
}
// Display the output of index.tpl, will be from cache if one exists
$smarty->display('home.tpl');
As for APC cache, it will work the same way that smarty does. They both store the data in a file for a specific amount of time. Every time you wish to access the data, it checks if the cache is valid, and if so returns the cache value.
However, if not using smarty you can use APC as such:
This example goes through storing the result of a DB query in the cache, similarly, you can modify this to instead store the entire page output so you don't have to run expensive PHP functions frequently.
// A class to make APC management easier
class CacheManager
{
public function get($key)
{
return apc_fetch($key);
}
public function store($key, $data, $ttl)
{
return apc_store($key, $data, $ttl);
}
public function delete($key)
{
return apc_delete($key);
}
}
Combined with some logic,
function getNews()
{
$query_string = 'SELECT * FROM news ORDER BY date_created DESC limit 5';
// see if this is cached first...
if($data = CacheManager::get(md5($query_string)))
{
// It was stored, return the value
$result = $data;
}
else
{
// It wasn't stored, so run the query
$result = mysql_query($query_string, $link);
$resultsArray = array();
while($line = mysql_fetch_object($result))
{
$resultsArray[] = $line;
}
// Save the result inside the cache for 3600 seconds
CacheManager::set(md5($query_string), $resultsArray, 3600);
}
// Continue on with more functions if necessary
}
This example is slightly modified from here.

Do you mean you are calling ob_get_contents() again after you called ob_end_flush() ? If so, the stuff you wrote to the file will have been "deleted" from PHP's memory.
If you wish to still output the HTML, save ob_end_flush to a variable first, then pass that to fwrite. You can use the variable later down the page.

Related

Updating php script one time per day

I am making a Covid-19 statistics website - https://e-server24.eu/ . Every time somebody is entering the website, the PHP script is decoding JSON from 3 urls and storing data into some variables.
I want to make my website more optimized so my question is: Is there any script that can update the variables data one time per day, not every time someone accesses the website?
Thanks,
I suggest looking into memory object caching.
Many high-performance PHP web apps use caching extensions (e.g. Memcached, APCu, WinCache), accelerators (e.g. APC, varnish) and caching DBs like Redis. The setup can be a bit involved but you can get started with a simple role-your-own solution (inspired by this):
<?php
function cache_set($key, $val) {
$val = var_export($val, true);
// HHVM fails at __set_state, so just use object cast for now
$val = str_replace('stdClass::__set_state', '(object)', $val);
// Write to temp file first to ensure atomicity
$tmp = sys_get_temp_dir()."/$key." . uniqid('', true) . '.tmp';
file_put_contents($tmp, '<?php $val = ' . $val . ';', LOCK_EX);
rename($tmp, sys_get_temp_dir()."/$key");
}
function cache_get($key) {
//echo sys_get_temp_dir()."/$key";
#include sys_get_temp_dir()."/$key";
return isset($val) ? $val : false;
}
$ttl_hours = 24;
$now = new DateTime();
// Get results from cache if possible. Otherwise, retrieve it.
$data = cache_get('my_key');
$last_change = cache_get('my_key_last_mod');
if ($data === false || $last_change === false || $now->diff($last_change)->h >= $ttl_hours ) { // cached? h: Number of hours.
// expensive call to get the actual data; we simple create an object to demonstrate the concept
$myObj = new stdClass();
$myObj->name = "John";
$myObj->age = 30;
$myObj->city = "New York";
$data = json_encode($myObj);
// Add to user cache
cache_set('my_key', $data);
$last_change = new DateTime(); //now
// Add timestamp to user cache
cache_set('my_key_last_mod', $last_change);
}
echo $data;
Voila.
Furthermore; you could look into client-side caching and many other things. But this should give you an idea.
PS: Most memory cache systems allow to define a time-to-live (TTL) which makes this more concise. But I wanted to keep this example dependency-free. Cache cleaning was omitted here. Simply delete the temp file.
Simple way to do that
Create a script which will fetch , decode JSON data and store it to your database.
Then set a Cron jobs with time laps of 24 hours .
And when user visit your site fetch the data from your database instead of your api provider.

cache folder not functioning, strange file names being saved (php)

Have a project where I'm scraping a few sites with data, then outputting onto one site. To help with load times, I'm trying to rig it so once every 10 mins, my main website does a full data scrape, then stores it all into a cache folder called "cache", stored in the root folder. Then, anytime I refresh main site after that 10 mins, it pulls from the cache, making load times quite fast at that point.
Trouble is, load times haven't changed, which it really should using this method, so I'm doing something wrong. Would appreciate any help. Now I can confirm the data IS being stored in the cache, because I see the files automatically appearing there. So the issue has to be that the code is broken where specified to grab the data from cache, after it's stored every 10 minutes, it's not grabbing the data.
*part of me wonders if the issue is with how the filenames are being saved in cache, right now it seems to be random values. for ex, one is named f32dd7f0b85eb4c1be0bb9a417cc29ea553d898e.html
I'd think it needs to be saved as a specific file name. Not sure how to achieve that though. The code at the end of my php reference files seem to specify this, so not sure issue. The code that is supposed to be doing this is at the bottom of the post.
I'm really new to php, and honestly have only gotten this far through some very nice and helpful people. I'm close, but not quite there yet with this cache framework.
global.php in root folder:
<?php
$_cache_time =600; //10 minutes
$_cache_dir="./cache"; //cache dir
function deleteBlankInArray($var){
return !ctype_space($var)&&!empty($var);
}
function cache_start($filename)
{
global $_cache_dir,$_cache_time;
$cachefile = $_cache_dir.'/'.sha1($filename).'.html';
ob_start();
if(file_exists($cachefile) && (time( )-$_cache_time <
filemtime($cachefile)))
{
include($cachefile);
ob_flush();
return true;
}
return false;
}
function cache_end($filename)
{
global $_cache_dir,$_cache_time;
$cachefile = $_cache_dir.'/'.sha1($filename).'.html';
$fp = fopen($cachefile, 'w');
fwrite($fp, ob_get_contents());
fclose($fp);
ob_flush();
}
My main website, is an xhtml site. It's referencing these php pages like this:
<?php include 's&pcurrent.php';?>
<?php include 'news.php';?>
It's referencing/outputting multiple php files, which is why load times are slow, if not pulling from cache.
And lastly, this is an example of one of my php files that are being "included". This one is called litecoinchange.php
<?php
error_reporting(E_ALL^E_NOTICE^E_WARNING);
include_once "global.php";
//filename of the file
if(!cache_start("litecoinchange.php")){
$doc = new DOMDocument;
// We don't want to bother with white spaces
$doc->preserveWhiteSpace = false;
$doc->strictErrorChecking = false;
$doc->recover = true;
$doc->loadHTMLFile('https://coinmarketcap.com/');
$xpath = new DOMXPath($doc);
$query = "//tr[#id='id-litecoin']";
$entries = $xpath->query($query);
foreach ($entries as $entry) {
$result = trim($entry->textContent);
$ret_ = explode(' ', $result);
//make sure every element in the array don't start or end with blank
foreach ($ret_ as $key=>$val){
$ret_[$key]=trim($val);
}
//delete the empty element and the element is blank "\n" "\r" "\t"
//I modify this line
$ret_ = array_values(array_filter($ret_,deleteBlankInArray));
//echo the last element
echo $ret_[7];
//filename of the file
cache_end("litecoinchange");
}
}

Use APC to store generated html

Some of our pages use a lot of processing power so when users are not signed in it makes sense to completely cache them.
I'm using apc and the following code to try and accomplish this:
$key = "forum-thread-list-cache";
if(($data = apc_fetch($key)) === false) {
ob_start();
$forumOb = new Forum();
$threadList = $forumOb->getThreadList();
require "templates/forum.php";
$data = ob_get_contents();
ob_end_clean();
//Debugging
file_put_contents("/home/user/log.txt", $data);
//15 Minutes
apc_store($key, $data, 60 * 15);
flush();
}
Currently the generated html will appear in the log.txt file but I can't get it to appear in the apc user cache entries?
The generated html is around 18kb in size.
Am I doing something wrong here?
Here are my runtime settings, is there anything in here that would prevent 18kb of html being cached?

Contingency plan for fopen error in php

I'm writing a PHP app that has a 'control panel' that writes a prefs file with certain variables. On every POST, if the file doesn't exist, it is created. If it does exist, it is unlinked and a new file is touched with the same filename and new variables. This file is then included on another page with displays content based on the variables inside it.
$file = "phpsettings.php";
if (!file_exists($file)) {
touch($file);
$handle = fopen ($file, 'r+');
$str = "<?php \$pref1 = \"$mypref\"; ?>";
} else {
unlink($file);
touch($file);
$handle = fopen ($file, 'r+');
$str = "<?php \$pref1 = \"$mypref\"; ?>";
}
fwrite ($handle, $str);
fclose ($handle);
Is this a safe way of writing preferences, provided this file will be overwritten many times per day? What is a good way of both alerting the user of this control panel if the file wasn't saved correctly, and in that case, what would be a good contingency plan to avoid breaking the page this prefs file is included on short of defining a default set of variables to fill if !(file_exists)?
If you store your settings in an array, you can serialize() them and write to a text file, rather than writing raw php to a php file and including it.
If you're not sanitising your input for those preferences, and say $mypref1 represents someone's name, there's nothing stopping them from filling this out in the form field:
\"; echo \"PWNED
and your resulting PHP will become
<?php \$pref1 = \"$mypref\"; echo \"PWNED\"; ?>
So firstly, storing your preferences in an array and using serialize() is much safer:
$prefs = array('mypref1' => 'somethingorother');
$handle = fopen ($file, 'w');
fwrite($handle, serialize($prefs));
fclose($h);
// example code demonstrating unserialization
$prefs2 = unserialize(file_get_contents($file));
var_dump($prefs == $prefs2); // should output "(bool) true"
In your question, you also mention that if the file does exist, it is unlinked. You can simply truncate it to zero length by passing "w" as the second argument to fopen - you don't need to manually delete it. This should set the mtime anyway, negating the need for the call to touch().
If the values being written to the file are preferences, surely each preference could have a default, unless there are hundreds? array_merge will allow you to overwrite on a per-key basis, so if you do something like this:
// array of defaults
$prefs = array(
'mypref1' => 'pants',
'mypref2' => 'socks',
);
if (file_exists($file)) {
// if this fails, an E_NOTICE is raised. are you checking your server error
// logs regularly?
if ($userprefs = unserialize(file_get_contents($file))) {
$prefs = array_merge($prefs, $userprefs);
}
}
If the issue is that there are heaps, and you don't want to have to initialise them all, you could have a get_preference method which just wraps an isset call to the prefs array.
function get_preference($name, &$prefs) {
if (isset($pref[$name]))
return $pref[$name];
return null;
}
var_dump(get_preference('mypref1', $prefs));
Beyond all of the questions this raises though, the reality is that with your app, in the unlikely event that something does go wrong with the fopen, it should be regarded as a serious failure anyway, and the handful of users you're likely to have making use of this feature are going to be contacting you pretty darn quick if something goes wrong.
It is always better to store your users state in a session and only persist that state when needed.
Why not just use the truncation capabilities of fopen()? I believe instead of "r+", you'll need to pass "w+"... Then if the file exists, it will be truncated, if it doesn't you'll just create a new file. So the code becomes:
$file = "phpsettings.php";
$handle = fopen( $file, 'w+' );
$str = "<?php \$pref1 = \"$mypref\"; ?>";
fwrite ($handle, $str);
fclose ($handle);

Caching HTML output with PHP

I would like to create a cache for my php pages on my site. I did find too many solutions but what I want is a script which can generate an HTML page from my database ex:
I have a page for categories which grabs all the categories from the DB, so the script should be able to generate an HTML page of the sort: my-categories.html. then if I choose a category I should get a my-x-category.html page and so on and so forth for other categories and sub categories.
I can see that some web sites have got URLs like: wwww.the-web-site.com/the-page-ex.html
even though they are dynamic.
thanks a lot for help
check ob_start() function
ob_start();
echo 'some_output';
$content = ob_get_contents();
ob_end_clean();
echo 'Content generated :'.$content;
You can get URLs like that using URL rewriting. Eg: for apache, see mod_rewrite
http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html
You don't actually need to be creating the files. You could create the files, but its more complicated as you need to decide when to update them if the data changes.
In my opinion this is the best solution. I use this for cache JSON file for my Android App. It can be simply use in other PHP files.
It's optimize file size from ~1mb to ~163kb (gzip).
Create cache folder in your directory
Then Create cache_start.php file and paste this code
<?php
header("HTTP/1.1 200 OK");
//header("Content-Type: application/json");
header("Content-Encoding: gzip");
$cache_filename = basename($_SERVER['PHP_SELF']) . "?" . $_SERVER['QUERY_STRING'];
$cache_filename = "./cache/".md5($cache_filename);
$cache_limit_in_mins = 60 * 60; // It's one hour
if (file_exists($cache_filename))
{
$secs_in_min = 60;
$diff_in_secs = (time() - ($secs_in_min * $cache_limit_in_mins)) - filemtime($cache_filename);
if ( $diff_in_secs < 0 )
{
print file_get_contents($cache_filename);
exit();
}
}
ob_start("ob_gzhandler");
?>
Create cache_end.php and paste this code
<?php
$content = ob_get_contents();
ob_end_clean();
$file = fopen ( $cache_filename, 'w' );
fwrite ( $file, $content );
fclose ( $file );
echo gzencode($content);
?>
Then create for example index.php (file which you want to cache)
<?php
include "cache_start.php";
echo "Hello Compress Cache World!";
include "cache_end.php";
?>
Manual caching (creating the HTML and saving it to a file) may not be the most efficient way, but if you want to go down that path I recommend the following (ripped from a simple test app I wrote to do this):
$cache_filename = basename($_SERVER['PHP_SELF']) . "?" . $_SERVER['QUERY_STRING'];
$cache_limit_in_mins = 60 * 32; // this forms 32hrs
// check if we have a cached file already
if ( file_exists($cache_filename) )
{
$secs_in_min = 60;
$diff_in_secs = (time() - ($secs_in_min * $cache_limit_in_mins)) - filemtime($cache_filename);
// check if the cached file is older than our limit
if ( $diff_in_secs < 0 )
{
// it isn't, so display it to the user and stop
print file_get_contents($cache_filename);
exit();
}
}
// create an array to hold your HTML output, this is where you generate your HTML
$output = array();
$output[] = '<table>';
$output[] = '<tr>';
// etc
// Save the output as manual cache
$file = fopen ( $cache_filename, 'w' );
fwrite ( $file, implode($output,'') );
fclose ( $file );
print implode($output,'');
I use APC for all my PHP caching (on an Apache server)
If you're not opposed to frameworks, try using the Zend Frameworks's Zend_Cache. It's pretty flexible, and (unlike some of the framework modules) easy to implement.
Can use Cache_lite from PEAR:
Details here
http://mahtonu.wordpress.com/2009/09/25/cache-php-output-for-high-traffic-websites-pear-cache_lite/
I was thinking from the point of load on the database, and charges for data bandwidth and speed of loading. I have some pages which are unlikely to change in years, (I know it is easy to use a CMS system based on a database ). Unlike in US, here the cost of bandwidth can be high. Anybody has any views on that, whether to create htmal pages or dynamic (php, asp.net)
Links to the pages would be stored on a database anyway.

Categories