Emily Emily - 5 months ago 29
PHP Question

PDO Sign Up Not Functioning or throwing Errors

I have recently switched over to using PDO from mySQLi for all mySQL queries and for the most part, it has been pretty straight forward. I recently completed a web app and am now implementing a PDO based sign up/sign in workflow for the first time.

After extensive research, I wrote the PHP and PDO for sign up and login. On Submit of sign up, the page refreshes, nothing appears in DB and the page does not redirect accordingly.

Below is the code to connect to the DB, register a user, and redirect them to their profile page.

<?php
require_once 'dbconfig.php';
$url = "http://mattmcclintock.com";
if($user->is_loggedin()!="")
{
$user->redirect($url);
}

if(isset($_POST['btn-signup']))

$uname = $_POST['txt_uname'];
$umail = $_POST['txt_umail'];
$upass = $_POST['txt_upass'];

if($uname=="") {
$error[] = "provide username !";
}
else if($umail=="") {
$error[] = "provide email id !";
}
else if(!filter_var($umail, FILTER_VALIDATE_EMAIL)) {
$error[] = 'Please enter a valid email address !';
}
else if($upass=="") {
$error[] = "provide password !";
}
else if(strlen($upass) < 6){
$error[] = "Password must be atleast 6 characters";
}
else
{
$stmt = $DB_con->prepare("SELECT user_name,user_email FROM users WHERE user_name=:uname OR user_email=:umail");
$stmt->execute(array(':uname'=>$uname, ':umail'=>$umail));
$row=$stmt->fetch(PDO::FETCH_ASSOC);

if($row) {
if($row['user_name']==$uname) {
$error[] = "sorry username already taken !";
}
else
{
$error[] = "sorry email id already taken !";
}
}
else
{
if($user->register($fname,$lname,$uname,$umail,$upass))
{
$user->redirect($url);
} else {
echo "There is an error";
}
}
}
?>


And the corresponding HTML for to signup with.

<form id="signupform" method="POST" action="registerUser.php" class="form-horizontal" role="form">
<div id="signupalert" style="display:none" class="alert alert-danger">
<p>Error:</p>
<span></span>
</div>
<div class="form-group">
<label for="email" class="col-md-3 control-label">Username</label>
<div class="col-md-9">
<input type="text" class="form-control" name="txt_uname" placeholder="Enter Username" value="<?php if(isset($error)){echo $uname;}?>" />
</div>
</div>
<div class="form-group">
<label for="firstname" class="col-md-3 control-label">Email</label>
<div class="col-md-9">
<input type="text" class="form-control" name="txt_umail" placeholder="Enter E-Mail ID" value="<?php if(isset($error)){echo $umail;}?>" />
</div>
</div>
<div class="form-group">
<label for="password" class="col-md-3 control-label">Password</label>
<div class="col-md-9">
<input type="password" class="form-control" name="txt_upass" placeholder="Enter Password" />
</div>
</div>
<div class="form-group">
<!-- Button -->
<div class="col-md-offset-3 col-md-9">
<button type="submit" class="btn btn-block btn-primary" name="btn-signup">
<i class="glyphicon glyphicon-open-file"></i>&nbsp;SIGN UP
</button>
</div>
</form>


Any help would be greatly appreciated. Having written exclusively in SQL switching over to PDO has been relatively difficult and I'm trying to get a feel for all of the common DB interactions and how to handle them with PDO.

Below I added code for class.user.php

