Hawk's By Design

You just finished writing a PHP script that hits up an API and sends back info. All is working well. You sit back and enjoy your code in all of its glory.

Then you start to think…

Do you really need to hit the API with every request? The data being sent back doesn’t change all too often, so is there really a need to constantly send requests?

Well, why don’t you cache the requests? It’s super easy to do, and can speed some things up.

Ground Work

Let’s say our function looks something like the following.

function run_curl($url, $params = false) {
	$curl = curl_init();
	if($params) {
		$url .= "?" . http_build_query($params);
	}
	curl_setopt($curl, CURLOPT_URL, $url);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
	$headers[] = "Authorization: Basic USERNAME:PASSWORD";
	curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
	$result = curl_exec($curl);
	$httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
	curl_close($curl);
	return array(
		"result" => $result,
		"httpcode" => $httpcode
	);
}
function get_data($url, $params = false) {
	$curl = run_curl($url, $params);
	if($curl[‘httpcode'] === 200) {
		return $curl[‘result'];
	}
	return false;
}

Super simple. Don’t even pay too much attention to the run_curl function, as that’s just there so we can see how we get the value of $curl.

The get_data() function takes two parameters: $url, and $params. The $params parameter is optional, as we could make a request that just spits out info without the need for its own parameters. We make a request to the $url, check to make sure it returns a 200 status code, and go from there.

So, how can we improve this function?

File Caching

function get_data($url, $params = false) {
	$file_name = $url;
	if($params) {
		$file_name .= http_build_query($params);
	}
	$file_name = "_data/" . hash("sha256", $file_name) . ".json";
	if(file_exists($file_name)) {
		$file_stats = stat($file_name);
		$file_expiry = $file_stats['mtime'] + (5 * 24 * 60 * 60); // Five days
		if($file_expiry > time()) {
			// File is valid
			if($file = file_get_contents($file_name)) {
				return $file;
			}
		} else {
			// Delete the file
			unlink($file_name);
		}
	}

	$curl = run_curl($url, $params);
	if($curl['httpcode'] === 200) {
		// Cache the result
		file_put_contents($file_name, $curl['result']);
		return $curl['result'];
	}
	return false;
}

Alright, so our get_data() function looks a lot bigger now, doesn’t it? Don’t worry, let’s break it down.

$file_name = $url;
if($params) {
	$file_name .= http_build_query($params);
}

First we store the $url variable in a variable called $file_name. If there are params, we use the built-in http_build_query() function to convert those $params (an array) to a string and add it onto $file_name.

$file_name = "_data/" . hash("sha256", $file_name) . ".json";

Next, we prepend $file_name with “_data/”, which is the directory our files are going to be stored in, hash $file_name using sha256, and add the JSON extension. Our api returns JSON, so we store it in a JSON file. If you need a more flexible file type, you could always use TXT.

You might be thinking, why hash the file name? Well, we want to ensure that we have a valid file name. Our parameters could contain characters that are illegal when creating a file, so best to use a hash that will use characters we know are perfectly legal.

Then comes some more fun. First, we need to see if the file name that we created exists. PHP has a handy function called file_exists() that does exactly what we need. If the file exists, then we have some work to do.

$file_stats = stat($file_name);
$file_expiry = $file_stats['mtime'] + (5 * 24 * 60 * 60); // Five days

Now that we know the file exists, we need to get some information from it. Specifically, how old it is. We use the built-in PHP function stat() to get that information. From the info we get from stat(), we grab mtime, which is the modification time.

The next key part is the amount of time we add to it, this will determine the life of this file as a cache. As an example, I have five days worth of time added. You can always change the number of days by changing the number “5” to however many days you’d like.

if($file_expiry > time()) {
	// File is valid
	if($file = file_get_contents($file_name)) {
		return $file;
	}
} else {
	// Delete the file
	unlink($file_name);
}

Once we have that time stored in $expiry_time, we compare that with the current time on the system using the time() function. This is a straight up comparison of numbers to numbers. Seconds against seconds. If $expiry_time is greater than what the time() function returns, then we know that it is still valid. In which case, we need to attempt to get the file using another built-in PHP function, file_get_contents(). If that succeeds, then we return the file.

On the flip side, if $expiry_time is less than time(), we know the file is too old and needs to be removed. In that case, we use unlink() to destroy the file.

// Cache the result
file_put_contents($file_name, $curl['result']);

Last one, I promise.

Now that we understand how we are accessing the files for cache, we have to see how these files are created. It’s as simple as calling file_put_contents() and using the $file_name and $curl[‘result’]. This creates a file with a name that matches the first parameter, and fills its contents with what was provided in the second parameter.

Conclusion

This is a super basic caching system that can help alleviate some unnecessary calls to an API that returns a lot of the same data. Obviously, you need to weigh the pros and cons of implementing a system like this. If you have data that is constantly changing, it may not be the best solution.

Are there other solutions out there? Oh hell yeah. It’s programming, there are always ten thousand ways to do something and everyone always thinks the other way is better.

Shoot me a message if you have issues or questions!

Thanks for reading, fellow nerd.

Some more articles for you

Coding

March 7, 2019

Random Accessibility Things I Find Tedious, But Necessary

Web accessibility can really be a pain in the butt...but it's necessary. Here are a couple random things that can prove troublesome.

View post

Coding

November 8, 2018

Browser Plugins: AdBlock and User CSS

Browser plugins are quite amazing things and can do a variety of tasks. AdBlock and User CSS are two Google Chrome plugins that I've come to love for various reasons. If you want to optimize your web surfing experience, then this is the article for you!

View post