Check my Code

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

ibnclaudius
Forum Newbie
Posts: 12
Joined: Thu Dec 08, 2011 7:41 pm

Re: Check my Code

Post by ibnclaudius »

Ohh, got it. But i'm not sure about how to use verifyCookie($token). Could you show me an example?

Another thing, what exists() does?
User avatar
Celauran
Moderator
Posts: 6427
Joined: Tue Nov 09, 2010 2:39 pm
Location: Montreal, Canada

Re: Check my Code

Post by Celauran »

exists() checks whether the user exists in the database. Well, technically, that's what validate() does, but validate() is a private function.

I must have been half asleep when I wrote verifyCookie(). The idea is to check for the existence of a 'token' cookie, pass it to the verifyCookie() function, and get back the userID or FALSE if no match is found. Trouble is, you'd need to instantiate the User object, which you can't do without a username. Like I said, I must have been half asleep. It needs rewriting. And rethinking.
ibnclaudius
Forum Newbie
Posts: 12
Joined: Thu Dec 08, 2011 7:41 pm

Re: Check my Code

Post by ibnclaudius »

I am not sure why have three functions doing basically the same thing: login(), validate(), exists()

About the problem with the cookie, you have an idea about how to fix it?

This is what I have:

login.php

Code: Select all

<?php

session_start();

include 'class.php';

if (isset($_POST['username']) && isset($_POST['password'])) {
	$user  = new User($_POST['username']);
	if ($user->exists()) {
		$login = $user->login($_POST['password']);
		if ($login) {
			$_SESSION['user_id'] = $login;
		
	session_write_close();
		} else {
			echo "Login failed.";
		}
	} else {
		header("Location: register.php");
	}
}

?>

<!DOCTYPE html>
<html>
	<head>
		<title>Login Form</title>
	</head>
	<body>
		<form action="" method="post">
			<label for="username">Username: </label>
			<input type="text" name="username" /><br />
			<label for="password">Password: </label>
			<input type="password" name="password" /><br />
			<input type="submit" value="Submit" />
		</form>
	</body>
</html>
class.php

Code: Select all

<?php

class User {
	protected $id;
	protected $username;
	protected $email;
	protected $sql;
	
	private $exists = FALSE;

	public function __construct($username) {
		if (empty($username)) {
			throw new Exception('Username cannot be blank.');
		}

		$this->username = $username;
		$this->sql      = new PDO(DSN, DBUSER, DBPASS);
		$this->exists   = $this->validate();
	}

	private function createLoginToken($id) {
		$token   = $id . md5(microtime());
		$expires = new DateTime();

		$expires->add(new DateInterval('P30D'));

		$query = "INSERT INTO sessions (userID, token, expires)
			  VALUES (:id, :token, :expires)";
		$stmt  = $this->sql->prepare($query);

		$stmt->execute(array(':id'      => $id,
				     ':token'   => $token,
				     ':expires' => $expires->format('Y-m-d H:i:s')));

		setcookie('token', $token, $expires->getTimestamp(), '/');
	}

	private function hashPassword($password, $salt) {
		$string = PASSWORD_SALT . $password . md5($salt);
		$hashed = crypt($string, '$2a$12$' . substr(md5($salt), 0, 22));

		return $hashed;
	}

	private function validate() {
		$query = "SELECT COUNT(id)
			  FROM users
			  WHERE username = :username";
		$stmt  = $this->sql->prepare($query);

		$stmt->execute(array(':username' => $this->username));
		$count = $stmt->fetchColumn();

		return ($count > 0) ? TRUE : FALSE;
	}

	public function exists() {
		return $this->exists;
	}

	public function login($password, $remember = FALSE) {
		$query = "SELECT id, password, UNIX_TIMESTAMP(created) AS salt
			  FROM users
			  WHERE username = :username";
		$stmt  = $this->sql->prepare($query);

		$stmt->execute(array(':username' => $this->username));

		$row = $stmt->fetch(PDO::FETCH_OBJ);

		$hashed = $this->hashPassword($password, $row->salt);

		if ($row->password == $hashed) {
			if ($remember) {
				$this->createLoginToken($row->id);
			}
			return $row->id;
		}

		return FALSE;
	}

	public function registerUser($password, $email) {
		if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
			throw new Exception('Email does not appear to be valid.');
		}
		$this->email = $email;

		$date   = new DateTime();
		$hashed = $this->hashPassword($password, $date->getTimestamp());

		$query = "INSERT INTO users (username, password, email, created)
			  VALUES (:username, :password, :email, :created)";
		$stmt  = $this->sql->prepare($query);

		$success = $stmt->execute(array(':username' => $this->username,
						':password' => $hashed,
						':email'    => $email,
						':created'  => $date->format('Y-m-d H:i:s')));

		return ($success === TRUE) ? $this->sql->lastInsertId() : FALSE;
	}

	public function verifyCookie($token) {
		$query = "SELECT userID
			  FROM sessions
			  WHERE token = :token
			  AND expires > NOW()";
		$stmt  = $this->sql->prepare($query);

		$stmt->execute(array(':token' => $token));

		return $stmt->fetchColumn();
	}
}

?>
db.sql

Code: Select all

CREATE TABLE IF NOT EXISTS `users` (
	`id` int(11) DEFAULT NULL AUTO_INCREMENT,
	`username` varchar(30) DEFAULT NULL,
	`password` varchar(60) DEFAULT NULL,
	`email` varchar(100) DEFAULT NULL UNIQUE,
	`created` datetime DEFAULT NULL,
	PRIMARY KEY (`id`)
) ENGINE=MyISAM;

CREATE TABLE IF NOT EXISTS `sessions` (
	`id` int(11) DEFAULT NULL AUTO_INCREMENT,
	`userID` int(10) DEFAULT NULL,
	`token` varchar(50) DEFAULT NULL,
	`expires` datetime DEFAULT NULL,
	KEY `userID` (`id`,`userID`,`token`,`expires`),
	KEY `token` (`id`,`userID`,`token`,`expires`),
	PRIMARY KEY (`id`)
) ENGINE=MyISAM;

I'm not sure about how hashPassword() works. Each user will have a different $salt and it will be the created date?
User avatar
Celauran
Moderator
Posts: 6427
Joined: Tue Nov 09, 2010 2:39 pm
Location: Montreal, Canada

Re: Check my Code

Post by Celauran »

ibnclaudius wrote:I am not sure why have three functions doing basically the same thing: login(), validate(), exists()
They don't do the same thing at all. Validate checks once whether the user exists in the database and stores that in the exists property, which needs to be private. The exists() method returns the value of the exists property; you can see the value but you can't change it. Login authenticates the user.
ibnclaudius wrote:About the problem with the cookie, you have an idea about how to fix it?
Haven't even had time to give it much thought. I suppose storing the username in the cookie could work.

You might be better off using InnoDB over MyISAM so you can cascade deletes. No point keeping a user's logins stored if the user no longer exists.
ibnclaudius
Forum Newbie
Posts: 12
Joined: Thu Dec 08, 2011 7:41 pm

Re: Check my Code

Post by ibnclaudius »

Finally got the login(), validate(), exists() function..

About the cookie, I'm in the waiting.

Thanks!
Post Reply