Find previous instance of class

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

User avatar
shiznatix
DevNet Master
Posts: 2745
Joined: Tue Dec 28, 2004 5:57 pm
Location: Tallinn, Estonia
Contact:

Find previous instance of class

Post by shiznatix »

Ok I have a class (class A) that is loaded and a method executed. The I have another class (class B) that is loaded and a method executed.

In that class B I 'reaload' class A.

Now the problem is that now all the class variables from class A are now back to default. How can I see if ANYWHERE there is an instance of class A and then return a reference to that instance so when I use class A inside class B all the variables are still there.

small example:

Code: Select all

public function ViewResults()
    {
        $SR = $this->LoadClass('Search', 'Controller');
        //i don't want to have to run $SR->Search() again as it has already been done
        //but if i don't then $SR->Results will just be a empty string
        //how can I have had $SR return the previous instance of that class
        $SR->Search();

        $Show = '';

        foreach ($SR->Results as $Result)
        {
            $Show .= $Result['Name'].'<br>';
        }

        return $Show;
    }
the LoadClass method is simply this:

Code: Select all

public function LoadClass($class, $level)
    {
        global $Config;

        require_once $Config[$class]['Path'].$class.'.'.$level.'.class.php';

        $ClassName = $class.$level;

        $tmp = new $ClassName;

        return $tmp;
    }
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

How can I see if ANYWHERE there is an instance of class A and then return a reference to that instance so when I use class A inside class B all the variables are still there.
Hmmm.... sounds like you need a singleton ;)

However, to answer your question the way you make it sound, a combination of get_defined_vars(), foreach() and instanceof could work.... a singleton would be so much better though.

Code: Select all

class mySingleton
{
    static private $instance = null;
    
    private function __construct()
    {
        //NOTE this is "private"
    }
    
    static public function getInstance()
    {
        //Create an instance, internally, only if not already there
        if (self::$instance === null) self::$instance = new mySingleton();

        return self::$instance;
    }
}

$obj = mySingleton::getInstance();
You just use the getInstance() method to get your object EVERYWHERE rather than using the "new" keyword. Works a peach.

EDIT | Corrected missing "function" keyword on getInstance()
User avatar
shiznatix
DevNet Master
Posts: 2745
Joined: Tue Dec 28, 2004 5:57 pm
Location: Tallinn, Estonia
Contact:

Post by shiznatix »

ok trying to understand here.

If I want to use the singleton way then I would have to put a singleton function in every class that I would want only 1 instance of, correct? Is there a way to automate this so I don't have to put that function in every class? Every class that would need this already extends the 'Base' class so is there any way to put this logic in the Base class?

