Reputation: 449
I have a verification form for registering users and I am trying this concept of try-catching for validating input data in my model. Let me show you my code first:
Model Register
public function register (string $username, string $email, string $email_repeat, string $password, string $password_repeat, string $ip, string $reCaptcha) {
$this->username = trim($username);
$this->email = trim($email);
$this->email_repeat = trim($email_repeat);
$this->password = $password;
$this->password_repeat = $password_repeat;
$this->ip = $ip;
$this->reCaptcha_response = $reCaptcha;
$this->reCaptcha = new ReCaptcha(GOOGLE_CAPTCHA_SECRET); //the API secret is in the config.php file
$this->verifyEmail();
$this->verifyUsername();
$this->verifyPassword();
$this->validateReCaptcha($this->reCaptcha_response, $this->ip);
$this->db->insertRow("INSERT INTO users (username, password, email) VALUES (?, ?, ?)", [$this->username, $this->password, $this->email]);
}
public function verifyEmail () {
if(empty($this->email) || empty($this->email_repeat)) {
throw new \Exception('Email address is empty');
}
$isTaken = $this->db->getRow("SELECT COUNT(*) as count FROM users WHERE email= ?", [$this->email]);
if($isTaken->count > 0){
throw new \Exception('This email is taken');
}
}
private function verifyUsername () {
if(strlen($this->username) < 3 || strlen($this->username) > 15) {
throw new \Exception('Username must be between 3 and 15 symbols');
}
$isTaken = $this->db->getRow("SELECT COUNT(*) as count FROM users WHERE username= ?", [$this->username]);
if($isTaken->count > 0){
throw new \Exception('This username is taken');
}
}
Controller:
public function register ()
{
if($this->post != null) {
try {
$register = $this->tableRegister->register(
$_POST['username'],
$_POST['email'],
$_POST['email_repeat'],
$_POST['password'],
$_POST['password_repeat'],
$_SERVER['REMOTE_ADDR'],
$_POST['g-recaptcha-response']
);
} catch (\Exception $err) {
$this->flashMessage->error($err->getMessage());
}
}
$this->renderView('users/register');
}
As you can see in the model, both methods validateEmail
and validateUsername
go through the database and check if the email or username are taken. If both of them are taken, I want to catch both of the exceptions, as well as every other exception that might occur - if email is taken, if username is taken, if username is invalid.. everything. Currently my script ends at the first exception. How can I do this?
Upvotes: 1
Views: 1391
Reputation: 1665
When an exception is thrown, code following the statement will not be executed, and PHP will attempt to find the first matching catch block. If an exception is not caught, a PHP Fatal Error will be issued with an "Uncaught Exception ..." message, unless a handler has been defined with set_exception_handler() (source).
A good solution here is to add the errors
property, hasErrors
and getErrors
methods for the model. This will allow you to store and to handle error check for each form field.
For example:
public function verifyEmail () {
if(empty($this->email) || empty($this->email_repeat)) {
$this->errors['email'] = 'Email address is empty';
return false;
}
$isTaken = $this->db->getRow("SELECT COUNT(*) as count FROM users WHERE email= ?",
[$this->email]);
if($isTaken->count > 0){
$this->errors['email'] = 'This email is taken';
return false;
}
return true;
}
public function hasErrors() {
return !empty($this->errors);
}
public function getErrors() {
return $this->errors;
}
Add check to the register
method in the model:
if (!$this->hasErrors()) {
$this->db->insertRow("INSERT INTO users (username, password, email) VALUES (?, ?, ?)",
[$this->username, $this->password, $this->email]);
}
Then add check in your controller:
if ($this->tableRegister->hasErrors()) {
$this->flashMessage->error('The form has error(s): '
.implode("; \r\n", $this->tableRegister->getErrors()));
} else {
// code for user registration here
}
Upvotes: 2
Reputation: 1181
You need to have a try-catch in your Model register
function, e.g.
public function register (string $username, string $email, string $email_repeat, string $password, string $password_repeat, string $ip, string $reCaptcha) {
try{
$this->username = trim($username);
$this->email = trim($email);
$this->email_repeat = trim($email_repeat);
$this->password = $password;
$this->password_repeat = $password_repeat;
$this->ip = $ip;
$this->reCaptcha_response = $reCaptcha;
$this->reCaptcha = new ReCaptcha(GOOGLE_CAPTCHA_SECRET); //the API secret is in the config.php file
$this->verifyEmail();
$this->verifyUsername();
$this->verifyPassword();
$this->validateReCaptcha($this->reCaptcha_response, $this->ip);
$this->db->insertRow("INSERT INTO users (username, password, email) VALUES (?, ?, ?)", [$this->username, $this->password, $this->email]);
} catch(\Exception ex){
//handle error
throw new \Exception("Error registering user"); //this will pass the exception up to the calling method instead of just killing the page
}
}
If either verifyEmail
or verifyUsername
throw errors, these stop the rest of the code from executing. As they're not handled correctly, this stops execution of the entire page.
Hope this helps!
Upvotes: 0