Previous   -   Index   -   Next  >


MySQL Mailing List


This application involves three PHP files:
mailform.php produces the form for user input and
maillistdb.php validates the form data and writes to the database and
mlistviewdb.php displays the mailing list.

The front end - mailform.php

<?php echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/tr/xhtml1/DTD/xhtml11.dtd" >
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-gb">
<head>
<title>Mailing list form</title>
<meta name="Author" content="k.mcmanus@gre.ac.uk"/>
<link href="mailform.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h2>Join our product mailing list</h2>
<h4>Plain XHTML MySQL version</h4>
<form method="post" action="maillistdb.php" enctype="application/x-www-form-urlencoded">
<?php
error_reporting(E_ERROR);
// parse POST input to restore checkboxes
$platDoze  = ( preg_match('/doze/',$_POST['platforms'])  ? 'checked="checked"' : '' );
$platUnix   = ( preg_match('/Unix/',$_POST['platforms'])   ? 'checked="checked"' : '' );
$platBSD  = ( preg_match('/BSD/',$_POST['platforms'])  ? 'checked="checked"' : '' );
$platLinux  = ( preg_match('/Linux/',$_POST['platforms'])  ? 'checked="checked"' : '' );
$platTails  = ( preg_match('/Tails/',$_POST['platforms'])  ? 'checked="checked"' : '' );
$platOSX = ( preg_match('/OSX/',$_POST['platforms']) ? 'checked="checked"' : '' );
$platIOS  = ( preg_match('/iOS/',$_POST['platforms'])  ? 'checked="checked"' : '' );
$platAndroid  = ( preg_match('/droid/',$_POST['platforms'])  ? 'checked="checked"' : '' );
?>
<p>
Title <input type="text" name="title" size="8" maxlength="16" value="<?php echo $_POST['title'] ?>" />
Initials <input type="text" name="initials" size="6" maxlength="12" value="<?php echo $_POST['initials'] ?>" />
Surname <input type="text" name="surname" size="32" maxlength="64" value="<?php echo $_POST['surname'] ?>" /><br /><br />
Email <input type="text" name="email" size="32" maxlength="64" value="<?php echo $_POST['email'] ?>" /><br /><br />
Your computer platform(s):<br />
<input type="checkbox" name="platforms[]" value="Windoze" <?php echo $platWindoze ?> />Windoze<br />
<input type="checkbox" name="platforms[]" value="Unix" <?php echo $platUnix ?> />Unix<br />
<input type="checkbox" name="platforms[]" value="BSD" <?php echo $platBSD ?> />BSD<br />
<input type="checkbox" name="platforms[]" value="Linux" <?php echo $platLinux ?> />Linux<br />
<input type="checkbox" name="platforms[]" value="Tails" <?php echo $platTails ?> />Tails<br />
<input type="checkbox" name="platforms[]" value="OSX" <?php echo $platOSX ?> />OS X<br />
<input type="checkbox" name="platforms[]" value="iOS" <?php echo $platIOS ?> />Open VMS<br /><br />
<input type="checkbox" name="platforms[]" value="Android" <?php echo $platAndroid ?> />Android<br />
<input type="submit" name="Sub" value="Add Details"/>
<input type="reset" value="Reset Form"/>
</p></form>
<hr />
</body></html>

Things to notice in the mailform.php code

The various form fields get default values from POST data.
Values for the Platforms arrive as a single string. This is searched using portable regular exppression preg_match() to find defaults for the checkboxes.

There is no database connection in this code.

The back end - maillistdb.php

<?php
require '/home/mkg01/include/mysql.php';
define('URLFORM', 'http://stuweb.cms.gre.ac.uk/~mkg01/web/PHP/mailform.php');
define('URLLIST', 'http://stuweb.cms.gre.ac.uk/~mkg01/web/PHP/maillistdb.php');
$referer = $_SERVER['HTTP_REFERER'];

// if rererrer is not the form redirect the browser to the form
if ( $referer != URLFORM && $referer != URLLIST ) {
   header('Location: ' . URLFORM);
}

// function to clean up any magic quotes
function stripslashes_array($data) {
    if (is_array($data)){
        foreach ($data as $key => $value){
            $data[$key] = stripslashes_array($value);
        }
        return $data;
    }else{
        return stripslashes($data);
    }
}
?>

