New to oop

HI
I’m new to OOP PHP.

I’m trying to create a login class.

The trouble is breaking it down into functions

For example

function check_inputs // check to make sure the username and password values
exist

function connect // connect to mysql

What I am trying to work out is how to conditionally call each function

For example only if a username and password is entered would I want to connect to db and only if a connection is made would I want to check the values against a table

I came up with the solution of returning a value either true or false and evaluating it although it would involve so much code that I would be easier to use procedural php

Lewis

Here is bare bone authentication system using OOP for data access. Notice that rather than creating a login function that the operation is being abstracted to make it more reusable.


<?php
/*
* All database adapters will adhere to this interface to make them interchangable
*/
interface DB {
	public function query($sql,$bind=null);
}

/*
* MySQL database adapter
*/
class MySQL implements DB {

	private
	
	/*
	* Connection link reference
	*/
	$_link;
	
	public function __construct($host,$user,$pwd,$db) {
		$this->_link = mysql_connect($host,$user,$pwd);
		
		if(!$this->_link) {
			throw new Exception('Unable to establish database connection');
		}
			
		if(!mysql_select_db($db,$this->_link)) {
			throw new Exception('Unable to select database');
		}
	}
	
	/*
	* Run a query
	*
	* @param str SQL statement
	* @param array bind variables 
	* @return mix result
	*/
	public function query($sql,$bind=null) {
	
		$sql = $bind === null?$sql:call_user_func_array('sprintf',array_merge(array($sql),$bind));
		$result = mysql_query($sql,$this->_link);
		
		if(!$result) {
			throw new Exception('SQL Query Invalid');
		}
		
		/*
		* When select occurs return result set
		* When insert occurs returned last insert id
		* When update occurs return affected rows
		* Otherwise return true
		*/
		if(strpos($sql,'SELECT') === 0) {
			$rows = array();
			while($row = mysql_fetch_assoc($result)) {
				$rows[] = $row;
			}
			return $rows;
		} else if(strpos($sql,'INSERT') === 0) {
			return mysql_insert_id($this->_link);
		} else if(strpos($sql,'UPDATE') === 0) {
			return mysql_affected_rows($this->_link);
		} else {
			return true;
		}
	
	}
	
}

/*
* Base data access layer
*/
abstract class DAO {

	protected 
	
	/*
	* Database adapter
	*/
	$_db;
	
	/*
	* DAO uses adpater
	*/
	public function __construct(DB $db) {
		$this->_db = $db;
	}

}

/*
* User data access layer
*/
class DAOUser extends DAO {
	
	/*
	* Get a user by matching username and password
	* 
	* @param str username
	* @param str password
	* @return array user data or null (user not matched)
	*/
	public function fetchUserByNameAndPassword($username,$password) {	
		return array_pop($this->_db->query("SELECT * FROM USERS WHERE username = '&#37;s' AND pwd = SHA1('%s')",array($username,$password)));	
	}

}


/*
* configuration
*/

define('DB_HOST','localhost');
define('DB_USER','user');
define('DB_PWD','pwd');
define('DB_NAME','db');

/*
* Initiation
*/

// begin session
session_start();

// instantiate database adpater
try {
	$db = new MySQL(DB_HOST,DB_USER,DB_PWD,DB_NAME);
} catch(Exception $e) {
	echo '<p>Unable to connect to database</p>';
	exit;
}

// Login Controller

// instantiate user DAO
$loginError = null;
$userDAO = new DAOUser($db);

// If login info has been posted attempt to locate user
if(isset($_POST['login'],$_POST['login']['username'],$_POST['login']['pass'])) {
	$user = $userDAO->fetchUserByNameAndPassword($_POST['login']['username'],$_POST['login']['pass']);
	// if user was found set session data
	if($user !== null) {
		$_SESSION['user'] = $user;
	} else {
		$loginError = true;
	}
} else if(isset($_POST['logout']) && isset($_SESSION['user'])) {
	unset($_SESSION['user']);
}

// set login template data
$login_error = $loginError;
$show_login = !isset($_SESSION['user']);
// create the  display
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
	<head>
		<meta http-equiv="content-type" content="text/html; charset=utf-8">
		<title>Simple Login Example</title>
	</head>
	<body>
		<div id="container">
			<?php if($show_login === true) { ?>
			<form name="login" method="POST" action="<?php $_SERVER['PHP_SELF']; ?>">
				<?php if($login_error === true) { ?>
					<p class="message error">Supplied username and password combination not found</p>
				<?php } ?>
				<fieldset>
					<legend>Login</legend>
					<ol>
						<li class="login-username">
							<label for="login-username">Username<span class="required">*</span></label>
							<input type="text" name="login[username]" id="login-username">
						</li>
						<li class="login-password">
							<label for="login-password">Password<span class="required">*</span></label>
							<input type="password" name="login[pass]" id="login-password">
						</li>
						<li class="login-submit">
							<input type="submit" name="login[login]" value="Login">
						</li>
					</ol>
				</fieldset>
			</form>
			<?php } else { ?>
			<form name="logout" method="POST" action="<?php $_SERVER['PHP_SELF']; ?>">
				<fieldset>
					<legend>Logout</legend>
					<input type="submit" name="logout" value="Logout">
				</fieldset>
			</form>
			<?php } ?>
		</div>
	</body>
</html>

Here is a half decent login system using oop

