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++;
}
?>
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++;
}
?>
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
...
}
}
?>
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++;
...
}
?>
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>
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> "The Pink Panther Theme"
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> "$title" " .
"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
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.
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!
How could you deal with a comment symbol (hash #
) that was part of a song title?
Try implementing the song database in PHP with MySQL.
More Homework Exercises