Counting the number of times a web page has been accessed, and recording the last access date of the web page, are two common tasks in web development. To demonstrate how to use Membase, we'll implement those features in PHP using the Membase library.
The data storage model of Membase is similar to that of the Array data type in PHP. You can store a value accessed by a key of some sort, then retrieve the value by the key. In this article we'll be storing and retrieving integer and string data (see Listing 1).
Listing 1: PHP/Memcached page counter and last accessed code
<?php #Create the Memcached object $mc_obj = new Memcached(); $mc_obj->addServer('localhost', 11211); #determine the name of the script currently running $script_name=$_SERVER["SCRIPT_NAME"]; #if the script name doesn't exist as a Membase key then add $script_access_count=$mc_obj->get($script_name); if($mc_obj->getResultCode() == Memcached::RES_NOTFOUND){ #the add will fail if it has already been added $mc_obj->add($script_name,0); } #increment the integer associated with the script name $access_count = $mc_obj->increment($script_name); #print the current access count echo "this page ($script_name) accessed $access_count times<br>"; #retrieve the last access date/time of the script. #the key name is is the script name prefixed with DATE:: $last_access_date=$mc_obj->get("DATE::" . $script_name); #handle the case where this is the first access to the script #and that key doesn't yet exist if($last_access_date ==FALSE){ $last_access_date = "never"; } echo "this page last accessed: " . $last_access_date; #save the current access date/time in a script $mc_obj->set("DATE::" . $script_name,date("F j, Y, g:i:s a ")); ?>
The above listing can be saved into a PHP file in a directory served by your web server.
Here we've saved it as test.php.
When you load test.php page you'll see something like this on the first load:
this page (/test.php) accessed 1 times this page last accessed: never
And like this on subsequent loads, with the access count incrementing and the date/time increasing:
this page (/test.php) accessed 2 times this page last accessed: June 1, 2011, 10:06:04 am
Reload the script a few times in rapid succession and you'll see a spike in traffic in the Membase Web UI. So, what exactly is our script doing to achieve this?
The first line of the script creates a Memcached object
($mc_obj), and then adds a Membase server
hostname and port to the server pool that we'll be accessing. In
this case we're only adding the one server running on our
localhost, but any hostname with a Membase server may be
specified. The port number, 11211 is the "default" bucket where
keys are stored, though you can assign different buckets to
different ports via the Moxi server bundled with Membase, which
manages connection pools, timeouts and cluster topology changes
for you.
$mc_obj = new Memcached(); $mc_obj->addServer('localhost', 11211);
We then get the "SCRIPT_NAME" value from the
$_SERVER Array, which tells us in which script
this PHP code is currently running.
$script_name=$_SERVER["SCRIPT_NAME"];Now we connect to Membase to retrieve the integer access count associated with this script using the script name (determined above) as the key.
$script_access_count=$mc_obj->get($script_name);
If there is no such key in Membase (because the page has not
previously been accessed), the request returns a Boolean
FALSE value, and the result code of the call to
the get method is the Memcached constant
RES_NOTFOUND (resource not found). In this case
we use the add method to set the value to zero.
If, between the failed get and
add method calls, the key has already been
added by another process, the add will fail and
we will not overwrite the added value. This sort of attention to
concurrency issues is overkill for the current application, as a
missed counting of a single script access is unlikely to be
critical; however, it illustrates how to write memcached code to
avoid unintentionally overwriting keys:
if($mc_obj->getResultCode() == Memcached::RES_NOTFOUND){ $mc_obj->add($script_name,0); }
The key associated with the script name count is then incremented by one for the current script access, and that incremented value is returned and printed out. The increment method is atomic, consisting of both an increment and retrieval of the resultant value. If the Membase server receives two such requests, they will be queued for action in the order they were received and return the correct count to each requesting process.
$access_count = $mc_obj->increment($script_name); echo "this page ($script_name) accessed $access_count times<br>";
To retrieve and update the last access date of the script we're doing something similar to what we did for the script access count, however, instead of adding and incrementing an integer, we're adding and updating a string.
As a key for the date any given script was accessed, we've
prepended the string "DATE::" to the beginning
of the script name; thus the key for the last access date of our
test.php script is "DATE::test.php". We first
try to get the date of last access and assign it to the
$last_access_date variable. If this fails (the
get method returns a Boolean FALSE value and
the subsequent getResultCode call returns
RES_NOTFOUND) that means it hasn't yet been
set, so the script has not previously been accessed. In that case
we'll set $last_access_date to "never". We then
print out the value of $last_access_date.
$last_access_date=$mc_obj->get("DATE::" . $script_name); if($mc_obj->getResultCode() == Memcached::RES_NOTFOUND){ $last_access_date = "never"; } echo "this page last accessed: " . $last_access_date;
We now set the access date for this access of the script for retrieval on the next access.
$mc_obj->set("DATE::" . $script_name,date("F j, Y, g:i:s a "));And that's the end of the script. There is no method in the memcached library to close the connection to the server, however it will automatically be closed at the end of script execution.