Will this code work? I was under the impression that any class declared as abstract needed all methods to be declared as abstract and not implemented in order to not throw an error in the parser.

I think your first problem here is that you’re still approaching this from a procedural mindset. You’re thinking in terms of raw functionality that you want and trying to organize those functions into classes. OOP is not about classes, and classes are not for simply grouping functions together in a common package. OOP about objects.

You need to be thinking instead about who and/or what needs to login. Most of the time that is a User, or a Service in the case of APIs. In other words, login is a function of a User. Objects should represent things that perform actions or can have actions performed on them. If you’re going to make a login class, it might as well just be procedural functions, because it’s not OOP.

I gave this presentation a long time ago on OOP at a local community conference:

Slides 11-14 touch on this very issue of what OOP is not, and even include a User class with login functions as an example, since it’s a common use case.

I agree. You’ve listed a set of tasks to perform…

Exactly, because it’s procedural PHP reimagined as OOP functions. The difference between procedural programming and OOP programming isn’t as great as a lot of people think; in a lot of cases good programming is just good programming. However, the problem your having is typical, and it’s solved by a mental change of perspective. Before we get too much into that, however, I do want to note some things that you’re doing well.

It’s nice that you have the inner guts of the functions hidden away, that accomplishes two major goals. One, hiding the implementation of the functions, this is called Information Hiding. Since the implementation is hidden, this code doesn’t know HOW those values are calculated. The huge advantage here is that this code won’t care if it changes. It didn’t know what the old implementation was and it won’t know the new one either. As long as it gets comprehensible output back from the function, you can change anything you want to. This makes your program much easier and more flexible to maintain.

Two, you’ve carved a piece out of this block of code. Instead of one huge block of code that’s going to be hard to read through, you have shorter pieces of code separated out; this is called Encapsulation and it’s the most important technique in programming. It’s used widely in procedural programming, but in some sense OOP is about taking Encapsulation to the absolute extreme. That’s because separating out a piece of code which performs one cohesive action makes it more sensible and easier to understand. It also makes the code reusable because this code can use the function and so can any other piece of code you write. This is also a huge maintenance advantage since now the code which performs a particular action is all consolidated in one place. Let’s say you change hosts and have to connect to a different database. Instead of changing connection strings and other code throughout the program, you can go to the function which handles database connections and make your changes in one place.

What you have is already much more readable than a collection of for loops and if-else decision making clauses. They’re still there, but because they’re encapsulated, you only have to deal with them one at a time. So before you think we’ve got to do everything over, remember that those are excellent things which you HAVE already done with this code.

Also, we want to keep making the function more readable, that’s good. And note how the functions you have do a good job of telling exactly what they’re doing, this is called ‘semantics’, the code (really the variable names, function names, etc.) tells you exactly what it is. The function which checks the inputs is named check_inputs(), the function which connects to the database is called connect(), it might even be better to name it connect_to_database(). Semantics means that you’re clearly identifying what everything is; this is one of the most important techniques to achieve the goal of self-documenting code.

Now, how to get around your problem of passing info from one function to the next. The problem here is that the function which does task1 is called first and then the function which calls task2 is called. Presumably the function which operates on any data returned from the database, task3 is then called. You can do this as you’ve noted by returning boolean values to indicate success or failure and then use if-else clauses to determine what to do next. However, part of the issue here is that these are sibling functions, they sit next to each other in a code block. You want to nest your functions based on the job that each is doing for you. Really, these are all part of one larger task, authenticating the user. That’s the function that you should be calling at the top level:

//code that grabs the username and password from the http headers
authenticateUser(inputUserName, inputPassword);

Now you write the function authenticateUser(inputUserName, inputPassword) function which look like…

$hashedPasswordFromDatabase = getHashedPassword(inputUserName);
$hashedPasswordFromUser = hashString(inputPassword);

if ($hashedPasswordFromDatabase == $hashedPasswordFromUser)
{
// log user in
}
else
{
// login is not valid, tell user to try again.
}

So you started by calling the first function, authenticateUser() which has additional functions inside it. Next you’ll write the getHashedPassword() function and the hashString() function. These may have other functions inside them. e.g. getHashedPassword() will probably involve calling the connectToDatabase() function to retrieve the password from the database. Then you’ll write the connectToDatabase() function in its turn… etc.

As you go through this process, you’ll find that you start to collect functions up the wazzu. It’s nice that all these functions are shorter and more readable than a huge block of procedural code, but if the goal is to be nice, readable, and manageable, you can see how we’re really only part way there. This is when you start grouping functions by purpose. In my code, I’ve gathered the hashString() function and the authenticateUser() function into Security class together. On the other hand getHashedPassword() is really about retrieving data from the database, so that is in one of my Data Access classes. Once they’re grouped into classes, you can start separating those out into files pretty easily.

Then in the page where you want to use this functionality you include a file something like, include.php. Inside this file is simply a series of include commands that pull in each file that’s part of the machinery to run your page. The idea here is that instead of littering your webpage with a bunch of include statements, you condense them all into one file. Now when you want to change the name of one of your files, you only have to change the name in include.php instead of on every webpage on your site.

And hopefully, that will keep you out of trouble for awhile. But I hope you can see how there are a lot of good things that you’re doing, and that you can continue to take advantage of those ideas and extend them to make your programming even easier to use and more maintainable.