Simple class hierarchy. Troubles with extend

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

Post Reply
kawsper
Forum Newbie
Posts: 11
Joined: Sun Jul 10, 2005 12:21 pm

Simple class hierarchy. Troubles with extend

Post 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?
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Post 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?
kawsper
Forum Newbie
Posts: 11
Joined: Sun Jul 10, 2005 12:21 pm

Post 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
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Post 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).
kawsper
Forum Newbie
Posts: 11
Joined: Sun Jul 10, 2005 12:21 pm

Post 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
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Post 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?
Last edited by superdezign on Mon Jun 25, 2007 2:52 pm, edited 1 time in total.
kawsper
Forum Newbie
Posts: 11
Joined: Sun Jul 10, 2005 12:21 pm

Post 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();
kawsper
Forum Newbie
Posts: 11
Joined: Sun Jul 10, 2005 12:21 pm

Post 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 :)
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Post by superdezign »

... What? You can't use abstract classes...? What version of PHP are you using? That's strange.
kawsper
Forum Newbie
Posts: 11
Joined: Sun Jul 10, 2005 12:21 pm

Post 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.
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Post 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?
kawsper
Forum Newbie
Posts: 11
Joined: Sun Jul 10, 2005 12:21 pm

Post by kawsper »

Hm.. I will try to look into my config later. My i am setting error reporting to max on my script.
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Post 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.
kawsper
Forum Newbie
Posts: 11
Joined: Sun Jul 10, 2005 12:21 pm

Post by kawsper »

Ah, caught the error error_reporting() was only set at E_Strict, not E_All, now it works. Thanks!
Post Reply