User Tools

Site Tools


eg-259:homework:php-files

Files and PHP

Hints for Coursework 2, prepared by Dr C.P. Jobling for Examples Class on Monday 9th December 2008.

Getting Started

The starting point for the coursework is the newsong.html page that was discussed in an earlier examples class (See Assignments> Coursework 2> Getting Started Tutorial on Blackboard). As an html file this will be something like:

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>
      Song-o-Matic: Add Your Favourite Song to Our Database
    </title>
  </head>
  <!-- Write an XHTML document to create a form that collects
       favourite popular songs, including the name of the song, the
       composer, and the performing artist or group. This document
       must call one CGI program when the form is submitted and
       another to request a current list of survey results. -->
   <body>
   <h1>
      Song-o-Matic: Add Your Favourite Song!
   </h1>
   <p>
     Add your favourite song to the World Wide Web's database of favourite
     songs!
   </p>
   <form method="post"
    id="song" action="receipt.php">
     <table border="0">
        <tr><th>Song Title</th>
            <td>
              <input type="text" size="40" 
                     name="title" value="Pink Panther Theme" />
            </td>
        </tr>
        <tr><th>Composer</th>
            <td>
              <input type="text" size="40" 
                     name="composer" value="Henry Mancini" />
            </td>
        </tr>
        <tr><th>Artist or Group</th>
            <td>
              <input type="text" size="40" 
                     name="artist_or_group" value="The Henry Mancini Orchestra" />
            </td>
        </tr>
      </table>
      <input type="submit" name="newsong" value="Add Song" /> 
      <input type="reset" name="reset" value="Reset Form" />
    </form>
    <p>Here are the current <a href="favsongs.php">favourites</a>.
  </body>
</html>

To convert this to PHP, simply rename the file to newsong.php. Edit the first line so that instead of

<?xml version="1.0"?>

it says

<?php echo("<?xml version=\"1.0\" encoding=\"utf-8\" ?>"); ?>

Also, you should remove the value attributes from each of the text fields. These were for the mock-up and your form will be getting real song information from its user. Copy your new file newsong.php to your web server's htdocs directory (or a subfolder thereof).

In the demo, I shall probably use Netbeans to create a new project and complete this step using copy and paste.

Create a data file

To create this example web application we shall be building up the songs listing part first. So, the next thing we need is a file format. Last time,. we agreed that it would be a text file, one song per line and we'd allow comments (lines starting with #). It's also useful to ignore blank lines. A suitable example data file that we can use for testing would be:

# A comment
# Format: Songtitle|Composer(s)|Artist or Group
Don't sleep in the subway darling!|Tony and Jackie Trent|Petula Clark
Help!|John Lennon and Paul McCartney|The Beatles

A Song|A Composer|An Artist or Group

Save this to the project directory. If you are running a web server on Linux, make sure that the file is writable by the web server. There are a couple of ways to do this. The easiest way is to make the file writable by others: chmod o+w songs.dat.1) On windows, this is probably not a problem.

Create a File Reader/Displayer

Opening a file for reading and displaying it is probably less challenging that extracting data from a web form and writing it into a file. So we'll start there. In this example, I will build up a file reader displayer in stages. This reflects my way of working, but it is not a bad way I think. We start small, building functionality step-by step and having working, testable code at each stage. I find that this approach works better than trying to plan a complete solution in advance and then struggling to implement it because there are too many unanswered questions, and opportunities for introducing coding errors. Also, I find that taking small steps gives me more of a sense of achievement and the impetus to keep going.

Step 1: Open a file, and read it line by line

Note we don't have to open the file, simply call $lines = file(“songs.dat”); to read the file into an array of strings with one line per array element. This will only work with text files as records are affectively deliminated by the end-of-line character which is \n on Unix and \r\n on Windows.

<?php 
// Read file into an array (one element per line)
$lines = file("songs.dat");
 
// print every line
$line_no = 1;
foreach ($lines as $line) {
    print "$line_no: $line <br />\n"; // \n has no effect on the browser but makes the HTML source look better.	
    $line_no++;
}
?>

Step 2: Extract the data in a line into an array

For thsi step, we take the line data and explode it on the | character which we decided to use for a field deliminator.

<?php
// Read file into an array (one element per line)
...
 
// print every line
foreach($lines as $line) {
    ....
    // explode each line into records
    $data = explode("|", $line);
    print "$line_no: ";
    foreach($data as $value) {
        print "\$value = $value; ";
    }
    print "<br />\n";	
    $line_no++;
}
?>

Step 3: Skip blank lines and comments

To skip blank lines or lines that start with a comment we use a regular expression:

