Jump to content
Larry Ullman's Book Forums
Sign in to follow this  
Christopher

Ch18, My Solutions To Pursue Exercises With Questions

Recommended Posts

Larry and all,

Please you take a look at my solutions and my questions.

 

1. Apply the same validation techniques to login.php as used in register.php.
2. Make the login form sticky.
3. Add a last_login DATETIME field to the users table and update its value when a user logs in. Use this information to indicate to the user how long it has been since the last time she or he accessed the site.

      The SQL to alter the table:

ALTER TABLE users
        ADD COLUMN last_login DATETIME NOT NULL
        AFTER active
;


4. If you’ve added the last_login field, use it to print a message on the home page as to how many users have logged in in the past, say, hour or day.
5. Validate the submitted email address in forgot_password.php using the Filter extension or a regular expression.

 

These are the pursue exercises in the book, expect the last one 'administrator pages', which I am still working on.

 

Here are the three involved scripts - login.php, index.php and forgot_password.php (new code are in red):

 

<?php # Script 18.8 - login.php
# PURSUE 1 - Apply the same validation techniques to login.php as used in register.php.
# PURSUE 2 - Make the login form sticky.
# PURSUE 3 - Add a last_login DATETIME field to the users table and update its value when a user logs in.
# PURSUE 4 - print a message on the home page as to how many users have logged in in the past, say, hour or day.


// This is the login page for the site.
require ('includes/config.inc.php');
$page_title = 'Login';
include ('includes/header.php');

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    require (MYSQL);
    
// PUESUE 1 - Apply the same validation techniques to login.php as used in register.php
               // Trim all the incoming data:
    $trimmed = array_map('trim', $_POST);
            
    // Validate the email address:
            if (filter_var($trimmed['email'], FILTER_VALIDATE_EMAIL)) {
        $e = mysqli_real_escape_string ($dbc, $trimmed['email']);
    } else {
                                $e = FALSE;
        echo '<p class="error">You forgot to enter your email address!</p>';
    }

        
//    if (!empty($_POST['email'])) {
//        $e = mysqli_real_escape_string ($dbc, $_POST['email']);
//    } else {
//        $e = FALSE;
//        echo '<p class="error">You forgot to enter your email address!</p>';
//    }
    
    // Validate the password:
    if (preg_match ('/^\w{4,20}$/', $trimmed['pass']) ) {  //Here the password input name is pass in the form below!!
        $p = mysqli_real_escape_string ($dbc, $trimmed['pass']);
    } else {
                                $p = FALSE;
        echo '<p class="error">You forgot to enter your password!</p>';
    }   
     
        
//    if (!empty($_POST['pass'])) {
//        $p = mysqli_real_escape_string ($dbc, $_POST['pass']);
//    } else {
//        $p = FALSE;
//        echo '<p class="error">You forgot to enter your password!</p>';
//    }
    
    if ($e && $p) { // If everything's OK.

         // Query the database:
// PURSUE 3 solution: select last_login value(both formatted and original) as part of the login query, and store the data in $_SESSION for data transfer between scripts.
// Then fetch the data and do the calculation in index.php 
           
         $q = "SELECT user_id, first_name, user_level, DATE_FORMAT(last_login, '%a, %b %e at %l:%i%p') as f_last_login, last_login FROM users WHERE (email='$e' AND pass=SHA1('$p')) AND active IS NULL";        
          
         $r = mysqli_query ($dbc, $q) or trigger_error("Query: $q\n<br />MySQL Error: " . mysqli_error($dbc));

         if (@mysqli_num_rows($r) == 1) { // A match was made.
                            
             // Register the values:
             $_SESSION = mysqli_fetch_array ($r, MYSQLI_ASSOC);
                                
// PURSUE 4 - print a message on the home page as to how many users have logged in in the past, say, hour or day.        
 
             $q = "SELECT COUNT(user_id) FROM users WHERE last_login > DATE_SUB(NOW(), INTERVAL 60 MINUTE)";
             $r = mysqli_query($dbc, $q) or trigger_error("Query: $q\n<br>MySQL Error: " . mysqli_error($dbc));
                                
             if (@mysqli_num_rows($r) == 1) {  
                 $row  = mysqli_fetch_array($r, MYSQLI_NUM); 
                 $_SESSION['count_uid'] = $row[0];
             } else {
                 echo '<p class="error">There is some system error, please contact administrator!</p>';
             }
                                                                                       
                                
// PURSUE 3 - update last_login column when a user logs in   

// Update last_login to current time: (last login time was stored in session already)                                 
             $q = "UPDATE users SET last_login=NOW()";
                       
             $r = mysqli_query($dbc, $q) or trigger_error("Query: $q\n<br>MySQL Error: " . mysqli_error($dbc));
                                
             if (@mysqli_num_rows($r) != 1) {
             echo '<p class="error">There is some system error, please contact administrator!</p>';
                                }

   
                                mysqli_free_result($r); 
                                mysqli_close($dbc); 

                                // Redirect the user:
                                $url = BASE_URL . 'index.php'; // Define the URL.
                                ob_end_clean(); // Delete the buffer.
                                header("Location: $url");
                                exit(); // Quit the script.

                        } else { // No match was made.
                                echo '<p class="error">Either the email address and password entered do not match those on file or you have not yet activated your account.</p>';
                        }

    } else { // If everything wasn't OK.
        echo '<p class="error">Please try again.</p>';
    }
    
    mysqli_close($dbc); 

} // End of SUBMIT conditional.
?>

