<?php // RAY_register_and_confirm_common.php
// COMMON CODE AVAILABLE TO ALL OUR REGISTER-AND-CONFIRM SCRIPTS
// SET ERROR REPORTING SO WE CAN DEBUG OUR SCRIPTS EASILY
error_reporting(E_ALL);
// CONNECTION AND SELECTION VARIABLES FOR THE DATABASE
$db_host = "localhost"; // PROBABLY THIS IS OK
$db_name = "??"; // GET THESE FROM YOUR HOSTING COMPANY
$db_user = "??";
$db_word = "??";
// OPEN A CONNECTION TO THE DATA BASE SERVER AND SELECT THE DB
$mysqli = new mysqli($db_host, $db_user, $db_word, $db_name);
// DID THE CONNECT/SELECT WORK OR FAIL?
if ($mysqli->connect_errno)
{
$err
= "CONNECT FAIL: "
. $mysqli->connect_errno
. ' '
. $mysqli->connect_error
;
trigger_error($err, E_USER_ERROR);
}
// AN EMAIL VALIDATION SCRIPT THAT RETURNS TRUE OR FALSE
function check_valid_email($email)
{
// IF PHP 5.2 OR ABOVE, WE CAN USE THE FILTER
// MAN PAGE: http://php.net/manual/en/intro.filter.php
if (strnatcmp(phpversion(),'5.2') >= 0)
{
if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) return FALSE;
}
// IF LOWER-LEVEL PHP, WE CAN CONSTRUCT A REGULAR EXPRESSION
else
{
$regex
= '/' // START REGEX DELIMITER
. '^' // START STRING
. '[A-Z0-9_-]' // AN EMAIL - SOME CHARACTER(S)
. '[A-Z0-9._-]*' // AN EMAIL - SOME CHARACTER(S) PERMITS DOT
. '@' // A SINGLE AT-SIGN
. '([A-Z0-9][A-Z0-9-]*\.)+' // A DOMAIN NAME PERMITS DOT, ENDS DOT
. '[A-Z\.]' // A TOP-LEVEL DOMAIN PERMITS DOT
. '{2,6}' // TLD LENGTH >= 2 AND =< 6
. '$' // ENDOF STRING
. '/' // ENDOF REGEX DELIMITER
. 'i' // CASE INSENSITIVE
;
if (!preg_match($regex, $email)) return FALSE;
}
// FILTER or PREG DOES NOT TEST IF THE DOMAIN OF THE EMAIL ADDRESS IS ROUTABLE
$domain = explode('@', $email);
if ( checkdnsrr($domain[1],"MX") || checkdnsrr($domain[1],"A") ) return TRUE;
// EMAIL NOT ROUTABLE
return FALSE;
}
<?php // RAY_register_and_confirm_create_table.php
// SET UP THE DATA BASE TABLE FOR THE REGISTER-AND-CONFIRM PROCESS
// BRING IN OUR COMMON CODE
require_once('RAY_register_and_confirm_common.php');
// REMOVE THE OLD VERSION OF userTable
$res = $mysqli->query('DROP TABLE userTable');
// CREATING THE userTable
$sql
=
"
CREATE TABLE userTable
( email_address VARCHAR(96) NOT NULL DEFAULT '' UNIQUE
, activate_code VARCHAR(32) NOT NULL DEFAULT ''
, activated_yes INT NOT NULL DEFAULT 0
)
"
;
$res = $mysqli->query($sql);
// IF mysqli::query() RETURNS FALSE, LOG AND SHOW THE ERROR
if (!$res)
{
$err
= "QUERY FAIL: "
. $sql
. ' ERRNO: '
. $mysqli->errno
. ' ERROR: '
. $mysqli->error
;
trigger_error($err, E_USER_ERROR);
}
// ALL DONE - SHOW WHAT WE CREATED
$sql = "SHOW CREATE TABLE userTable";
$res = $mysqli->query($sql);
$row = $res->fetch_object($res);
echo "<pre>";
var_dump($row);
<?php // RAY_register_and_confirm_admin.php
// ADMINISTRATOR SCRIPT FOR A VIEW OF THE REGISTER-AND-CONFIRM PROCESS
// BRING IN OUR COMMON CODE
require_once('RAY_register_and_confirm_common.php');
// SET THE URL OF THE CONFIRMATION PAGE HERE:
$confirm_page = $_SERVER["HTTP_HOST"] . '/RAY_register_and_confirm.php';
// GATHER INFORMATION ABOUT THE SUCCESSFUL REGISTRATIONS
$sql = "SELECT email_address FROM userTable WHERE activated_yes > 0 ORDER BY email_address";
if (!$res = $mysqli->query($sql))
{
$err
= "QUERY FAIL: "
. $sql
. ' ERRNO: '
. $mysqli->errno
. ' ERROR: '
. $mysqli->error
;
trigger_error($err, E_USER_ERROR);
}
// HOW MANY ARE CONFIRMED
$num = $res->num_rows();
echo "<br/>$num CONFIRMED REGISTRATIONS:" . PHP_EOL;
// SHOW WHO IS CONFIRMED
while ($row = $res->fetch_assoc($res))
{
$email_address = $row["email_address"];
$uri
= '<a href="mailto:'
. $email_address
. '?subject=THANK YOU FOR YOUR REGISTRATION'
. '">'
. $email_address
. '</a>'
;
echo "<br>$uri" . PHP_EOL;
}
// GATHER INFORMATION ABOUT THE PENDING REGISTRATIONS
$sql = "SELECT email_address, activate_code FROM userTable WHERE activated_yes = 0 ORDER BY email_address";
if (!$res = $mysqli->query($sql))
{
$err
= "QUERY FAIL: "
. $sql
. ' ERRNO: '
. $mysqli->errno
. ' ERROR: '
. $mysqli->error
;
trigger_error($err, E_USER_ERROR);
}
// HOW MANY ARE NOT CONFIRMED
$num = $res->num_rows();
echo "<br/>$num REGISTRATIONS NOT YET CONFIRMED:" . PHP_EOL;
// SHOW WHO IS NOT CONFIRMED
while ($row = $res->fetch_assoc())
{
$email_address = $row["email_address"];
$activate_code = $row["activate_code"];
// CONSTRUCT A CLICKABLE LINK TO RE-SEND THE ACTIVATION CODE
$msg = '';
$msg .= 'WE SEE YOU HAVE NOT CONFIRMED YOUR REGISTRATION YET. TO CONFIRM, PLEASE CLICK THIS LINK: ' . PHP_EOL;
$msg .= "http://" . $confirm_page . "?q=$activate_code";
$msg .= PHP_EOL;
$uri
= '<a href="mailto:'
. $email_address
. '?subject=PLEASE CONFIRM REGISTRATION'
. '&body='
. $msg
. '">'
. $email_address
. '</a>'
;
echo "<br/>$uri" . PHP_EOL;
}
<?php // RAY_register_and_confirm.php
// HANDLE THE REGISTER-AND-CONFIRM PROCESS
// BRING IN OUR COMMON CODE
require_once('RAY_register_and_confirm_common.php');
// PART ONE - IF THIS IS A POST-METHOD REQUEST FOR CONFIRMATION
if
( (!empty($_GET["q"]))
&& (is_string($_GET["q"]))
&& (!empty($_POST["e"]))
&& (is_string($_POST["e"]))
)
{
// PROVIDE FILTERING AND SANITY CHECKS FOR THE EXTERNAL INPUT
$activate_code = preg_replace('/[^A-Z0-9]/i', '', $_GET["q"]);
$activate_code = substr(trim($activate_code), 0, 32);
$safe_activate_code = $mysqli->real_escape_string($activate_code);
$email_address = strtolower($_POST["e"]);
$email_address = substr(trim($email_address), 0, 96);
$safe_email_address = $mysqli->real_escape_string($email_address);
// PREPATE AND RUN THE QUERY TO CONFIRM THE REGISTRATION
$sql = "UPDATE userTable SET activated_yes = activated_yes + 1 WHERE activate_code = '$safe_activate_code' AND email_address = '$safe_email_address' LIMIT 1";
if (!$res = $mysqli->query($sql))
{
$err
= "QUERY FAIL: "
. $sql
. ' ERRNO: '
. $mysqli->errno
. ' ERROR: '
. $mysqli->error
;
trigger_error($err, E_USER_ERROR);
}
// DID THE UPDATE AFFECT A ROW?
if ( $mysqli->affected_rows ) die("THANK YOU - YOUR ACTIVATION IS COMPLETE");
// SHOW ERROR RESPONSE
die("SORRY - YOUR ACTIVATION CODE OR EMAIL ADDRESS WAS NOT FOUND");
}
// PART TWO - IF THIS IS A GET-METHOD REQUEST FOR CONFIRMATION
if
( (!empty($_GET["q"]))
&& (is_string($_GET["q"]))
&& (empty($_POST["e"]))
)
{
// PROVIDE FILTERING AND SANITY CHECKS FOR THE EXTERNAL INPUT
$activate_code = preg_replace('/[^A-Z0-9]/i', '', $_GET["q"]);
$activate_code = substr(trim($activate_code), 0, 32);
$safe_activate_code = $mysqli->real_escape_string($activate_code);
// GET THE EMAIL ADDRESS FROM THE ACTIVATION CODE
$sql = "SELECT email_address FROM userTable WHERE activate_code = '$safe_activate_code' LIMIT 1";
if (!$res = $mysqli->query($sql))
{
$err
= "QUERY FAIL: "
. $sql
. ' ERRNO: '
. $mysqli->errno
. ' ERROR: '
. $mysqli->error
;
trigger_error($err, E_USER_ERROR);
}
if ( $res->num_rows() == 0 ) die("SORRY - YOUR ACTIVATION CODE WAS NOT FOUND");
// SET UP THE EMAIL ADDRESS HINT - billy@gmail.com HINTS bill? gmail com
$row = mysql_fetch_assoc($res);
$arr = explode('@', $row["email_address"]);
$uid = $arr[0];
$dmn = $arr[1];
$len = strlen($dmn);
$poz = strrpos($dmn, '.');
$email_hint
= substr($uid, 0, -1)
.'?'
. ' '
. substr($dmn, 0, $poz)
. ' '
. end(explode('.', $dmn))
;
// SHOW THE CONFIRMATION FORM WITH THE EMAIL ADDRESS HINT
echo '<form method="post" action="' . $_SERVER["REQUEST_URI"] . '">' . PHP_EOL;
echo 'TO CONFIRM REGISTRATION, ENTER YOUR EMAIL ADDRESS HERE:' . PHP_EOL;
echo "<br/>HINT: IT LOOKS LIKE $email_hint" . PHP_EOL;
echo '<input name="e" />' . PHP_EOL;
echo '<input type="submit" />' . PHP_EOL;
echo '</form>';
die();
}
// PART THREE - IF THE REGISTRATION FORM HAS BEEN POSTED
if
( (!empty($_POST["e"]))
&& (is_string($_POST["e"]))
&& (empty($_GET))
)
{
// VALIDATE THE EMAIL ADDRESS
if (!check_valid_email($_POST["e"])) die("SORRY - THE EMAIL ADDRESS IS NOT USABLE");
// NORMALIZE THE EMAIL ADDRESS
$email_address = trim($_POST["e"]);
$email_address = strtolower($email_address);
$safe_email_address = $mysqli->real_escape_string($email_address);
// MAKE THE ACTIVATION CODE
$activate_code
= md5
( mt_rand()
. time()
. $email_address
. $_SERVER["REMOTE_ADDR"]
)
;
$safe_activate_code = $mysqli->real_escape_string($activate_code);
// INSERT THE EMAIL ADDRESS AND ACTIVATION CODE INTO THE DATA BASE TABLE
$sql = "INSERT INTO userTable
( email_address
, activate_code
) VALUES
( '$safe_email_address'
, '$safe_activate_code'
)"
;
if (!$res = $mysqli->query($sql))
{
// IF ERROR, BUT NOT A DUPLICATE EMAIL ADDRESS
if ( $mysqli->errno != 1062 )
{
$err
= "QUERY FAIL: "
. $sql
. ' ERRNO: '
. $mysqli->errno
. ' ERROR: '
. $mysqli->error
;
trigger_error($err, E_USER_ERROR);
}
// IF A DUPLICATE REGISTRATION, RECOVER THE ACTIVATION CODE
else
{
$sql = "SELECT activate_code FROM userTable WHERE email_address = '$safe_email_address' AND activated_yes = 0 LIMIT 1";
$res = $mysqli->query($sql);
$num = $res->num_rows();
if ($num == 0) die("THANK YOU - YOU ARE ALREADY REGISTERED AND CONFIRMED");
$row = $res->fetch_assoc();
$activate_code = $row["activate_code"];
}
}
// SEND THE ACTIVATION EMAIL
$msg = '';
$msg .= 'THANK YOU FOR YOUR REGISTRATION. TO CONFIRM, PLEASE CLICK THIS LINK:' . PHP_EOL;
$msg .= "http://" . $_SERVER["HTTP_HOST"] . $_SERVER["PHP_SELF"] . "?q=$activate_code";
mail
( $email_address
, 'PLEASE CONFIRM YOUR REGISTRATION'
, $msg
)
;
// TELL THE CLIENT TO CHECK HER EMAIL
die("PLEASE CHECK YOUR EMAIL FOR A CONFIRMATION LINK");
}
// PART FOUR - THE FORM FOR REGISTRATION
$form = <<<ENDFORM
<form method="post">
TO REGISTER, ENTER YOUR EMAIL ADDRESS HERE:
<input name="e" />
<input type="submit" />
</form>
ENDFORM;
echo $form;
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (4)
Commented:
How can I make the script generate say 3, 5, 10 or more uniquely generated tokens and then send a batch of '1-time use' links to the newly registered users email address in one go ? (instead of sending the user only 1 single link from a single token.)
I would like a new user to register but instead of only being emailed a single url link to a page on my website, I would like them to receive a batch of 5 different 'single use' links which the user can return to and click-on to access different pages of my website.
I have been searching for answers to this for weeks so your help will be utterly amazing!
Thanks!
Author
Commented:This SitePoint article looks good to me. Just repeat the part about generating a token five times, store the URLs in an array and send the collection.
https://www.sitepoint.com/generating-one-time-use-urls/
Commented:
It seem failing all the occurrence of instance like this: $num = $res->num_rows();
In example: Fatal error: Call to undefined method mysqli_result::num_rows() in /RAY_register_and_confirm.
Do you think it is a version problem?
Commented:
I ran into the same problem. For everybody who is going to use this script with up to date PHP Versiions:
change num_rows() to num_rows
it's not a function anymore, but a variable
for instance, here:
if ( $res->num_rows == 0 ){
die("SORRY - YOUR ACTIVATION CODE WAS NOT FOUND");
}
further you have to replace
$row = mysql_fetch_assoc($res);
with
$row = mysqli_fetch_assoc($res);