<?php
class USER
{
private $db;

function __construct($DB_con)
{
$this->db = $DB_con;
}

public function register($fname,$lname,$uname,$umail,$upass)
{
try
{
$stmt = $this->db->prepare("INSERT INTO users(user_name,user_email,user_pass)
VALUES(:uname, :umail, :upass)");

$stmt->bindparam(":uname", $uname);
$stmt->bindparam(":umail", $umail);
$stmt->bindparam(":upass", $upass);
$stmt->execute();

return $stmt;
}
catch(PDOException $e)
{
echo $e->getMessage();
}
}

public function login($uname,$umail,$upass)
{
try
{
$stmt = $this->db->prepare("SELECT * FROM users WHERE user_name=:uname OR user_email=:umail LIMIT 1");
$stmt->execute(array(':uname'=>$uname, ':umail'=>$umail));
$userRow=$stmt->fetch(PDO::FETCH_ASSOC);
if($stmt->rowCount() > 0)
{
if(password_verify($upass, $userRow['user_pass']))
{
$_SESSION['user_session'] = $userRow['user_id'];
return true;
}
else
{
return false;
}
}
}
catch(PDOException $e)
{
echo $e->getMessage();
}
}

public function is_loggedin()
{
if(isset($_SESSION['user_session']))
{
return true;
}


}

public function redirect()
{
header("Location: mattmcclintock.com");
}

public function logout()
{
session_destroy();
unset($_SESSION['user_session']);
return true;
}
}
?>

Answer

Your problem could be many things, so I will review your code line by line with some comments.

registerUser.php

<?php
// good idea to turn on the errors during development
error_reporting(E_ALL);
ini_set('display_errors', 1);

require 'dbconfig.php';

$url = "http://mattmcclintock.com";

$uname = null; 
$umail = null;
$upass = null;

// always declare the variables that you will be using

// this will be an associative array where the keys will be the name of the fields
// and the values will be their associated error message
$errors = [];

// new: this will hold the main error message
$message = null;


// we'll comment this out for now
/*
if ($user->is_loggedin() != "") {
      $user->redirect($url);
}
*/


// everything below should be wrapped inside this IF condition
if (isset($_POST['btn-signup'])) { 
    // good idea to trim non-password fields
    $uname = trim($_POST['txt_uname']); 
    $umail = trim($_POST['txt_umail']);
    $upass = $_POST['txt_upass']; 

    // I am assuming you want to check every field independently, so you need to 
    // group your IF conditions for each field

    // empty string is a falsey (i.e. '' == false => true; though '' === false => false), 
    // so we'll shorten your IF conditions from  $var == ''  to  !$var
    if (!$uname) {
        $errors['txt_uname'] = "provide username !"; 
    }

    if (!$umail) {
        $errors['txt_umail'] = "provide email id !"; 
    } elseif (!filter_var($umail, FILTER_VALIDATE_EMAIL)) {
        $errors['txt_umail'] = 'Please enter a valid email address !';
    }

    if (!$upass) {
        $errors['txt_upass'] = "provide password !";
    } elseif (strlen($upass) < 6) {
        $errors['txt_upass'] = "Password must be atleast 6 characters"; 
    }


    // Assuming you only want to check for duplications when there are no errors with the username or email

    if (empty($errors['txt_umail']) && empty($errors['txt_umail'])) {
        if ($row = $user->getUserByUsernameOrEmail($uname, $umail)) {
            if ($row['user_name'] == $uname) {
                $errors['txt_uname'] = "sorry username already taken !";
            } else {
                $errors['txt_umail'] = "sorry email id already taken !";
            }
        }
    }

    // empty array is also a falsey
    if ($errors) {
        $message = "There are errors";

    // WHOA! where did $fname and $lname come from?!
    // original: if($user->register($fname,$lname,$uname,$umail,$upass)) {

    } elseif ($user->register($uname, $umail, $upass)) {
        $user->redirect($url);
    } else {
        $message = "An unexpected error has occurred";
    } 
}
?>

<form id="signupform" method="POST" action="registerUser.php" class="form-horizontal" role="form">
    <div id="signupalert" style="display:none" class="alert alert-danger">
        <p>Error: <?= $message ?></p>
        <?php foreach ($errors as $name => $message) : ?>
          <span><?= $message ?></span><br>
        <?php endforeach ?>
    </div>
    <div class="form-group">
       <label for="email" class="col-md-3 control-label">Username</label>
       <div class="col-md-9">
          <input type="text" class="form-control" name="txt_uname" placeholder="Enter Username" value="<?= $uname ?>">
       </div>
    </div>
    <div class="form-group">
       <label for="firstname" class="col-md-3 control-label">Email</label>
       <div class="col-md-9">
          <input type="text" class="form-control" name="txt_umail" placeholder="Enter E-Mail ID" value="<?= $umail ?>">
       </div>
    </div>
    <div class="form-group">
       <label for="password" class="col-md-3 control-label">Password</label>
       <div class="col-md-9">
          <input type="password" class="form-control" name="txt_upass" placeholder="Enter Password">
       </div>
    </div>
    <div class="form-group">
       <!-- Button -->                                        
       <div class="col-md-offset-3 col-md-9">
          <button type="submit" class="btn btn-block btn-primary" name="btn-signup">
             <i class="glyphicon glyphicon-open-file"></i>&nbsp;SIGN UP
          </button>          
       </div>
    <!-- missing: closing tag -->
    </div>
</form>

class.user.php

<?php
class USER 
{
    private $db;

    public function __construct($DB_con)
    {
        $this->db = $DB_con;
    }

    public function getUserByUsernameOrEmail($uname, $umail)
    {
        try {   
            $stmt = $this->db->prepare("
                SELECT user_name, user_email FROM users WHERE user_name= ? OR user_email = ?
            ");
            $stmt->execute([$uname, $umail]);
            return $stmt->fetch(PDO::FETCH_ASSOC); 
        } catch(PDOException $e) {
            echo $e->getMessage();
        }
        return null;
    }

    // WHOA! where did $fname and $lname come from?!
    // original: public function register($fname, $lname, $uname, $umail, $upass)

    public function register($uname, $umail, $upass)
    {
        try {   
            $stmt = $this->db->prepare("
                INSERT INTO users (user_name, user_email, user_pass) VALUES(?, ?, ?)
            ");
            // keep things short and simple
            $stmt->execute([$uname, $umail, $upass]);            
            // better to return the number of affected rows when dealing with insert/update/delete queries
            return $stmt->rowCount(); 
        } catch(PDOException $e) {
            echo $e->getMessage();
        }
        return false;
    }

    public function login($uname, $umail, $upass)
    {
        try {
            $stmt = $this->db->prepare("SELECT * FROM users WHERE user_name = ? OR user_email= ?");
            $stmt->execute([$uname, $umail]);
            if ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
                if (password_verify($upass, $row['user_pass'])) {
                    $_SESSION['user_session'] = $row['user_id'];
                    return true;
                }
            }
        } catch (PDOException $e) {
            echo $e->getMessage();
        }
        return false;
    }

    public function is_loggedin()
    {
        return isset($_SESSION['user_session']); // shortened
    }

    // your code is expecting the URL to be passed into the method
    // original: public function redirect()

    public function redirect($url) 
    {
        header("Location: $url");
        // it's good practice to exit 
        exit;
    }

    public function logout()
    {
        session_destroy();
        unset($_SESSION['user_session']);
        return true;
    }
}
?>

IMO I think your USER class is doing too much and that you are violating the Single Responsibility Principle.

For more information on booleans, read this link.

For more information on redirecting, read the answers on this post.