Home => PHP Projects => Worlds Best Page Footer Specs

The Worlds Best Page Footer Specs

As an intro to PHP, the world's best page footer will illustrate the use of date/times, file I/O and a TCP/IP function. The page footer is a single PHP include with no parameters. Ultimately it will auto-create all the files and directories it needs. It uses on file to hold application scope variables. Each web page that includes the footer gets a single counter file created for it. All the files have a single record/row/line. 


Low Hanging Fruit

A standard web page footer includes a last update date for the web page. The PHP getlastmod() function provides this information:

$unixts = getlastmod(); //last modified date/time for this web page as Unix timestamp

PHP has a date function that can be used to externalize Unix timestamps. For example, this PHP code:
      echo date ("l dS of F Y h:i:s A")
Prints this current time as: Wednesday 15th of January 2004 10:51:38 AM 

The date function can take a second argument of a Unix timestamp:      echo date ("F j, Y g A",$unixts); //ex: January 15, 2004 10 AM


It would also be nice to include the URL of the web page in its footer. This is to insure that it gets included when the page is printed. A web browser usually prints the URL but not always.  PHP can do this with the $PHP_SELF variable. This variable starts with a slash, and does not include the domain name. 

In the output below the "www.michaelhorowitz.com" part is hard coded. Use a PHP function to improve on this and get the current domain. 

The end result: 

Page: www.michaelhorowitz.com/php/somepage.php                       Last Updated: January 30, 2004 10 PM

Advanced: Calculate how long ago the web page was last modified. That is, include in the page footer, that it was last changed 999 days ago. PHP does not provide functions to calculate the difference between two timestamps. To do this manually, convert timestamps into a number of days first. Present it as an integer number of days, rounded off. 


Page Counter

Writing your own page counter routine provides a good introduction to file I/O with PHP. 

Counters can either be stored in a single file for then entire web site or you can use a different file for each web page. Using different files scales better as it provides less enque/semaphore contention and it also provides a better introduction to using files with PHP. For simplicity sake, there should be a single directory storing all the counter files, one file per web page on the web site. The PHP script has to generate the file name dynamically based on the name of the web page it is running within. 

Test for the existence of the directory where all the counter files live, lets call it "counterfiledir". If there is no such directory, create one. You also have to control the file permissions on the directory because we will be updating and adding files. Note that if you view these counter files with an FTP program, you may need to refresh the file listing when you create a new file or update an existing file. 

You can start with $PHP_SELF as a basis for generating a name for the page counter file. One problem is that there may be two pages with the same name in different directories, as such, the directory name should be part of the file name. The end result should be file names such as:
   counterfiledir/dir2.fileabc.php
   counterfiledir/dir3.filexyz.php
   counterfiledir/filexxx.php
This way they can all live in a single page counter file directory without any subdirectories. $PHP_SELF provides the directory name but it needs to be massaged. It starts with a leading slash that has to be removed. Then any remaining slashes have to be converted, preferably to periods. In the example above, the value of $PHP_SELF for the first file is /dir2/fileabc.php. And, it couldn't hurt to add a suffix to each file name to help identify it as a counter file. I chose ".txt". 

Before updating the page counter, you have to check if the file already exists, if not, it needs to be created and initialized. 

After every file related operation, you have to test the return code to insure it worked and deal with errors.

I haven't found a PHP function to update a record in a file. Instead I read a record, use the rewind() function to position the file pointer back to the start of the file, and then write the updated data (clobbering the original data). Obviously, this only works if the file has a single record.

If the page counter file is to be expanded and include other data, this can either be done with a different record for the different types of data or all the data can exist on a single record. The latter is preferable and PHP provides the fgetcsv() function that both reads a record and parses the data, splitting it out based on a separation character. Normally, commas delimit the different data items. CSV stands for comma separated values. This is better than choosing a fixed length for each data field and parsing the input record with substrings. When get input with fgetcsv() insure that the number of fields is what is expected and handle errors. This can be done with the count() function on the returned array.

Format the page counter so that it displays with commas. That is, display 1,234 not 1234. 


Last Page View

Wouldn't it be nice to know this: 

 Prior to you, the last person to look at this web page did so on Monday, February 2, 2004 10:09:05 AM

We can get this information from the page counter file for a web page. Each page in this section of my web site has its own counter which is stored in a file, one file per web page. PHP has filemtime() function that returns the date/time a file was last modifed. Using filemtime() on the page counter file, we can tell the last time someone read a web page.

 $filename = "../counterfilesdir/somefilename"; 
 if (file_exists($filename))
     {echo "counter file last changed:" . date("l, F j, Y g:i:s A", filemtime($filename));}

The file modification time is, of course, reflective of the time on the web server computer. You're time will be different unless you happen to be in the same time zone as the web server. For example, if the last page view (and thus the last update to the counter file) was a minute ago, it may appear as 2 hours and 1 minute ago if you are two time zones removed from the web server computer. 