<?php
// Read file into an array (one element per line)
...
 
// print every line
foreach($lines as $line) {
    // slip lines that start with a comment
    // or empty lines
    if (! preg_match("/(^#)|(^$)/", line) {
        // explode each line into records
        ...
    }
}
?>

Step 4: Extract array data into variables for printing

Once you have the data exploded into an array, it is trivial to assign the values to variables:

<?php
// Read file into an array (one element per line)
...
 
// print every line
foreach($lines as $line) {
    ....
    // explode each line into records
    // if data valid
      $title = $data[0];
      $composer = $data[1];
      $artist = $data[2];
      print "$line_no: " .
         "<em>Song title</em>: $title; " . // could use $data[0] here of course
         "<em>Composer</em>: $composer; " .
         "<em>Artist or Group</em>: $artist;" .
         "<br />\n";	
      $line_no++;
   ...
}
?>

Step 5: Format data as an HTML table

The task is almost complete, simply surround the loop with a table to get the list output.

<?php 
// Read file into an array (one element per line
?>
<table border="1">
  <tr><th>Song</th><th>Composer</th><th>Artist</th></tr>
<?php
  // Operate on every line
  foreach ($lines as $line) {
    // Ignore any line that starts with a comment or empty lines
    ...	
      // Extract data values
      $data = explode("|", trim($line)); // trim removes trailing white space
      $title = $data[0];
      $composer = $data[1];
      $artist = $data[2];
      print "<tr> <td> $title </td> <td> $composer </td> <td> $artist </td> </tr>";	
    }
  }
?>
</table>

Processing the web form data

We have written code to read data in the format we have specified. Now we need to write some PHP to handle the form data and write it into a file. We do this in the action target of the form newsong.php which is receipt.php. Our intention is to extract the song data submitted in the form, create a new record for it in the form we specified, write the data to disk, then return a receipt. The format of the receipt was given to you in the last tutorial so the framework already exists:

<?xml version="1.0" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>
      Song-o-Matic: Thanks for your input
    </title>
  </head>
  <!-- Returned to the user as a receipt for their input -->
   <body>
   <h1>
      Song-o-Matic: Thanks for your input
   </h1>
   <p>
     <strong>This is a mock-up of what we want the results to look like.
     Always useful to have this when you are designing a web app!</strong>
   </p>
   <p>Thanks for your input. We have added <em>Henry Mancini</em> &quot;The Pink Panther Theme&quot; 
   as performed by The Henry Mancini Orchestra to the Song-o-Matic database of the Web's favourite 
   songs.</p>\n";
   <p>You can <a href="newsong.php">Add another</a> or see the
   current <a href="favsongs.php"> complete list</a>.</p>
   </body>
</html>

Extraction of the data is easy:

<?php 
$title = $_POST["title"];
$composer = $_POST["composer"];
$artist_or_group = $_POST["artist_or_group"];
?>

So is adding the data to the output. Here's one way to do it:“ .

print "<p>Thanks for your input. We have added <em>$composer</em> &quot;$title&quot; " .
   "as performed by $artist or group to the Song-o-Matic database of the Web's favourite " .
   "songs.</p>\n";

If you are a java programmer you will be suprised to see that writing the data is as simple as:

<?php
$songs = fopen("songs.dat","a"); // open for append
fwrite($songs, "$title|$composer|$artist\n"); // write data with | deliminator and end-of-line marker
?>

On testing, the only likely problem will be with the file permissions: song.dat must be writable by the web server.

In production, you would need to lock the file for exclusive access using flock to prevent two simultaneous form submissions attempting to write data into the file at the same time. More detail on the use of flock is to be found in the PHP manual.

Summary

I have given you sufficient information here to make the implementation of the Course Work 2 exercise trivial.

Additional Exercise

  1. Use the ideas presented in the second database example to put the entire application into a single PHP file. Then comment on why this might not be a good idea for very large applications.
  2. Extend the code to allow comments that do not start at the start of the line: e.g. All you need is love|Lennon and McCartney|The Beatles # This is the greatest Beatles son ever!
  3. How could you deal with a comment symbol (hash #) that was part of a song title?
  4. Try implementing the song database in PHP with MySQL.

More Homework Exercises

1)
In practice this is rather insecure. It is better to change the group ownership of the file to be the same as the group that the web server runs as (say www): sudo chgrp www songs.dat then change permissions to be group writable chmod 664 songs.dat. Now although everyone can open the file to read it, only the web server and the file owner can open the file for writing. Of course, this requires that you have administrator access to the files that you need to change group for.
eg-259/homework/php-files.txt · Last modified: 2011/05/05 17:36 by eechris