<h1>Login</h1>
<p>Your browser must allow cookies in order to log in.</p>
<form action="login.php" method="post">
    <fieldset>
<!-- PURSUE 2 - Make the login form sticky.
My Question: in register.php, trimmed $_POST values like $trimmed['first_name'] are used for form stickiness, is it also ok to just use $_POST value here? (I think that way we can exactly preserve what the user just input)
-->            
    <p><b>Email Address:</b> <input type="text" name="email" size="20" maxlength="60"  value="<?php if (isset($_POST['email'])) echo $_POST['email']; ?>" /></p>
    <p><b>Password:</b> <input type="password" name="pass" size="20" maxlength="20" value="<?php if (isset($_POST['pass'])) echo $_POST['pass']; ?>" /></p>
    <div align="center"><input type="submit" name="submit" value="Login" /></div>
    </fieldset>
</form>

<?php include ('includes/footer.php'); ?>

 

 

 

<?php # Script 18.5 - index.php
// This is the main page for the site.

// Include the configuration file:
require ('includes/config.inc.php');

// Set the page title and include the HTML header:
$page_title = 'Welcome to this Site!';
include ('includes/header.php');

// Welcome the user (by name if they are logged in):
echo '<h1>Welcome';
if (isset($_SESSION['first_name'])) {
    echo ", {$_SESSION['first_name']}";
}
echo '!</h1>';

// PURSUE 3 - indicate to the user how long it has been since the last time the user accessed the site
if (isset($_SESSION['last_login'])) {  //do calculation only the user logged in
        // Date difference calculation routine:
        $date1 = time();
        $date2 = strtotime($_SESSION['last_login']);
        $dateDiff = $date1 - $date2;
        $fullDays = floor($dateDiff/(60*60*24));
        $fullHours = floor(($dateDiff-($fullDays*60*60*24))/(60*60));
        $fullMinutes = floor(($dateDiff-($fullDays*60*60*24)-($fullHours*60*60))/60);
        $sec_used = ($fullDays * 60 * 60 * 24) + ($fullHours * 60 * 60) + ($fullMinutes * 60);
        $sec_rem = $dateDiff - $sec_used;
        echo "<h4>The last time you logged in is: " . $_SESSION['f_last_login'] . "</h4>";
        echo "<h4>There have been $fullDays days, $fullHours hours $fullMinutes minutes and $sec_rem seconds since you last logged in.</h4>";


// PURSUE 4 - print a message on the home page as to how many users have logged in in the past, say, hour or day.        
        echo "<h5>There have been " . $_SESSION['count_uid'] . " users logged in in the last hour.</h5>";

        
}
?>
<p>Spam spam spam spam spam spam
spam spam spam spam spam spam
spam spam spam spam spam spam
spam spam spam spam spam spam.</p>
<p>Spam spam spam spam spam spam
spam spam spam spam spam spam
spam spam spam spam spam spam
spam spam spam spam spam spam.</p>

<?php include ('includes/footer.php'); ?>

 

 

The result of the index.php:

 

User Registration

Welcome, Mary! The last time you logged in is: Fri, Jan 17 at 3:05PM There have been -1 days, 15 hours 22 minutes and 19 seconds since you last logged in. There have been 0 users logged in in the last hour.

Spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam.

Spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam spam.

 

My question: it seems the calculation of time difference has some issue here:

        $date1 = time();
        $date2 = strtotime($_SESSION['last_login']);
        $dateDiff = $date1 - $date2;

I debugged in Netbeans and found that $date1 is < $date2, so the $dateDiff is a negative number. Any hint for the root cause?

 

 

Another issue is when I use Netbeans to debug, I have to comment out database closing statements such as

                                //mysqli_free_result($r);  // get around debug issue
                                //mysqli_close($dbc);  // get around debug issue

otherwise there will be error during debugging. Also, I have to do some getaround in here:

                            // Add the user to the database:
                            $q = "INSERT INTO users (email, pass, first_name, last_name, active, registration_date)
                              VALUES ('$e', SHA1('$p'), '$fn', '$ln', '$a', NOW() )";
                            $r = mysqli_query ($dbc, $q) or trigger_error("Query: $q\n<br />MySQL Error: " . mysqli_error($dbc));
                            $test_affected_rows = mysqli_affected_rows($dbc); // Get around debug issue

                            if ($test_affected_rows == 1) { // If it ran OK.

The reason is during debugging in Netbeans, affected_rows always return -1, so I have to use a temporary variable here and manually change -1 to 1 during debug. I researched a lot on the Internet, but haven't got the answer yet.

 

 

 

<?php # Script 18.10 - forgot_password.php
# PURSUE 5 - Validate the submitted email address in forgot_password.php using the Filter extension or a regular expression.


// This page allows a user to reset their password, if forgotten.
require ('includes/config.inc.php');
$page_title = 'Forgot Your Password';
include ('includes/header.php');

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    require (MYSQL);

    // Assume nothing:
    $uid = FALSE;

    // Validate the email address...
// PURSUE 5 - Validate the submitted email address in forgot_password.php using the Filter extension
//    if (!empty($_POST['email'])) {
                if (filter_var($_POST['email'], FILTER_VALIDATE_EMAIL))
{   ...(
This one is easy so the rest of the code are ignored.)

Share this post


Link to post
Share on other sites

Very good work! As for the last login issue, it could be a timezone thing. Did you think about storing the last_login as a timestamp so that you don't have to convert it to a timestamp in PHP?

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×
×
  • Create New...