Article
The PHP Anthology Volume 2, Chapter 1 - Access Control
Moving on, the createCode method is called internally within the class, and is used to generate the confirmation code that will be sent via email:
Example 1.16. AccessControl/SignUp.php (in SPLIB) (excerpt)
/**
* Creates the confirmation code
* @return void
* @access private
*/
function createCode($login)
{
srand((double)microtime() * 1000000);
$this->confirmCode = md5($login . time() . rand(1, 1000000));
}
The createSignup method is used to insert records into the signup table:
Example 1.17. AccessControl/SignUp.php (in SPLIB) (excerpt)
/**
* Inserts a record into the signup table
* @param array contains user details. See constants defined for
* array keys
* @return boolean true on success
* @access public
*/
function createSignup($userDetails)
{
$login = mysql_escape_string(
$userDetails[USER_TABLE_LOGIN]);
$password = mysql_escape_string(
$userDetails[USER_TABLE_PASSW]);
$email = mysql_escape_string(
$userDetails[USER_TABLE_EMAIL]);
$firstName = mysql_escape_string(
$userDetails[USER_TABLE_FIRST]);
$lastName = mysql_escape_string(
$userDetails[USER_TABLE_LAST]);
$signature = mysql_escape_string(
$userDetails[USER_TABLE_SIGN]);
// First check login and email are unique in user table
$sql = "SELECT * FROM " . USER_TABLE . "
WHERE
" . USER_TABLE_LOGIN . "='$login' OR
" . USER_TABLE_EMAIL . "='$email'";
$result = $this->db->query($sql);
if ($result->size() > 0) {
trigger_error('Unique username and email address required');
return false;
}
$this->createCode($login);
$toName = $firstName . ' ' . $lastName;
$this->to[$toName] = $email;
$sql = "INSERT INTO " . SIGNUP_TABLE . " SET
" . USER_TABLE_LOGIN . "='$login',
" . USER_TABLE_PASSW . "='$password',
" . USER_TABLE_EMAIL . "='$email',
" . USER_TABLE_FIRST . "='$firstName',
" . USER_TABLE_LAST . "='$lastName',
" . USER_TABLE_SIGN . "='$signature',
" . SIGNUP_TABLE_CONFIRM . "='$this->confirmCode',
" . SIGNUP_TABLE_CREATED . "='" . time() . "'";
$result = $this->db->query($sql);
if ($result->isError()) {
return false;
} else {
return true;
}
}
When the registration form is submitted, we'll use this method to create a record of the signup. Note that when the system checks to see whether the submitted user name or email address already exists in the database, a match will trigger an error. You could "catch" this error by defining your own custom error handler (see Chapter 10, Error Handling for more information).
We add slashes to the incoming fields to make sure there are no injection attacks (see Chapter 3, PHP and MySQL). Because we're using QuickForm, any slashes added by magic quotes are automatically removed; but when you're not using QuickForm, be sure to include the script from the section called "How do I write portable PHP code?", which strips quotes from your form code.
Next, we use the sendConfirmation method to send a confirmation email to the person who's just signed up:
Example 1.18. AccessControl/SignUp.php (in SPLIB) (excerpt)
/**
* Sends the confirmation email
* @return boolean true on success
* @access public
*/
function sendConfirmation()
{
$mail = new phpmailer();
$from = each($this->from);
$mail->FromName = $from[0];
$mail->From = $from[1];
$to = each($this->to);
$mail->AddAddress($to[1], $to[0]);
$mail->Subject = $this->subject;
if ($this->html) {
$replace = '<a href="' . $this->listener . '?code=' .
$this->confirmCode . '">' . $this->listener .
'?code=' . $this->confirmCode . '</a>';
} else {
$replace = $this->listener . '?code=' . $this->confirmCode;
}
$this->message = str_replace('<confirm_url/>',
$replace,
$this->message);
$mail->IsHTML($this->html);
$mail->Body = $this->message;
if ($mail->send()) {
return TRUE;
} else {
return FALSE;
}
}
Finally, the confirm method is used to examine confirmations via the URL sent in the email:
Example 1.19. AccessControl/SignUp.php (in SPLIB) (excerpt)
/**
* Confirms a signup against the confirmation code. If it
* matches, copies the row to the user table and deletes
* the row from signup
* @return boolean true on success
* @access public
*/
function confirm($confirmCode)
{
$confirmCode = mysql_escape_string($confirmCode);
$sql = "SELECT * FROM " . SIGNUP_TABLE . "
WHERE " . SIGNUP_TABLE_CONFIRM . "='$confirmCode'";
$result = $this->db->query($sql);
if ($result->size() == 1) {
$row = $result->fetch();
// Copy the data from Signup to User table
$sql = "INSERT INTO " . USER_TABLE . " SET
" . USER_TABLE_LOGIN . "='" .
mysql_escape_string($row[USER_TABLE_LOGIN]) . "',
" . USER_TABLE_PASSW . "='" .
mysql_escape_string($row[USER_TABLE_PASSW]) . "',
" . USER_TABLE_EMAIL . "='" .
mysql_escape_string($row[USER_TABLE_EMAIL]) . "',
" . USER_TABLE_FIRST . "='" .
mysql_escape_string($row[USER_TABLE_FIRST]) . "',
" . USER_TABLE_LAST . "='" .
mysql_escape_string($row[USER_TABLE_LAST]) . "',
" . USER_TABLE_SIGN . "='" .
mysql_escape_string($row[USER_TABLE_SIGN]) . "'";
$result = $this->db->query($sql);
if ($result->isError()) {
return FALSE;
} else {
// Delete row from signup table
$sql = "DELETE FROM " . SIGNUP_TABLE . "
WHERE " . SIGNUP_TABLE_ID . "='" .
$row[SIGNUP_TABLE_ID] . "'";
$this->db->query($sql);
return TRUE;
}
} else {
return FALSE;
}
}
}
If an account is successfully confirmed, the row is copied to the user table (note that I had to re-escape the values stored in the signup table in case they contained SQL injections that were escaped when originally inserted), and the old row in the signup table is deleted. You will need to edit this method if your table structures do not match the ones used here.
Putting this class into action, we'll modify the registration form we built in Chapter 9, Web Page Elements with QuickForm. For the sake of clarity, I've kept the registration form to a single procedural listing, but in practice, to help keep code both maintainable and readable, it would be better to restructure it using classes. This time, I've also used QuickForm's templating features to modify the look of the page; you'll find the details in the code for this chapter. Here, we'll concentrate on the code that's specific to the sign up process:
First, we have to include the five classes we'll be using:
Example 1.20. 6.php (excerpt)
<?php
// Include the MySQL class
require_once 'Database/MySQL.php';
// Include the Session class
require_once 'Session/Session.php';
// Include the SignUp class
require_once 'AccessControl/SignUp.php';
// Include the QuickForm class
require_once 'HTML/QuickForm.php';
// Include the phpmailer class
require_once 'ThirdParty/phpmailer/class.phpmailer.php';
Once we set up the variables we need, we can instantiate our own classes:
Example 1.21. 6.php (excerpt)
// Settings for SignUp class
$listener = 'http://localhost/sitepoint/AccessControl/6.php';
$frmName = 'Your Name';
$frmAddress = 'noreply@yoursite.com';
$subj = 'Account Confirmation';
$msg = <<<EOD
<html>
<body>
<h2>Thank you for registering!</h2>
<div>The final step is to confirm
your account by clicking on:</div>
<div><confirm_url/></div>
<div>
<b>Your Site Team</b>
</div>
</body>
</html>
EOD;
// Instantiate the MySQL class
$db = &new MySQL($host, $dbUser, $dbPass, $dbName);
// Instantiate the Session class
$session = new Session;
// Instantiate the signup class
$signUp = new SignUp($db, $listener, $frmName,
$frmAddress, $subj, $msg, TRUE);
The following code checks to see if we have an incoming confirmation:
Example 1.22. 6.php (excerpt)
// Is this an account confirmation?
if (isset($_GET['code'])) {
if ($signUp->confirm($_GET['code'])) {
$display = 'Thank you. Your account has now been confirmed.' .
'<br />You can now <a href="4.php">login</a>';
} else {
$display = 'There was a problem confirming your account.' .
'<br />Please try again or contact the site ' .
'administrators';
}
// Otherwise display the form
} else {
// ...form creation code omitted...
If not, execution moves on to building the body of the form, which I'll omit here, as it was covered in Chapter 9, Web Page Elements. If you don't have that volume, you can refer to the code archive.