Page 1 of 2
Find previous instance of class
Posted: Fri Jul 21, 2006 7:30 am
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;
}
Posted: Fri Jul 21, 2006 7:41 am
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()
Posted: Fri Jul 21, 2006 8:38 am
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?
Posted: Fri Jul 21, 2006 9:37 am
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();
Re: Find previous instance of class
Posted: Fri Jul 21, 2006 9:38 am
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];
}
}
Posted: Fri Jul 21, 2006 10:25 am
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
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);
?>
Posted: Fri Jul 21, 2006 10:41 am
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.
Posted: Fri Jul 21, 2006 10:56 am
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.
Posted: Fri Jul 21, 2006 11:03 am
by shiznatix
d11wtq: i debated with myself as to where to put this, theory or code forums, guess we all make mistakes
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?
Posted: Fri Jul 21, 2006 11:07 am
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.
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);
?>
Posted: Fri Jul 21, 2006 11:22 am
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?
Posted: Fri Jul 21, 2006 11:41 am
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)
Posted: Fri Jul 21, 2006 12:52 pm
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).
Posted: Fri Jul 21, 2006 1:31 pm
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.
Posted: Fri Jul 21, 2006 2:38 pm
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.