Page 1 of 1

Simple class hierarchy. Troubles with extend

Posted: Mon Jun 25, 2007 1:47 pm
by kawsper
Hi there,
I am fiddling a little with this code of mine.

I have the base class:

Code: Select all

abstract class UserDummy 
{
	function __construct($userID, $userEmail)
	{	
		
	}
}
And the user class

Code: Select all

class User extends UserDummy 
{
	private $db;
	private $userID;
	
	function __construct(mysqlconnector $db, $userID)
	{
		$this->db = new mysqlconnector;
		$this->userID = $userID;
	}
	function checkType()
	{
		return 'user';
	}
	
	function changeUserInformation($row, $information)
	{
		// Some checking
		$this->db->execute_query("UPDATE users SET $row = ".$this->db->quoteSmart($information)." WHERE id = '$this->userID'");
	}
	function ()
	{
		echo "lol";
	}
}
And then the class that makes the pain:

Code: Select all

class Admin extends User  
{
	private $db;
	private $userID;
	private $userEmail;
	
	function __construct(mysqlconnector $db, $userID, $userEmail)
	{
		$this->db = $db;
		$this->userID = $userID;
		$this->userName = $userEmail;
	}
	function checkType()
	{
		return 'admin';
	}
	
	function sendNewsletter($titel, $besked)
	{
		// Some code
	}
}
However, according to PHP.net:
A class can inherit methods and members of another class by using the extends keyword in the declaration. It is not possible to extend multiple classes, a class can only inherit one base class.

The inherited methods and members can be overridden, unless the parent class has defined a method as final, by redeclaring them within the same name defined in the parent class. It is possible to access the overridden methods or members by referencing them with parent::
http://dk2.php.net/manual/en/language.oop5.basic.php

So it should be possible to do.

I have a function called login() that determines which classobject (user, admin) to put inside a session.
Calling

Code: Select all

echo $_SESSION[s_user_object]->checkType();
Works on both classes.

However, the lol() function in User which Admin should have inherited doesn't work:

Code: Select all

echo $_SESSION[s_user_object]->lol();
It just outputs nothing.

What on earth am i doing wrong?

Posted: Mon Jun 25, 2007 2:15 pm
by superdezign
Firstly, you have a checkType() function. That's a waste of space. Use instanceof.

Code: Select all

if($userObj instanceof Admin)
{
    // ....
}
As for the other inheritance related things, whatever it is.. I can't see it. Are you sure you're including the User class' file, even when using the Admin object?

Posted: Mon Jun 25, 2007 2:24 pm
by kawsper
superdezign wrote:Firstly, you have a checkType() function. That's a waste of space. Use instanceof.

Code: Select all

if($userObj instanceof Admin)
{
    // ....
}
Thanks, much better :)
superdezign wrote: As for the other inheritance related things, whatever it is.. I can't see it. Are you sure you're including the User class' file, even when using the Admin object?
Yep, they are placed under the same file.

Userfactory.class.php

Code: Select all

<?php
abstract class UserDummy 
{
	function __construct($userID, $userEmail)
	{	
		
	}
}
class Guest extends UserDummy 
{
	private $db;
	private $userID;
	
	function __construct(mysqlconnector $db, $userID)
	{
		$this->db = $db;
		$this->userID = $userID;
	}
}
class User extends UserDummy 
{
	private $db;
	private $userID;
	
	function __construct(mysqlconnector $db, $userID)
	{
		$this->db = new mysqlconnector;
		$this->userID = $userID;
	}
	
	function changeUserInformation($row, $information)
	{
		// code
	}
	function ()
	{
		echo "lol";
	}
}
class Admin extends User  
{
	private $db;
	private $userID;
	private $userEmail;
	
	function __construct(mysqlconnector $db, $userID, $userEmail)
	{
		$this->db = $db;
		$this->userID = $userID;
		$this->userName = $userEmail;
	}
	function sendNewsletter($title, $message)
	{
		// some code
	}
}


class userFactory {
	private $db;
	private $userID;
	private $userEmail;
	private $userAdmin;
	