<?php echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/tr/xhtml1/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-gb">
<head>
<title>Results from mailing list program maillistdb.php</title>
<meta name="Author" content="k.mcmanus@gre.ac.uk"/>
<link href="mailform.css" rel="stylesheet" type="text/css" />
</head>
<body>

<?php
// blimmin magic quotes should be off
if (get_magic_quotes_gpc()) {
   $_POST = stripslashes_array($_POST);
}
// copy POST input to scalars
extract($_POST);
$strValid  = '';

// $plats is a space delimited string
if ( isset($platforms) ) {
   $plats = '';
   if ( is_array($platforms) ) {
      foreach ($platforms as $value) { $plats .= "$value "; }
   } else {
      $plats = $platforms;
   }
}

// the Yes button commits input to the database
if ( isset($_POST['yesButton']) ) {
   if ( !($link=mysql_connect($host, $user, $passwd)) ) {
      echo '<p>Error connecting to database</p>';
   } else {
      mysql_select_db($dbName);
      $escapedSurname = mysql_real_escape_string($surname);
      $query = "INSERT INTO mlist (title, initials, surname, email, platforms)" .
               "VALUES ('$title', '$initials', '$escapedSurname', '$email', '$plats')";
      if ( !mysql_query($query,$link) ) {
         echo '<p>Insert error</p>';
      } else {
         echo "<p>Thank you for completing the registration form $title $initials $surname\n" .
           '<br /><br />Perhaps you would like to ' .
           '<a href="mlistviewdb.php"> look at the database</p>';
      }
      mysql_close($link);
   }
   exit('</head><body></body></html>');
}

// use hidden fields to pass state back to the input form (No/Back button)
echo '<form action="' . URLFORM . '" method="post"><p>' . "\n" .
     '<input type="hidden" name="title" value="' . $title . '" />' . "\n" .
     '<input type="hidden" name="initials" value="' . $initials . '" />' . "\n" .
     '<input type="hidden" name="surname" value="' . $surname . '" />' . "\n" .
     '<input type="hidden" name="email" value="' . $email . '" />' . "\n" .
     '<input type="hidden" name="platforms" value="' . $plats . '" /></p>' . "\n";

// validate input from the form
if ( preg_match('/[^a-zA-Z ]|^$/',$title) )
   $strValid .= "  Title<br />\n";
if ( preg_match('/[^a-zA-Z ]|^$/',$initials) )
   $strValid .= "  Initials<br />\n";
if ( preg_match("/[^a-zA-Z \-']|^$/",$surname) )
   $strValid .= "  Surname<br />\n";
if ( !preg_match('/^([\w\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/',$email) )
   $strValid .= "  Email<br />\n";
if ( $plats == '' ) $strValid .= "  Platforms<br />\n";

// report input data back to the user
$platstring = preg_replace('/ /',"<br />\n     ",$plats);
echo '<h2>Information received from the mail list form</h2><p>' .
      "\n Title: $title<br />" .
      "\n Initials: $initials<br />" .
      "\n Surname: $surname<br />" .
      "\n Email: $email<br />" .
      "\n Platforms:<br />     $platstring</p>";

// if there are validation failures
// report them to the user an offer a 'Back' button
if ( $strValid ) {
?>

<p>The following details have not been entered correctly:<br /><br />
<?php echo $strValid ?>
<br /><br />Please go back and correct them<br /><br />
<input type="submit" value="Back"/></p>

<?php
} else {
// otherwise ask for input confirmation
?>

<p>Please confirm that the above details are correct<br />
<input type="submit" value="No"/>  
<input type="submit" name="yesButton" value="Yes" onclick="this.form.action='<?php echo URLLIST ?>'"/>
</p>

<?php } ?>

</form>
</body>
</html>

Things to notice in the maillistdb.php code

Values for $host, $user, $passwd and $dbName are in an external file mysql.php. This is included into maillistdb.php using require.


require '/home/mkg01/include/mysql.php';

Long URL strings are held in constants. Check to make sure that the request ($_SERVER[HTTP_REFERER]) has come from either this script or from mailform.php. If not then the browswer is redirected back to the form.


