Jump to content

[SOLVED] Cache included PHP file


Yves

Recommended Posts

Is it possible to cache an included php file for 30 minutes (as a txt somewhere) and chache another included php file for 10 minutes without having APC installed? The thing is I don't want to cache a whole page, because there is a log in. When logged out, it showes as if you're still logged in. If I could cache included files, that would ofcourse be fabulous.

Link to comment
Share on other sites

Well, I have around 300 article categories with around 60,000 articles on my site. All 300 categories are all linked on the homepage. Each category also has the number of articles in it next to it. So you can imagine how many tables it has to go through to find the number of articles currently in the database, for each seperate category..

 

As of now the menu displaying the categories/subcategories is included in the index.php. Each time someone visits or revisits the homepage it takes a while to load. What I would like is to have the included (display menu) file cached for 30 minutes. After that, it can regenerate a new cache and update the content of the menu to represent to current number of articles in each category.

 

If I could accomplish that, I would be very pleased. If I could repeat that on aother included, but this time cache for 10 minutes, my day would be made. :)

 

I hope I explained well enough. !thanks

Link to comment
Share on other sites

You could do something like this:

<?php
$cache_file = 'cache/menu.html';

if(filemtime($cache_file) + 30*60 > time())
{
// generate the content here and store it in $menu_html;

file_put_contents($cache_file, $menu_html);
}
else {
$menu_html = file_get_contents($cache_html);
}
?>

 

You might want to expand it and make it into a class so you can do this:

 

<?php
$cache = new Cache('menu.html');
if($cache->isOlderThan(30 * 60))
{
// generate content and store in $menu_html
$cache->update($menu_html);
}
else {
$menu_html = $cache->getContents();
}
?>

Link to comment
Share on other sites

I feel generous so I decided to make the class:

 

<?php
class Cache
{
static protected $cache_dir;

protected $contents;
protected $path;

public function __construct($filename)
{
	$this->path = self::$cache_dir . $filename;

	if(!file_exists($filename))
	{
		throw new Exception("Cache file '{$filename}' could not be found");
		return false;
	}

	$this->contents = file_get_contents($this->path);
}

public function isOlderThan($seconds)
{
	return filemtime($this->path) + 30*60 < time();
}

public function update($new_content)
{
	$this->contents = $new_content;
	file_put_contents($this->path, $this->contents);
}

public function getContents()
{
	return $this->contents;
}

public function __toString()
{
	return $this->getContents();
}

static public function setCacheDir($dir)
{
	self::$$cache_dir = $dir;
}
}

// In another file:

require_once 'library/Cache.php';

Cache::setCacheDir('/home/somebody/cache/');

// ...

$cache = new Cache('menu.html');
if($cache->isOlderThan(30 * 60))
{
// generate content and store in $menu_html
$cache->update($menu_html);
}
else {
$menu_html = $cache->getContents();
}
?>

Link to comment
Share on other sites

Just spotted an error in the class. Updated:

<?php
class Cache
{
static protected $cache_dir;

protected $contents;
protected $path;

public function __construct($filename)
{
	$this->path = self::$cache_dir . $filename;

	if(!file_exists($filename))
	{
		throw new Exception("Cache file '{$filename}' could not be found");
		return false;
	}

	$this->contents = file_get_contents($this->path);
}

public function isOlderThan($seconds)
{
	return filemtime($this->path) + $seconds < time();
}

public function update($new_content)
{
	$this->contents = $new_content;
	file_put_contents($this->path, $this->contents);
}

public function getContents()
{
	return $this->contents;
}

public function __toString()
{
	return $this->getContents();
}

static public function setCacheDir($dir)
{
	self::$$cache_dir = $dir;
}
}
?>

 

Note: I have not tested this version or any other code I have posted in this topic.

Link to comment
Share on other sites

I'm still trying to make the first code you wrote work. It doesn't seem to put the generated content (or html code) in cache/menu.html

What I did was create the cache/ folder and menu.html file (empty) and uploaded it. Then I reloaded the homepage to see if it worked. It did display the menu so I thought it worked. However when I downloaded the menu.html file I saw it was still empty. So I'm a bit puzzled w/ that atm. ::)

 

EDIT: ofcourse I uploaded my edited menu.php file too.

Link to comment
Share on other sites

sorry for taking a while

<?php 
require_once("system/display.inc.php"); 

$cache_file = 'cache/menu.html';

if(filemtime($cache_file) + 1*60 > time())
{
// generate the content here and store it in $menu_html;
$menu_html = "
<td>".echo Display($obj_db,0,left)."</td>
<td width=\"25\"> </td>
<td>".echo Display($obj_db,0,right)."</td>
";

file_put_contents($cache_file, $menu_html);
}
else {
$menu_html = file_get_contents($cache_html);
}
?>

