by Barry Andrew, May 2004
Part 1 :: Introduction
Part 2 :: Page output
Part 3 :: Processing user actions
Download complete script
Requirements: PHP4, register_globals are assumed to be off. Ensure you define a path to
a datafile in a directory which has write permission.
Introduction
Most PHP tutorials involving data storage are aimed at databases, usually MySQL. However there are
many people who, for one reason or another, want or need to store data in a plain text file.
In this tutorial we will construct a page which allows the user to
- enter new records
- list the records
and, by clicking in the list,
- edit existing records
- delete records
For data storage I have elected to use an ini-file format, primarily for the ease with which data
can be indexed and retrieved with this format. For those not familiar with this format, an ini file is a list of items with values and the file can be divided into sections. The format is like this
[SECTION_1]
item1=value1
item2=value2
[SECTION_2]
item1=value3
item2=value4
item3=value5
For storing records we will adapt this format so that each section is a record with its id as the section name and the field names of the record will be the items. The data in each record will be:-
- username (record id)
- firstName
- lastName
- email
so our text file will be a series of records like this
[jdoe]
firstname=John
lastname=Doe
email=jd@domain.com
Many of the programming techniques used in this tutorial can also be used with data stored in a database.
Page output
The page will display a data form, so the user can enter new records, and a list of the current records in the file. By clicking on the ID field of a record, that record is selected for editing. The data to be changed will be displayed in the same form as that used for new records. When we are editing a record, only the form will be displayed and not the record list.
By clicking on an 'X' at the end of each record, the user can delete a record.
Input form
The data input form has four text boxes, one for each of the fields. These will normally be blank so the user can enter a new record. When the user wants to edit a record, the text boxes will contain the current values. The form looks like this
The form code is
<FORM method="POST" action="<?php echo $_SERVER['PHP_SELF']?>">
<INPUT TYPE="HIDDEN" name="userdata" value="1">
<?php
if (!empty($username))
echo "<INPUT TYPE=\"HIDDEN\" name=\"username\" value=\"$username\">";
?>
<TABLE style="background: #E0E0E0; border: solid 2pt #666666" >
<TR>
<TD colspan='2' style="border-bottom: solid 1pt #666666; font-weight: bold; text-align: center">
User data
</TD>
</TR>
<TR>
<TD>
User name
</TD>
<TD>
<INPUT TYPE="TEXT" name="username" size="10" maxlength="10"
value='<?php echo $username ?>'
<?php echo empty($username) ? '' : ' DISABLED ' ?> >
</TD>
</TR>
<TR>
<TD>
First name
</TD>
<TD>
<INPUT TYPE="TEXT" name="firstname" size="20" maxlength="20" value='<?php echo $firstname ?>'>
</TD>
</TR>
<TR>
<TD>
Last name
</TD>
<TD>
<INPUT TYPE="TEXT" name="lastname" size="20" maxlength="20" value='<?php echo $lastname ?>'><br>
</TD>
</TR>
<TR>
<TD>
Email address
</TD>
<TD>
<INPUT TYPE="TEXT" name="email" size="30" maxlength="30" value='<?php echo $email ?>'><br>
</TD>
</TR>
<TR>
<TD>
</TD>
<TD>
<INPUT TYPE="SUBMIT" value="Send data">
<?php
if (!empty($username))
echo "<INPUT TYPE=\"BUTTON\" value=\"Cancel\" onClick=\"history.go(-1);\">";
?>
</TD>
</TR>
</TABLE>
</FORM>
A fairly standard table and form but the highlighted code may warrant further explanation.
<INPUT TYPE="HIDDEN" name="userdata" value="1">
When we are processing the form, the existence of a 'userdata' item will tell us that data has been posted. Using a hidden field like this works whether the user clicks the submit button or just hits the enter key.
<?php echo empty($username) ? '' : ' DISABLED ' ?> >
If the user wants to edit a record, the username will contain the id of the record to be edited. The username textbox will be greyed out when editing as we want the user to see but don't want them to be able to change it (because then we wouldn't know which record to update). We do this by disabling the text box
<?php
if (!empty($username))
echo "<INPUT TYPE=\"HIDDEN\" name=\"username\" value=\"$username\">";
?>
Because the username textbox is disabled, the username would not be posted, so, if we're editing, we put the username in a hidden field. Similarly, a 'Cancel' button is placed on the form when a record is selected for editing which will take the user back to the record list.
Record List
The record list is produced by the function listData($data). This is a general purpose function that you can use to list in a table any similarly formatted text file by passing your data array to it ($data).
function listData($data) {
if (count($data) > 0) {
echo "<TABLE border='0' cellspacing='1' cellpadding='2'>\n";
# headings
echo "<tr style='background: #C0C0C0'><th>ID</th>";
list ( $key, $dataArray) = each($data);
foreach($dataArray as $k=>$v) {
echo "<th>$k</th>";
}
echo "<th>Del</th></tr>";
#data
$i = 0;
foreach ($data as $key=>$dataArray) {
$bg = ++$i % 2 ? "#FFFFFF" : "#EEEEEE";
echo "<tr style='background: $bg'>
<td>
<a href='$_SERVER['PHP_SELF']?changeid=$key'>$key<a>
</td>";
foreach ($dataArray as $v) {
echo "<td>$v</td>";
}
echo "<td>[<a href='$_SERVER['PHP_SELF']?delid=$key'>X<a>]</td></tr>";
}
echo "</TABLE>";
}
else echo "<p>No data</p>";
}
Let's have look at what it does
if (count($data) > 0) {
echo "<TABLE border='0' cellspacing='1' cellpadding='2'>\n";
# headings
echo "<tr style='background: #C0C0C0'><th>ID</th>";
|
Check we have data. Create a table and put 'ID' in the first column of the headings |
list ( $key, $dataArray) = each($data);
|
Read the first item in the data array. The record id is placed in $key and the data items
for the record are placed in $dataArray. |
foreach($dataArray as $k=>$v) {
echo "<th>$k</th>";
}
|
Take each data item in turn, putting the fieldname in $k and its value in $v. At this point we are interested only in the fieldnames. Create a column heading in the table for each fieldname |
echo "<th>Del</th></tr>";
|
Write 'Del' to the final column heading. |
foreach ($data as $key=>$dataArray) {
|
We now want each element of the data array to list the data |
$bg = ++$i % 2 ? "#FFFFFF" : "#EEEEEE";
|
Set alternating row background colours (white and light grey) |
echo "<tr style='background: $bg'>
<td>
<a href='$_SERVER[PHP_SELF]?
changeid=$key'>$key<a>
</td>";
|
Start new row. Put the record key in the first column making it a hyperlink to this page
passing "changeid=recordid" in the url |
foreach ($dataArray as $v) {
echo "<td>$v</td>";
}
|
Take each data item and put its value in a new column |
echo "<td>[<a href='$_SERVER[PHP_SELF]
?delid=$key'>X<a>]</td></tr>";
}
|
Put '[X]' in the final column making it a hyperlink to this page
passing "delid=recordid" in the url |
|
End the table |
Processing User Actions
We want to do all the processing and updating before we display the page again so any changes are
immediately displayed. This section is therefore placed above the opening <html> tag.
The first thing we do is initialise our variables and define data file
# initialise variables
$username = $firstname = $lastname = $email = "";
$data = array();
$changed = false;
# define your data file and its path, ensure you have write permission to folder
$myTextFile = 'data/mydata.txt';
|
|
Read the data from the current text file, if it exists, storing it i the array '$data'.
if (file_exists($myTextFile))
$data = parse_ini_file($myTextFile, true);
|
Parse_ini_file() reads the file into the data array. Because we want the section headers,
the second parameter is set to 'true' |
Now check if data was submitted using the form. If it is a new record, it will be added to the $data array. If it's and edit, the current values will be updated. The $data array is a two-dimaensional array where the first key
is the username and the second key is the data field name. If the userdata item was posted we know form data was sent. Username is our section key and the data items are firstname, lastname and email. Add the items to out $data array (or update if it is already there)
if (isset($_POST['userdata'])) {
if (!empty($_POST['username']) && !empty($_POST['email']) ) {
# add new data (or update if exists already)
$data[$_POST['username']]['firstname'] = $_POST['firstname'];
$data[$_POST['username']]['lastname'] = $_POST['lastname'];
$data[$_POST['username']]['email'] = $_POST['email'];
$changed = true;
}
else echo "<p>Must have username and email</p>";}
|
Make sure username and email were entered Set changed to true so we know we need to update the file |
Deletions and edit request come via links in the data table and use query strings to pass the
record id's. In these instances we need to look at the $_GET data.
If a deletion has been requested, 'delid' will have a value.
if (isset($_GET['delid'])) {
# remove deleted item from the array
unset($data[$_GET['delid']]);
$changed = true;
}
|
unset() removes an array item. Again, set changed to true |
If the user has requested to edit a record, changeid has a value
This time, all we do is get the data to be displayed in the edit form
if (isset($_GET['changeid'])) {
# get the items we want to edit so they can appear in the form
$username = $_GET['changeid'];
$firstname = $data[$username]['firstname'];
$lastname = $data[$username]['lastname'];
$email = $data[$username]['email'];
}
|
|
Now check to see if the file needs to be updated, If it does, $changed will be 'true'.
if ($changed) {
# now create new output file, writing in ini file format
$fp = fopen('mydata.txt', 'w');
|
Open the data file in write mode |
|
|
|
Sort the array keys. This puts the array in username order before we write to the file |
foreach ($data as $key=>$dataArray) {
fwrite($fp, "[$key]\n");
|
Taking each record in turn, write section header as '[username]' |
foreach ($dataArray as $k => $v) {
fwrite($fp, "$k=$v\n");
}
fwrite($fp, "\n");
}
fclose($fp);
}
|
Because it is a 2D array, the value assigned to the first level key is itself an array. Loop through this array so each data field item is written as 'fieldname=value'. Leave a blank line between each record (for clarity) and close the file when finished |