define("URLFORM", "http://stuweb.cms.gre.ac.uk/~mkg01/web/PHP/mailform.php");
define("URLLIST", "http://stuweb.cms.gre.ac.uk/~mkg01/web/PHP/maillistdb.php");
$referer = $_SERVER['HTTP_REFERER'];

if ( $referer != URLFORM && $referer != URLLIST ) {
   echo '<meta http-equiv="Refresh" content="0; url=' . URLFORM . '" />';
   exit ('</head><body></body></html>');
}

If magic quotes are set to on (very old fashioned) in your installation it will cause problems with apostrophes in your POST data. The PHP function stripslashes() undoes the magic quotes mahem. The function stripslashes_array() does the same thing for arrays. The contents of $_POST are copied into local variables. $_POST['Platforms'] is converted from an array into a space delimited string.


if (get_magic_quotes_gpc()) {
   $_POST = stripslashes_array($_POST);
}
$title     = $_POST['Title'];
$initials  = $_POST['Initials'];
$surname   = $_POST['Surname'];
$email     = $_POST['Email'];
$strValid  = '';
$platforms = '';
if ( $_POST['Platforms'] ) {
   if ( is_array($_POST['Platforms']) ) {
      foreach ($_POST['Platforms'] as $value) { $platforms .= "$value "; }
   } else {
      $platforms = $_POST['Platforms'];
   }
}

If the 'Yes' button was pressed then the data is comitted to a database.

Note the escaping of the surname string to allow for apostrophes.


if ( isset($_POST['yesButton']) ) {
   if ( !($link=mysql_connect($host, $user, $passwd)) ) {
      printError(sprintf("Error connecting to database from %s, by user %s", $host, $user));
   } else {
      mysql_select_db($dbName);
      $escapedSurname = mysql_escape_string($surname);
      $query = "INSERT INTO mlist (title, initials, surname, email, platforms)" .
               "VALUES ('$title', '$initials', '$escapedSurname', '$email', '$platforms')";
      if ( !mysql_query($query,$link) ) {
         echo '<p>Insert error</p>';
      } else {
         echo "Thank you for completing the registration form $title $initials $surname\n" .
              '<br /><br />Perhaps you would like to' .
              '<a href="mlistviewdb.php"> look at the database';
      }
      mysql_close($link);
   }
   exit('</head><body></body></html>');
}

Data from the form is embedded as hidden fields in a form to maintain state when moving between the form and itself.


echo '<form action="' . URLFORM . '" method="post"><p>' . "\n" .
     '<input type="hidden" name="Title" value="' . $title . '" />' . "\n" .
     '<input type="hidden" name="Initials" value="' . $initials . '" />' . "\n" .
     '<input type="hidden" name="Surname" value="' . $surname . '" />' . "\n" .
     '<input type="hidden" name="Email" value="' . $email . '" />' . "\n" .
     '<input type="hidden" name="Platforms" value="' . $platforms . '" /></p>' . "\n" ;

preg_match() is used to validate the form data. Invalid fields concatentated into a string $strValid.


if ( preg_match('/[^a-zA-Z ]|^$/',$title) )
   $strValid .= "  Title<br />\n";
if ( preg_match('/[^a-zA-Z ]|^$/',$initials) )
   $strValid .= "  Initials<br />\n";
if ( preg_match("/[^a-zA-Z \-']|^$/",$surname) )
   $strValid .= "  Surname<br />\n";
if ( !preg_match('/^([\w\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/',$email) )
   $strValid .= "  Email<br />\n";
if ( $platforms == '' ) $strValid .= "  Platforms<br />\n";

preg_replace() is used to replace spaces in $platforms for formatting feedback to the user.


$platstring = preg_replace('/ /',"<br />\n &nbsp; &nbsp; ",$platforms);
echo ('<h2>Information received from the mail list form</h2>' .
      "\n Title: $title<br />" .
      "\n Initials: $initials<br />" .
      "\n Surname: $surname<br />" .
      "\n Email: $email<br />" .
      "\n Platforms:<br /> &nbsp; &nbsp; $platstring");

If $strValid has a value then report on it's contents and only display a 'Back' button.