Link to comment
Share on other sites

Warning: filemtime() [function.filemtime]: stat failed for cache/menu.html in inc/navigation.inc.php on line 6

 

Warning: file_put_contents(cache/menu.html) [function.file-put-contents]: failed to open stream: No such file or directory in inc/navigation.inc.php on line 25

 

I think I need to add that the included file is in an inc/ folder and the cache/ folder is in the inc/ folder too

Link to comment
Share on other sites

Caching results is fine, but make sure that there is not a bigger problem elsewhere first -

 

Here is your performance problem -

 

So you can imagine how many tables it has to go through to find the number of articles currently in the database, for each separate category..

 

By using a separate table for each category, your code is doomed to execute a query for each table present. Also, if you truly have a list of 300 categories displayed at one time on one page, you are not going to get many repeat visitors because no one wants to try and wade through that much information at one time.

 

If the database was designed with one table for all the articles, with just a column to identify the category, it would only take one query.

 

My guess is that the method being used to count the articles in a category is not the most efficient. Could you post just a sample of one query (or the query loop) that is getting the count(s).

Link to comment
Share on other sites

I luckally understand what you're saying. But I had to write 'rows'. Sorry about that.

So you can imagine how many rows it has to go through to find the number of articles currently in the database, for each separate category ID..

 

Here's the function Display($obj_db="",$ParentID,$pos) to display the menu. It's the system/display.inc.php file. If you look at my site in my signature, you can see 2 columns of menu links.

 

The left one is generate by this code:

 

<?php echo Display($obj_db,0,left); ?>

 

The right one is generate by this code:

 

<?php echo Display($obj_db,0,right); ?>

 

<?php
// Display Menu's - BEGIN //
function Display($obj_db="",$ParentID,$pos) 
{
$query = mysql_query("SELECT * FROM tblcategories  WHERE intParentID = ".$ParentID." ORDER BY varCategory ASC");
$all = mysql_num_rows($query);
$half = floor($all / 2);

if($pos == "left") {  
$start = 0;
if ($all % 2 == 1) { $end = $half; }
if ($all % 2 == 0) { $end = $half-1; }
}
if($pos == "right") { 
if ($all % 2 == 1) { $start = $half+1; }
if ($all % 2 == 0) { $start = $half; }
$end = $all-1;
}

for($i=0; $i <= $all; $i++)
{
$row = mysql_fetch_array($query);
if(($i >= $start )&& ($i <= $end))
{
$query2 = mysql_query("SELECT * FROM tblcategories  WHERE intParentID = ".$row['intID']." ORDER BY varCategory ASC");
$numchild = mysql_num_rows($query2);
if($numchild>0)
{
echo "<div class=\"ctitle\"><a class=\"ctitle\" href=\"javascript:display('cat_".($i+1)."');\">".$row['varCategory']."</a><img src=\"images/arrow_down.gif\" alt=\"\"></div>";
echo "<div style=\"display:none;\" id=\"cat_".($i+1)."\">";
echo "<a class=\"stitle\" href=\"?Cat=".str_replace(" ","-",$row['varCategory'])."\" rel=\"nofollow\">".$row['varCategory']."</a>";
echo "<br>";
for($j=0; $j < $numchild; $j++)
{
$row2 = mysql_fetch_array($query2);
$query3 = mysql_query("SELECT count(intID) FROM `tblarticles`  WHERE intCategory = ".$row2['intID']."");
$row3 = mysql_fetch_array($query3);
$numArticles = $row3['count(intID)'];
if($numArticles!=0) {
$numArticles = "<span class=\"stitle\">[".$numArticles."]</span>";
} else { $numArticles = ""; }
echo "<a class=\"stitle\" href=\"?Cat=".str_replace(" ","-",$row['varCategory']).":".str_replace(" ","-",$row2['varCategory'])."\" rel=\"nofollow\">".$row2['varCategory']."</a>".$numArticles."";
echo "<br>";
}
echo "</div>\n";
} else {
echo "<div><a class=\"ctitle\" href=\"".$location."?Cat=".str_replace(" ","-",$row['varCategory'])."\" rel=\"nofollow\">".$row['varCategory']."</a> </div>";
echo "<span style=\"display:none;\" id=\"cat_".($i+1)."\"></span>\n";
}
}
}
}
// Display Menu's - END //
?>

Link to comment
Share on other sites

By the way, there is an existing php mmcache that was designed to cache database data like your counts - http://www.php.net/manual/en/ref.memcache.php

 

... which was especially designed to decrease database load in dynamic web applications.

 

I knew. But I have no idea how to install such a thing. Plus I read it uses 2GB os space. Imo Danial's solution looks like it could work for what I need. All pages load fast except for the homepage. The homepage may load slow but never on every pageload. Once every 30 minutes seems suitable.

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.