I took a stab at get_declared_classes() but that just returns strings, not objects :( .

after thinking about it I thought something like this might work (this is incorrect syntax but the idea is there):

Code: Select all

//put this in the Base class
static public function Singleton($ClassName)
    {
        if (null === $this->Instance)
        {
            $this->Instance = new $ClassName;
        }

        return $this->Instance;
    }
but the problem comes with I do this:

Code: Select all

$UIClass = $Page.'View';

$this->UI = $UIClass->Singleton($UIClass);
//can't do $UIClass::Singleton because it gives me a parse error
it thinks that $UIClass is a object (of course) and not like just some text. so it fails.

I even went so far as to try:

Code: Select all

define('TMP_CLASSNAME', $UIClass);

$this->UI = TMP_CLASSNAME::Singleton($UIClass);
to no avail.


Maybe variable variables would work somehow? Any other ideas?
User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Post by Ollie Saunders »

Just thought I'd give my two cents:
You need a singleton here or failing that make use of some static properties to achieve what you desire.

Code: Select all

class A
{
    static private $instance = NULL;
    static function getInstance()
    {
        if (self::$instance === NULL) {
            self::$instance = new Logger();
        }
        return self::$instance;
    }
    private function __construct()
    {
    }
    private function __clone()
    {
    }
}

$a = A::getInstance();
jmut
Forum Regular
Posts: 945
Joined: Tue Jul 05, 2005 3:54 am
Location: Sofia, Bulgaria
Contact:

Re: Find previous instance of class

Post by jmut »

shiznatix wrote:Ok I have a class (class A) that is loaded and a method executed. The I have another class (class B) that is loaded and a method executed.

In that class B I 'reaload' class A.

Now the problem is that now all the class variables from class A are now back to default. How can I see if ANYWHERE there is an instance of class A and then return a reference to that instance so when I use class A inside class B all the variables are still there.

small example:

Code: Select all

public function ViewResults()
    {
        $SR = $this->LoadClass('Search', 'Controller');
        //i don't want to have to run $SR->Search() again as it has already been done
        //but if i don't then $SR->Results will just be a empty string
        //how can I have had $SR return the previous instance of that class
        $SR->Search();

        $Show = '';

        foreach ($SR->Results as $Result)
        {
            $Show .= $Result['Name'].'<br>';
        }

        return $Show;
    }
the LoadClass method is simply this:

Code: Select all

public function LoadClass($class, $level)
    {
        global $Config;

        require_once $Config[$class]['Path'].$class.'.'.$level.'.class.php';

        $ClassName = $class.$level;

        $tmp = new $ClassName;

        return $tmp;
    }











This is by no means the way you should do it.
Just ideas. Problem with what I wrote is....does not check if same instance only relys on object with same name.
Hence if you build more objects of same class and want to use them all as separate this will not work (will return first build each time).

Code is not tested. Hope it lead you in right direction.

Code: Select all

public function LoadClass($class, $level)
    {
        global $Config;

        require_once $Config[$class]['Path'].$class.'.'.$level.'.class.php';

        $ClassName = $class.$level;



       
        $obj = getObject($ClassName);
        if ($obj) {
              return $obj;
        } else {
            $tmp = new $ClassName;
            ObjectStorage::addObject($ClassName,$tmp);
            return $tmp;
        }
    }

class ObjectStorage
{
    static private $object = array();

    static public function addObject($name,$object) {
        if (!is_object($object)) {
            throw new Exception("This is not an object.".gettype($object)." provided");
        }

        self::$object[$name] = $object;
    }

    static public function getObject($name) {
        return self::$object[$name];
    }
}
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

~Shiznatix. Perhaps a factory class which caches instances itself. Sounds a little ad-hoc and I'm just improvising here but.

Ohh..... This is nice. I just made a singleton factory. Albeit a basic one :P

Code: Select all

<?php

class one
{
	public $foo = 42;
}

class factory
{
	static private $instances = array();
	
	static public function create($objectName)
	{
		foreach (self::$instances as $key => $instance)
		{
			if ($instance instanceof $objectName)
			{
				echo 'Yes'; //Ermm you might wanna delete this... I was debugging
				return self::$instances[$key];
			}
		}
		return self::$instances[] = new $objectName();
	}
}

$obj1 = factory::create('one');

var_dump($obj1);

$obj1->foo = 10;

$obj2 = factory::create('one');

var_dump($obj2);

?>
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

I'm moving this to Theory and Design because I belive it's not your average coding question and is, basically more theory based.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

I think the biggest question is why you are not passing the search object in? That would be preferable.

Code: Select all

public function ViewResults($SR)
    {
        $Show = '';

        foreach ($SR->Results as $Result)
        {
            $Show .= $Result['Name'].'<br>';
        }

        return $Show;
    }
Otherwise, you have a couple of options. You can make your Search a Singleton or you can use some kind of Factory/Registry to hold the instances. The latter is prefered for reasons of long term flexiblity and testablity, the former is simpler in the short term.

Take a look at the Service Locator thread and grab a minimal version of what Ninja is working on.
(#10850)
User avatar
shiznatix
DevNet Master
Posts: 2745
Joined: Tue Dec 28, 2004 5:57 pm
Location: Tallinn, Estonia
Contact:

Post by shiznatix »

d11wtq: i debated with myself as to where to put this, theory or code forums, guess we all make mistakes :P

YES I GOT IT WORKING!

jmut basically handed me the idea. d11, you just remade the idea a bit. arborint, perfectly legal but goes against how i am (trying) to design this. than you all for the help.

here is what I ended up with. if this can be improved please tell me.

objects class:

Code: Select all

<?php

class ObjectStorage
{
    private $objects;

    public function AddObject($name, $object)
    {
        if (!is_object($object))
        {
            die("This is not an object.".gettype($object)." provided");
        }

        $this->objects[$name] = $object;
    }

    public function GetObject($name)
    {
        if (isset($this->objects[$name]))
        {
            return $this->objects[$name];
        }
        else
        {
            return false;
        }
    }
}

$ObjectStorage = new ObjectStorage;

?>
method in base class:

Code: Select all

public function LoadClass($class, $level)
{
    global $Config;
    global $ObjectStorage;

    require_once $Config[$class]['Path'].$class.'.'.$level.'.class.php';

    $ClassName = $class.$level;

    $obj = $ObjectStorage->GetObject($ClassName);

    if (false !== $obj)
    {
        return $obj;
    }
    else
    {
        $obj = new $ClassName;
        $ObjectStorage->AddObject($ClassName, $obj);
        return $obj;
    }
}
also, why bother with static variables? whats the difference between self:: and $this->? anything wrong with how im doing it?
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

Looks good. It's a registry ;)

I just worked a little further on what I did before but now I come to think of it, it's crap. I should just have made a registry. :lol:

Code: Select all

<?php

class one
{
	public $foo = 42;
	
	public function __construct($foo=null)
	{
		$this->foo = $foo;
	}
}

class Singleton
{
	static private $instances = array();
	
	static public function Fetch($className, $args=null)
	{
		if (isset(self::$instances[$className]))
		{
			return self::$instances[$className];
		}
		
		if ($args !== null)
		{
			return self::$instances[$className] = eval('return new '.$className.'('.$args.');');
		}
		else return self::$instances[$className] = new $className();
	}
}

$obj1 = Singleton::Fetch('one', '13');

var_dump($obj1);

$obj1->foo = 10;

$obj2 = Singleton::Fetch('one');

var_dump($obj2);

?>
User avatar
shiznatix
DevNet Master
Posts: 2745
Joined: Tue Dec 28, 2004 5:57 pm
Location: Tallinn, Estonia
Contact:

Post by shiznatix »

nice, i like the ability to pass arguments to the class with it. thank you guys so much.

should i start a new thread about my self::, $this->, static..., questions?
jmut
Forum Regular
Posts: 945
Joined: Tue Jul 05, 2005 3:54 am
Location: Sofia, Bulgaria
Contact:

Post by jmut »

shiznatix wrote:nice, i like the ability to pass arguments to the class with it. thank you guys so much.

should i start a new thread about my self::, $this->, static..., questions?

...............
also, why bother with static variables? whats the difference between self:: and $this->? anything wrong with how im doing it?
Well there is nothing wrong the way you did it but...
This is basically design think nothing more. The way you did it you actually allow for the user of this class to make several instances.
But what you actually need is on class. Static class to store data.

so what you need is not actually building an object from the class but rather use the class as a container.
In this case declaring __construct() private and making public static other variables.

This way you express your intentions not only in mind but in code. People will use this class as store and no more. Will not build objects from it (as this may lead to disaster and that is not the purpose of the class - you prevent them from misusing the class)
User avatar
shiznatix
DevNet Master
Posts: 2745
Joined: Tue Dec 28, 2004 5:57 pm
Location: Tallinn, Estonia
Contact:

Post by shiznatix »

jmut wrote:Well there is nothing wrong the way you did it but...
This is basically design think nothing more. The way you did it you actually allow for the user of this class to make several instances.
But what you actually need is on class. Static class to store data.

so what you need is not actually building an object from the class but rather use the class as a container.
In this case declaring __construct() private and making public static other variables.

This way you express your intentions not only in mind but in code. People will use this class as store and no more. Will not build objects from it (as this may lead to disaster and that is not the purpose of the class - you prevent them from misusing the class)
i think i get it. the ObjectStorage class is a box (per say). I don't want to be like 'Box do this!' or 'Box all of a sudden spawn other boxes!' because the box doesn't do anything, it just holds what i want. BUT! I want to be able to reach into the box and grab what I have previously put into it, thus making it 'static' and private contruct() and whatnot. Is this a good annalogy?

im somewhat new to the OOP field and I am trying to move into it 100% so this is a good lesson. i will move this class to be a container and not and object. thanks for the clarification (if im correct).
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Post by Christopher »

Usually when I am trying to force something to work like this, when I look around a little I find that I did not know a common design concept and that my problem is a symptom of a deeper design problem.
(#10850)
User avatar
shiznatix
DevNet Master
Posts: 2745
Joined: Tue Dec 28, 2004 5:57 pm
Location: Tallinn, Estonia
Contact:

Post by shiznatix »

arborint wrote:Usually when I am trying to force something to work like this, when I look around a little I find that I did not know a common design concept and that my problem is a symptom of a deeper design problem.
i thought that would be the case but i think my design is good and flexable enough to be able to change without much problem. *so far* my design is solid.
Post Reply