To put the counter file modification timestamp in context, externalize the current time on the server and the current time on your computer. 

PHP has a date function that can be used to externalize the date on the web server computer. For example, this PHP code:
      echo date ("l dS of F Y h:i:s A")
Prints something like: Wednesday 15th of January 2003 05:51:38 AM

The time on your computer comes from a client side JavaScript that looks like this: 

  <script language="JavaScript">
  tempdate = new Date();
  document.writ
e ("The time on your computer is: " + tempdate.toLocaleString());
  </script>

The net result of these three timestamps is output such as: 

Prior to you, the last person to look at this web page did so on Monday, February 2, 2004 10:09:05 AM 
                                   Current time on the server is Monday, February 2, 2004 11:06:01 AM 
                                    The time on your computer is Monday, February 2, 2004  1:06:33 PM  

Bug: When I did this on the one web server I've tried it so far, the results were wrong. The current time on the server was always over a minute different from the last modification timestamp for the counter file. You see this by refreshing the page a couple times very quickly. When doing so, the server time should not be more than a second or two off from the file modification time for the counter file. 

This may be more information than most people want to see. Condense these three timestamp displays down to 

 Prior to you, the last person to look at this web page did so X days, Y hours, Z minutes and X seconds ago

 


Using The Page Footer From Outside

The page footer is a PHP file used in an environment that supports PHP. What about using from an environment that does not support PHP? 

Can the PHP page footer be used from an html file that is not processed by PHP? 

Possibilities: iframe, frame, modify web server to process html files as PHP files, client side JavaScript include or document. write 

iframe html: <iframe src="somepage.php" width=100%>
iframe works in that the included PHP page is processed by PHP. However, the PHP_SELF thinks it is running in the footer PHP page, not in the owning html page. As a result the counter incorrectly reflects that of the included footer PHP page. Also, the page URL prints that of the PHP footer page, not of the owning html page. You have to force the iframe to be the full width of the page. A bigger problem though is the height of the iframe and making the footer appear at the top of it. The included PHP page does not inherit the CSS of the including html page so it has to include the same external CSS file (if there is one). Can it determine the identity of the page including it?? The last updated time is that of the PHP footer, not of the owning html page. 

A client side JavaScript did not work. For example
  
<script language="JavaScript" src="http://www.somedomain.org/pcrsfooterforjs.php"></script>
failed to produce any output at all. Then I tried to change the PHP script to spit out JavaScript commands as opposed to doing normal echos. For example 
    echo "document.write('This is a test from PHP to JS on the client side')";  
This did not work either. The PHP page is not being processed by PHP. 

Can it be used from another web site? 

If the output is an image (or a set of images, one per digit) then the IMG tag can include images from anywhere on the net. 
With an iframe, a web site that only has html files and does not support PHP at all, may be able to use a PHP page footer to get a total page views counter. If combined with a date, it can also get an average page views per day. Test... 


First Access

Automatically track the first access to the web page. When the counter routine finds no pre-existing counter file it creates a new one and initializes it. Add the current Unix timestamp to the counter file as another data field (the second).  Previous counter files have to be deleted. This field will not change over time, it will forever mark the first access to the page. Externalize the first access timestamp. 

Use the first access timestamp to calculate the average page views per day. Externalize this as an integer and round it off. Say: 
 
This page has been viewed an average of X times over the last Y days


Site Wide (global) Data 

Page Counter: 

Track data for the entire web site for the first time. This requires variables that maintain their state across many web pages. Sessions are not appropriate for this as sessions are short term in duration. PHP does not support this concept, something ASP calls application scope variables. Use a file to fudge around this limitation, a second file that exists solely to hold global data. 

Call the file "globalasa.txt" named after the control block where ASP stores its global variables. Test for the existence of this file, if it does not exist, then create it an initialize it. This auto-creation lets us change the format of the file very easily in the future. All we would have to do is rename the current file and the page footer will then auto-create a new globals file in the new format. Store the file in the same directory as the page counter files. 

Track two global fields, a page counter for the whole web site that grows forever and the go-live data of the site. The go-live date is the current time when the global data file is created.  Report: 
  
  This site has served up X pages since January 2, 2004 11:11 AM (X per day)  

That is, also calculate, the number of days between  now and when the site went live and divide the total pages by this to get the average page views per day. 

Last 10 users: 

Track the last 5 people to view the web site. Output should look like 
  1.2.3.4  xxxxxxxxxxxx
  1.2.3.5  xxxxxxxxxxxx
Where the first column is the IP address of the user and the second column is the name of the computer at that IP address. 

The computer name can be derived from the IP address with the PHP gethostbyaddr() function, so we only need to store the IP addresses. The function returns the IP address unchanged, if it can't find a name. In this case, display "no name found". 

