Back to DFS's PHP Page


Arrays II: One-Dimensional with String Index

Quick Links

Back to Top

A Problem

Consider the following problem: Your computer is connected to the internet 24/7 and you are concerned about security. You would like to check your httpd error log and print a table of how many times your machine has been accessed from each IP address. The table below is an abbreviated example of what you would like.

Visitor# of Times
4.34.152.271
24.78.141.2713
24.100.34.19026
24.100.213.243146
24.100.254.12865
217.37.216.681

The format of the information -- the fact that it appears in a table -- suggests that we would like to store the data in an array. Furthermore, there being six entries in the table, it would seem reasonable to have subscripts ranging from 0 to 5.

Unfortunately, in most computer languages (Logo and Lisp being exceptions), an array is permitted to contain only a single data type. While the "number of times" is clearly an integer, the IP address looks like it needs to treated as a string, since it has three "dots".

When we consider that the IP address actually is a human-friendly representation of a 32-bit binary address, we might be tempted to convert from the four decimal representations to a single binary value, convert that number to a single decimal value and use that as the subscript... Time out!!! No home system can have that many items in an array.

On the other hand, we could program in C and use structs or parallel arrays. However, that makes the programming very messy.

Fortunately, PHP allows different data types to be stored in the same array. It even allows strings to be used as keys (a more general kind of subscript). We will use the latter technique.

N.B. In the code below, the first occurrence of each of most of the built-in functions has been made into a link to the PHP site. There are many other functions that have not been used here. Investigate the PHP site to find out about the others.

Back to Top

A Simple Sample

As an example, you can populate a one-dimensional array with student averages using the students' names as keys:

$averages = array( "Cathy" => 96,
                   "Guru" => 98,
                   "Elena" => 99,
                   "Harrison" => 96);

This call to the array function results in the creation of a four-element array called $averages. To access and print Harrison's average, we could use the following statement:

echo "Harrison's average is " . $averages["Harrison"] . ".";

Or to calculate the average of the student averages:

$total = 0;
foreach ($averages as $avg)
   $total += $avg;
$classave = $total / count($averages);

or to print out each student's name and his average and the overall average:

foreach ($averages as $name => $avg)
   echo "<p>$name: $avg</p>";
echo "<p>Class average: $classave</p>";

There are many other things that can be done with arrays using built-in functions. Have a look at the PHP web site for a lot more information.

Back to Top

A Solution

A sample line from the httpd error log would look like:

[Sun Oct 27 20:53:53 2002] [error] [client 24.100.213.243] File does not exist: /var/www/html/scripts/root.exe

An easy way to find the IP address from which the contact was initiated is by finding the word "client" to find the beginning and the "]" to find the end.

  1. Process the file:
    $fh = fopen("/var/log/httpd/error_log", "r") or
          die("File /var/log/httpd/error_log cannot be opened.\n");
    $str = fgets($fh, 2048);
    while( !feof($fh) ) {
       $pos = strpos($str, "client");
       if( !($pos === false) ) {
          // EXTRACT IP ADDR
          $posbeg = strpos($str, " ", $pos) + 1;
          $posend = strpos($str, "]", $pos) - 1;
          $ipaddr = substr($str, $posbeg, $posend - $posbeg + 1);
          // ADD LEADING ZEROES TO ADDR
          $octets = explode(".", $ipaddr);
          for($i = 0; $i < 4; $i++)
             $octets[$i] = substr("000" . $octets[$i], -3, 3);
          $ipaddr = implode(".", $octets);
          // INCREMENT COUNTER or START NEW ELEMENT
          if( array_key_exists($ipaddr, (array)$visitors) )
             $visitors[$ipaddr]++;
          else 
             $visitors[$ipaddr] = 1;
       }
       $str = fgets($fh, 2048);
    }
    fclose($fh);
    
  2. Do some error checking, reporting and sorting:
    if( count($visitors) < 1) die("There were no visitors.\n");
    echo "<p>Total different visitors: " . count($visitors). "</p>\n";
    ksort($visitors);
    
  3. Print the table
    echo "<table border cellpadding=5>\n";
    echo "<tr><th>Visitor</th><th align=right># of Times</th></tr>\n";
    foreach( $visitors as $key => $value) {
       // ELIMINATE LEADING ZEROES
       $octets = explode(".", $key);
       for($i = 0; $i < 4; $i++)
          $octets[$i] = (int)$octets[$i];
       $ipaddr = implode(".", $octets);
       // PRINT LINE OF TABLE
       echo "<tr><td>$ipaddr</td><td align=right>$value</td></tr>\n";
    }
    echo "</table>\n";
    

© DFStermole 2002
Created: 31 Oct 02
Last Modified: 31 Oct 02