	function userFactory($userID, mysqlconnector $db)
	{
		$this->db = $db;
		if($userID == "0")
		{
			// The user is a guest with no login
			$this->userID = "0";
		} 
		else 
		{
			$user = $this->db->execute_query("SELECT COUNT(*) AS cnt FROM users WHERE id='$userID'");
			if($db->numRows() <= 0)
			{
				throw new Exception('The user doesn't exist.');
			}
			$user = $this->db->execute_query("SELECT email,admin FROM users WHERE id='$userID'");
			$user = $this->db->fetchRow(3);
	
			$this->userID = $userID;
			$this->userEmail = $user['email'];
			$this->userAdmin = $user['admin'];
		}
	}
	
	public function retur()
	{
		if($this->userAdmin == '1')
		{
			return new Admin($this->db, $this->userID, $this->userEmail);
		}
		elseif($this->userID == '0')
		{
			return new Guest($this->db, $this->userID);
		}
		else 
		{
			return new User($this->db, $this->userID, $this->userEmail);
		}
	}
}
?>
And a login function binds it together:

Code: Select all

function login($username, $password, mysqlconnector $db)
{
	$db->execute_query("SELECT id FROM users WHERE username = ".$db->quoteSmart($username)." AND password = '".md5($password)."' LIMIT 1");
	if($db->numRows() == '1')
	{
		$row = $db->fetchRow();
		$_SESSION['s_id'] = $row['id'];
		
		$usr = new userFactory($row[id],$db);
		$_SESSION['s_user_object'] = $usr->retur();
	}
	else 
	{
		throw new Exception("Login error");
	}
}
Test.api.php (which includes all the above files + mysqlconnector)

Code: Select all

error_reporting(E_STRICT); //Debugging ONLY!

require('classes/MysqlConnector.class.php');
require('classes/userFactory.class.php');
require('functions/login.function.php');

session_start();

$db = new mysqlconnector();

try {
	login('Kasper','apple',$db);				
	echo $_SESSION[s_user_object]->checkType();	// this worked before the function got removed
	
	echo $_SESSION[s_user_object]->lol(); // doesnt work for admins
} catch (Exception $e)
{
	echo"Fejl: ".$e->getMessage();
}
Hope that gave you a more bright view. Thanks for answering

Posted: Mon Jun 25, 2007 2:35 pm
by superdezign
So you know, inheritance allows inheritance of properties as well. Why do you override the properties in every extension? I looks to me that $db and $userID should be part of the dummy class (which should have get and set functions for them both), and $userEmail be exclusive to Admin. Then, you'd use the parent constructors to handle most of that work.

Also, saving objects in sessions will destroy your object. Objects only have the properties of their class if the class file gets loaded, but session variables are loaded before anything else, thus it'd lose it's tie to the class.

Serialize it into the session, then unserialize it out of the session when you need it (AFTER loading the class files).

Posted: Mon Jun 25, 2007 2:49 pm
by kawsper
Ah, now it is much cleaner, thanks. But i still don't understand the inheriting problem.

Is it because PHP can't extend a class that extends another class? It seems so for me. But writing a simple extender script its also works:

Code: Select all

class abc {
	function ()
	{
		echo"ABC";
	}
}
class def extends abc {

}
class ghi extends def {
	
}

$abc = new abc();
$abc->lol();

$def = new def();
$def->lol();

$ghi = new ghi();
$ghi->lol();
Prints: ABCABCABC

Posted: Mon Jun 25, 2007 2:52 pm
by superdezign
superdezign wrote:... saving objects in sessions will destroy your object. Objects only have the properties of their class if the class file gets loaded, but session variables are loaded before anything else, thus it'd lose it's tie to the class.

Serialize it into the session, then unserialize it out of the session when you need it (AFTER loading the class files).
You are getting your object out of a session, right?

Posted: Mon Jun 25, 2007 2:52 pm
by kawsper
Aha, found the error my UserDummy class was declared as an abstract.
I thought abstract classes worked like other, only that you couldn't use them as objects and they were there for others classes to extend from.

This doesn't work:

Code: Select all

abstract class abc {
	function ()
	{
		echo"ABC";
	}
}
class def extends abc {

}
class ghi extends def {
	
}

$abc = new abc();
$abc->lol();

$def = new def();
$def->lol();

$ghi = new ghi();
$ghi->lol();

Posted: Mon Jun 25, 2007 2:53 pm
by kawsper
superdezign wrote:
superdezign wrote:... saving objects in sessions will destroy your object. Objects only have the properties of their class if the class file gets loaded, but session variables are loaded before anything else, thus it'd lose it's tie to the class.

Serialize it into the session, then unserialize it out of the session when you need it (AFTER loading the class files).
Yeah, would look into serialization next, thanks for the help :)

Posted: Mon Jun 25, 2007 2:54 pm
by superdezign
... What? You can't use abstract classes...? What version of PHP are you using? That's strange.

Posted: Mon Jun 25, 2007 3:23 pm
by kawsper
I use PHP Version 5.2.2-pl1-gentoo does the above example works for you? Just name the smiley functions something else.

Posted: Mon Jun 25, 2007 3:53 pm
by superdezign
kawsper wrote:

Code: Select all

$abc = new abc();
$abc->Laughing();
You can't make an object out of an abstract... Do you have error_reporting turned off or something?

Posted: Mon Jun 25, 2007 4:39 pm
by kawsper
Hm.. I will try to look into my config later. My i am setting error reporting to max on my script.

Posted: Mon Jun 25, 2007 5:03 pm
by superdezign
You sure? Because if your error_reporting was at E_ALL, it'd have told you that you couldn't make an object out of an abstract.

Posted: Mon Jun 25, 2007 6:34 pm
by kawsper
Ah, caught the error error_reporting() was only set at E_Strict, not E_All, now it works. Thanks!