Simple PHP Caching and Content Retrieval Function

By  on  

One way to make your website exponentially faster is by caching both remote and internal requests. Why request your RSS subscriber count from FeedBurner more than once a day if that count is calculated once per day? Why hit your database on each page load if that content rarely changes? I've created a primitive request-and-cache function for PHP that checks for fresh content in the cache and retrieves content from a source if fresh content isn't available.

The PHP Function

/* gets the contents of a file if it exists, otherwise grabs and caches */
function get_content($file,$url,$hours = 24,$fn = '',$fn_args = '') {
	//vars
	$current_time = time(); $expire_time = $hours * 60 * 60; $file_time = filemtime($file);
	//decisions, decisions
	if(file_exists($file) && ($current_time - $expire_time < $file_time)) {
		//echo 'returning from cached file';
		return file_get_contents($file);
	}
	else {
		$content = get_url($url);
		if($fn) { $content = $fn($content,$fn_args); }
		$content.= '';
		file_put_contents($file,$content);
		//echo 'retrieved fresh from '.$url.':: '.$content;
		return $content;
	}
}

/* gets content from a URL via curl */
function get_url($url) {
	$ch = curl_init();
	curl_setopt($ch,CURLOPT_URL,$url);
	curl_setopt($ch,CURLOPT_RETURNTRANSFER,1); 
	curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,5);
	$content = curl_exec($ch);
	curl_close($ch);
	return $content;
}

My get_content function accepts four arguments:

  • The file to grab content from. If the file doesn't exist, the file is created and content placed into.
  • The URL to get content from if cached content isn't fresh.
  • A function name to pass the freshly received content to.
  • Arguments to pass to the third argument's function.

The function is, of course, very primitive. I like that my function handles both retrieval and caching so that I don't need to repeat code whenever I want cached content.

Sample Usage 1

/* usage */
$TWITTER_FOLLOWERS_FILE_NAME = 'twitter-followers.txt';
$TWITTER_FOLLOWERS_URL = 'http://twitter.com/users/show.json?screen_name=davidwalshblog';

$TWITTER_FOLLOWERS = get_content($TWITTER_FOLLOWERS_FILE_NAME,$TWITTER_FOLLOWERS_URL,3,'format_followers',array('file'=>$TWITTER_FOLLOWERS_FILE_NAME));
/* utility function */
function format_followers($content,$args) {
	$content = json_decode($content);
	$twitter_subscribers = $content->{'followers_count'};
	if($twitter_subscribers) {
		$twitter_subscribers = number_format($twitter_subscribers,0,'',',');
		file_put_contents($args['file'],$twitter_subscribers);
		return $twitter_subscribers;
	}
}

The above code retrieves my Twitter follower count, parses the code, and caches the content for three hours.

There are several more advanced PHP caching classes available but the simple function above covers most of my needs -- hopefully it can help you out too!

Recent Features

  • By
    CSS @supports

    Feature detection via JavaScript is a client side best practice and for all the right reasons, but unfortunately that same functionality hasn't been available within CSS.  What we end up doing is repeating the same properties multiple times with each browser prefix.  Yuck.  Another thing we...

  • By
    Conquering Impostor Syndrome

    Two years ago I documented my struggles with Imposter Syndrome and the response was immense.  I received messages of support and commiseration from new web developers, veteran engineers, and even persons of all experience levels in other professions.  I've even caught myself reading the post...

Incredible Demos

Discussion

  1. Hervé

    Why don’t you simply use something simple like pear cache lite ?

  2. Hi,
    ever thought about memcached? Its a nice daemon, which can store such things in memcache, its very easy to usw and very efficient. Many big sites use it, facebook has about 800 Memcached-Servers.

  3. Chris the Developer

    @herve + michael: Simply put, this approach allows the developer to cache without having to set the environment up to use memcache or pear cache lite….

  4. Thank you! I was looking for something like this yesterday. Exactly what I needed.

  5. Basic caching like this is really useful when dealing with APIs, but I wish WordPress would add some centralized caching functions. It would make things neater than dumping data into assorted wp_options fields…

  6. I must say that i like this blog very much. I’m pretty interested in mootools because i’m in the process of creating javascript UIs. This tutorial is also very nice. It’s a good habit to cache whatever is possible.

  7. Nice function.
    I’ll be looking to build something similar, but with images created dynamically… Check if image exist and check if image is “recent”… else regenerate the image.

    I allready did something similar with an XML generator, so users cannot spam the website to generate a new XML every time.

    Caching is something useful when controlled… but can be a pain when you don’t know stuff is cached (ka-Map comes to mind…)!

  8. Zend Framework (Zend_Http & Zend_Cache) ^^!

    And… a few small suggestions:

    * is_file instead file_exists: No warnings and not @ supress errors operator.
    * strtotime(‘text’) instead calculate hours: More intuitive.
    * register_shutdown_function for caching new content, after
    the first petition and the file creation if not exists: No delay, no http errors for dump the file content.

    I used this at betools.culturadigital.org ;)

  9. mylife

    I am working on a job site wordpress, I want to pull user linkedin profile details with image and appear that on site, when anonymous user submit linkedin public url in the wordpress registration form, their profile header content should fetch on the site.

    This should work for anonymous user, every time new user put his linked-in profile URL, so this should not use that only for single user.

    hope you get what I need. please help if somebody have such function in php for wordpress site.

    waiting……….

    thanks

  10. Here is class of caching the full page:
    https://github.com/joaoverissimo/SimpleCachePhp

    thanks

  11. David, thank you very much for this, that’s exactly what I was looking for. :)

  12. You gave a good starting place, but a nice feature which you are missing is to save yourself some IO if the response headers say the file has not been altered:

    public static function get_content($url) {
            //vars
            $file = self::$cachDir . md5($url);
            $currentTime = time();
            $expireTime = self::$expireMin * 60;
            $fileTime = filetime($file);
    
            $content = '';
    
            //decisions, decisions
            if(file_exists($file) && ($currentTime - $expireTime  $fileTime){
                    file_put_contents($file,$content);
                //document has not been modified, lets update the file's time
                }else{
                    touch($file);
                }
    
            }
            else {
                $content = file_get_contents($url);
                file_put_contents($file,$content);
            }
    
            return $content;
        }
    
  13. stewart

    Hi

    that’s brilliant, wanted a way to cache files without using ob_start(),ob_get_contents()

    u just saved me ages of time messing around coding

    thank you David

  14. @Will Smelser: what the hell … your code is messed up and semantically and logically wrong

  15. I had some problems with this caching function when trying to use json_decode() on the returned result, for some reason it was not being recognised.

    I removed the cached text from the file and everything works fine!

    Andy
    Web Canvas Design

    • Josh

      Thanks, Andy, this is exactly what I was trying to figure out.

  16. crabtronic

    Hello, is it possible to get content from any site then check if adress is the same and so add a checkmark in the row of the user in the backoffice user page ?

  17. Thank you very much for this. This helped me out tremendously!

  18. David Spector

    A different approach is appropriate for doing development work. All the PHP file system functions do their own caching, and you can use the realpath_cache_size = 0 directive in PHP.ini to disable the caching if you like. The default caching timeout is 120 seconds. This is separate from the caching typically done by browsers for all GET requests (the majority of Web accesses) unless the HTTP headers override it. Caching is not a good idea during development work, since your code may read in old data from a file whose contents you have changed.

  19. David Spector

    Correction (these postings can’t be edited and the avatars can’t be chosen!): to disable the PHP file contents cache, use the directive realpath_cache_ttl = 0 in the PHP.ini file.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!