Otherwise the data probably meets the data expectations of this application so offer a 'No' button that simply submits the hiden form data back to URLFORM so that it can be edited and a 'Yes' button in which the onclick JavaScript event handler changes the action of the form to submit to itself. This will cause the hidden form data to be comitted to the database.


if ( $strValid ) {
?>

<p>The following details have not been entered correctly:<br /><br />
<?php echo $strValid ?>
<br /><br />Please go back and correct them<br /><br />
<input type="submit" value="Back"/></p>

<?php } else { ?>

<p>Please confirm that the above details are correct<br />
<input type="submit" value="No"/>  
<input type="submit" name="yesButton" value="Yes" 
   onclick="this.form.action='<?php echo URLLIST ?>'"/>
</p>

<?php } ?>

This script protects itself by ensuring that POST data only arrives either from the approved form or from itself.

Why would it do this?

Information from the form is tested to see if it matches the data expectations of the application.

The data is presented to the user for confirmation before it is comitted to the database.

Because some valid characters may upset the SQL statement the data is escaped before it is incorporated into the INSERT string.

Hidden form fields are used to carry data between this script and the form and itself.

JavaScript is used to alter submission of the form depending on the button which was pressed.

The database - mlistviewdb.php

<?php echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/tr/xhtml1/DTD/xhtml11.dtd" >
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-gb">
<head>
<title>MySQL Mailing List</title>
<meta name="Author" content="k.mcmanus@gre.ac.uk"/>
<link href="mailform.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h1>MySQL Mailing List</h1>
<p><a href="http://staffweb.cms.gre.ac.uk/~k.mcmanus/web/PHP/mailListDB.html">Return to the Mailing List notes</a></p>
<?php
require '/home/mkg01/include/mysql.php';

function printError($errorMesg) {
   printf("<br /> %s <br />\n", $errorMesg);
}

if ( !($link=mysql_connect($host, $user, $passwd)) ) {
   printError(sprintf("Error connecting to database from %s, by user %s", $host, $user));
} else {
   mysql_select_db($dbName);
   $query = 'SELECT * FROM mlist';
   if ( !($result = mysql_query($query,$link)) ) {
      printError(sprintf("Error %s : %s", mysql_errno(), mysql_error()));
   } else {
      if ( $result == 0 ) {
         echo "Error " . mysql_errno() . ":" . mysql_error();
      } else {
         echo "<table><thead><tr><th>Row</th>";
         for ( $i = 0 ; $i < mysql_num_fields($result) ; $i++ ) {
            echo "<th>" . mysql_field_name($result,$i) . "</th>\n";
         }
         echo "</tr></thead>\n<tbody>";
         for ( $i = 0 ; $i < mysql_num_rows($result) ; $i++ ) {
            echo "<tr><td>" . ($i + 1) . "</td>";
            $row = mysql_fetch_row($result);
            for ( $j = 0 ; $j < mysql_num_fields($result) ; $j++ ) {
               echo "<td>&nbsp;" . $row[$j] . "</td>";
            }
            echo "</tr>\n";
         }
         echo "</tbody></table>";
      }
   }
}
?>
</body></html>

Things to notice in the mlistviewdb.php code

That sensitive MySQL authentication information is included using require() - just like in maillistdb.php

This is largely similar to the code in mySimpleForm.php but with a single SQL query.

So what is in mysql.php?

<?php
$host    = 'studb.cms.gre.ac.uk';
$user    = 'mkg01';
$passwd  = 'Lerdorf';
$dbName  = 'mdb_mkg01';
?>

This is kept securely outside the public_html tree. An access control list (acl) for mysql.php allows access for user httpd and the owner only.

Any number of PHP variables and functions could be concealed in this manner. In this example the include file is primarily used as a mechanism for concealing the password.

The Unix team here provide more help on password concealment.

In a nutshell...

mkdir ~/include
mv mysql.php ~/include
cd ~/include
chmod 755 .
chmod 600 mysql.php
setfacl -s user:httpd:r--,user::rw-,group::---,mask:r--,other:--- mysql.php

Previous   -   Index   -   Next  >

best viewed using Mozilla browsers
© k.mcmanus 2004
Valid XHTML 1.! . Valid CSS . WCAG priority 3 approved