I am wondering if it is possible while using the keyword 'new' to return a reference to an existing object instead of a new object. And if not, what would be the best way to avoid co-existence of objects with the same parameters?
I have a class with arguments on the constructor. If I am using arguments similar to the ones of an existing instance, I'd like my new object to be a reference to the existing one. Is it possible? I understand it is different than a singleton as in this case we can have several instances of the same class, but not several instances using the same parameters.
class myClass{
public $signature; /* Unique signature for the connection */
public static $instances = array(); /* Array of references to connection */
private static function singleton($cfg,$inst){
$signature = sha1(serialize($cfg));
foreach ( self::$instances as $obj )
{
if ( $obj->signature == $signature )
return $obj;
}
array_push(self::$instances,$inst);
return true;
}
function __construct($myParam){
if ( is_object(self::singleton($myParam,$this) )
// Here I'd like $this to become the ref of my existing object
else
{
$this->signature = $myParam;
// I construct my new object
}
}
}
$e1 = new myClass(1);
$e2 = new myClass(2);
$e3 = new myClass(1); // $e3 should be a reference to $e1
First a few facts:
- The constructor can never return a value.
- $this cannot be reassigned (it could in PHP 4 but wasn't intended, and was undocumented)
Conclusion:
- Use factory methods or delegation.*
* There is a slight difference between the two, and that is that using delegation $a!=$b, but using factory methods $a === $b (see below)
Delegation example:
// Declare the delegate that will be wrapped by DelegationClass
// * Only public methods/properties will be accessible
class MyClass {
public $a;
public function a($a) {
echo "This is a(\$a=$a)\n";
}
}
class MyDelegationClass {
static protected $_delegation = 'MyClass'; // define the delegate
static protected $_i = array(); // instances
protected $_l = null; // delegation link
public function __construct($n) {
if (!array_key_exists($n,self::$_i)) {
// ensures that the instance is always created
self::$_i[$n] = new self::$_delegation;
}
$this->_l = self::$_i[$n]; // link to delegate
}
public function __get($v) {
return $this->_l->$v; // get property from delegate link
}
public function __set($k,$v) {
return $this->_l->$k = $v; // set property on delegate link
}
public function __call($f,$p) {
// call method on delegate link
return call_user_func_array(array($this->_l,$f),$p);
}
}
// $a != $b, but $a->a === $b->a
$a = new MyDelegationClass(1);
$b = new MyDelegationClass(1);
Factory class example:
class MyFactoryClass {
static protected $_i = array(); // instances
public static function newMyClass($n) {
if (!array_key_exists($n,self::$_i)) {
// ensures that the instance is always created
self::$_i[$n] = new MyClass;
}
return self::$_i[$n]; // return instance
}
}
// $a === $b
$a = MyFactoryClass::newMyClass(1);
$b = MyFactoryClass::newMyClass(1);
Wouldn't it just be easier to directly store the object references in your instances
var?
if (!isset(self::$instances[$myparam])) {
self::$instances[$myParam] = ... new object here ...;
} else
return &self::$instances[$myParam];
}
Just guessing, but it seems that storing your simple integer parameter as an array key to the instances cache would be "cheaper" than storing an sha1 signature of a serialized structure that could potentially NOT be the same even if the object has the same initial parameter.