This will require 5 more data fields in the global data file. Each time a page is read, the IP addresses have to be cycled down, with the oldest one falling into the bit bucket. If the page is being read by the same IP address as the last time, do nothing. This way, we track the last 5 visitors, not the last 5 page views. 

Keep the global data file to one single record, separate each IP address with a | character. 


What's Happening Now on The Web Site

Per Page: 

Track page views for each page for yesterday and the current day. This data is stored in the counter file for the page. Like above, three application wide data fields are needed: a timestamp, the total page views since the timestamp and the total page views for the previous full day. Externalize this as: 
  This page was viewed X times yesterday 
  This page has been viewed X times in the last Y hours 
Calculate the Y hours as the difference between the current time and the timestamp. 

Add a link in the footer to a web page with the Per Page stats. 

Globally: 

Add two more global page counters to produce output like:
 In the last X minutes, X pages have been viewed (X per minute) 
 In the previous hour, X pages were viewed (X per minute)
 Yesterday, X people viewed this web page 
 So far today, X people have viewed this web page
 

The number of minutes in the first line is the time between now and an hourly timestamp that is created automatically the first time this logic is invoked. This timestamp lets us track the number of page views in the last complete hour and in the current hour (time between now and the timestamp). When the current hour is an hour old (or more), cycle down this count to become the count for the last complete hour. 

Three application wide data fields are needed: a timestamp, the total page views since the timestamp and the total page views for the previous full hour. If there is no timestamp, create initial data. 

When the difference between the current time and the timestamp is less than an hour, just increment the page views since the timestamp. When the difference between the current time and the timestamp is greater than an hour, then: 
 -move the count of page views since the timestamp to the Total Page Views for the last hour (really the last complete hour)
 -move the current time to the timestamp field and 
 -initialize the Total Page Views Since the Timestamp to one


Weekly Stats

The current system has a single set of files, one file per page with the data being grand total data. 

Modify each file to also keep data for the current week. This requires a new counter and a new starting timestamp (first usage in the week). 

Once a week the current week data will be copied to a new set of files that represent a single week, then reset back to zero. 

How do you know when a week is up? One way is to have a scheduled job, but this complicates things. Another way is to test if a week has elapsed in a low used page - maybe the About page. It's no big deal if the definition of a week is off by a few hours. You don't want to test for the end of a week too often or too infrequently. 

At the end of a week, save all the weekly data. Maybe a new directory with copies of all the files? Maybe a new directory with a single summary file? Maybe just a new single summary file in the current directory? Maybe a weekly summary directory that consists of a summary file for each week?   ???   


Advanced

Make a page that reads all the counter files and displays the result. This is made simpler by the fact that all the files reside in a single directory with no sub-directories. The readdir() function returns the names of all files in a given directory one by one on each invocation. When all the filenames are listed it returns FALSE.  

Since this requires reading all the page counter files, get their last modified timestamps and sort them. The result is a list of the last time each web page was accessed. Externalize this in a web page sorted by date/time. 

Sort the page counters and take the three most popular pages and generate an email message with the page name its hit count. Then schedule this to run once a day. Then get the email address of the cell phone of the site administrator and send this message once a day. 

Enque on the counter file to insure it is only being updated by one instance of the PHP script at a time.  

Display the source code for the footer. How remove all HTML? Create a link in the demo page. 

Add data on the last two accesses of the web page: timestamp and IP address. Then use gethostbyaddr() function to convert the IP address to a computer name. Result: 
  Last access to this page was January 29, 2004 at 11:22 AM from IP address 1.2.3.4 by xxxxxxxxxxxx 
  Prev access to this page was January 29, 2004 at 11:11 AM from IP address 1.2.3.5 by yyyyyyyyyy 

We use this data to expand the earlier list of each page and the last time it was read. Extract all the page access data and sort it time sequence and present the most recent 50 in a web page. That is, let's see the last 50 pages access on the site, by whom and when. 

Errors: Log all errors to an error log file and have a web page that displays the errors. Log the date/time, web page, function. Generate an email message to the web site admin when there is an error. Insure the error log does not get too big. Add enque on the error log, but if you can't get the enque, continue without logging the error. 

Get daily stats... 

Application scoped variables . . . 



 Viewed 19125 times since July 17,2007 (63 per day over 304.7 days) Home => PHP Projects  
Page: www.michaelhorowitz.com Last Updated: April 19, 2006 4 PM  
Prior to you, the last person to look at this web page did so on Friday, May 16, 2008 11:18:53 PM
Current time on the server is Friday, May 16, 2008 11:19:38 PM
There have been 99378 page views of these PHP pages since February 10, 2004 for an average of 64 per day over 1,557.1 days
The last 5 people to view this web site were from these IP addresses:
38.103.63.17no name found
74.6.8.95llf520030.crawl.yahoo.net
80.237.209.89robot.acoon.de
65.55.213.109livebot-65-55-213-109.search.live.com
74.6.22.241lj511933.crawl.yahoo.net