What would be a good way (along with any pros and cons) of initializing an instance of a PHP class with another object of the same class (ideally in PHP 4.x)?
Here in initialize()
is essentially what I'd like to be able to do (example is extremely simplified from my use-case, see below):
$product = new Product('Widget');
$product2 = new Product('Widget #2');
$product->initialize($product2);
echo $product->name; // echos "Widget #2"
class Product {
var $name;
function __constructor($name) {
$this->name = $name;
}
function initialize($product) {
// I know this cannot be done this way in PHP.
// What are the alternatives and their pros & cons?
$this = $product;
}
}
I know this may not be "good programming practice"; with 20+ years programming experience on other languages I know a bit about what's good and what's not. So hopefully we won't get hung up on if doing this makes sense or not. I have a use-case working with some open-source code that I can't change so please just bear with me on my need for it. I'm actually trying to create an OOP wrapper around some really ugly array code buried deep in the core of WordPress.
I'm trying to write it so in future versions they can move away from the ugly array-based code because everyone will be using the new API that otherwise fully encapsulated these nasty arrays. But to make it work elegantly I need to be able to do the above (in PHP 4.x) and I don't want to write code that just copies the properties.
Thanks in advance for your help.
UPDATE
Many of you are suggesting clone
but unless I misunderstand that doesn't address the question. clone
makes a copy; that's not the crux of the question. I'm instead trying to get the constructed object to "become" the object passed in. At this point I'm assuming there isn't a way to do that based on the fact that 0 out of 5 answers have suggested anything but I'll wait a bit longer before selecting a best in case it开发者_Go百科 was simply that my questions was unclear.
In PHP 5, object cloning might be more relevant:
http://php.net/manual/en/language.oop5.cloning.php
You can define a special __clone
method.
In PHP 4 and 5, you can copy properties via:
function copy($obj)
{
foreach (get_object_vars($obj) as $key => $val)
{
$this->$key = $val;
}
}
However, you wrote "I don't want to write code that just copies the properties," and I'm not exactly sure what you mean by that.
Preferred way of doing this is to use clone
keyword and to implement appropriate __clone()
method if needed as mentioned by other posters. Another trick way of doing this (con: slow, pros: can be stored, sent over network and works identical in php4/5) is to serialize an object and then unserialize to create new copies of it with identical variable values.
Example:
$productCopy = unserialize(serialize($product));
EDIT: Sorry, misunderstood what you were asking for. You will have to initialize variables of the object being constructed with passed in object's variables inside of the constructor. You can't return a reference to another object from the constructor.
Example:
public function __construct($name, $object = null) {
if($object) {
foreach(get_object_vars($object) as $k => $v) {
$this->$k = $v;
}
} else {
$this->name = $name;
}
}
class Product {
var $name;
function __construct($value) {
if (is_a($value, 'Product')) {
$this->name = $value->name;
} else {
$this->name = $value;
}
}
}
Similarly, you can use instanceof
instead of is_a
if you prefer (depending on your PHP version).
Now you can pass a Product instance OR a name to the construct.
$product = new Product('Something');
$clone = new Product($product);
This is the best way of doing it so far:
http://www.blrf.net/howto/51_PHP__How_to_control_object_instances_in_PHP_.html
Don't use "new", instead use a static function that returns the instance you want.
I have done the following:
class MyClass {
private static $_instances;
public static function get($id) {
if (!self::$_instances) self::$_instances = Array();
$class = get_called_class();
if (!array_key_exists($class, self::$_instances)) self::$_instances[$class] = Array();
if (!is_numeric($id) || $id == '') throw new Exception('Cannot instantiate a non-numeric ID.');
if (array_key_exists($id, self::$_instances[$class])) return self::$_instances[$class][$id];
else {
self::$_instances[$class][$id] = new static($id);
return self::$_instances[$class][$id];
}
}
function __construct($id=false) {
// new instance code...
// I use $id=false to create new a db table row not load an old one
}
}
Usage:
// New instance
$a = new MyClass();
$a = new MyClass;
// MyClass with $id = 1
$b = MyClass::get(1);
$c = MyClass::get(1);
$d = new MyClass(1);
$b and $c point to the same object, while $d is a new one.
Caveats:
Garbage collection will no longer apply as your instances are stored in a static array
You'll have to change your code to use MyClass::get
Notes in my code:
New instances are called with "new static" instead of "new self" to use late static bindings.
You can set your constructor to private. This will break all your old code if you use "new", but will ensure you don't get double instances or more. You'll have to change a bit in the get function's arguments and code to allow $id=false or $id=-1 or whatever.
maybe
$product = new Product('Widget');
$product2 = new Product(null, $product);
echo $product2->name; // echos "Widget #2"
class Product {
var $name;
function __constructor($name, $product = null) {
$this->name = !empty($name) ? $name : $product->name;
}
}
Adding another answer due to it being radically different.
$product = new Product('Widget');
$product2 = new Product('Widget #2');
$product =& $product2;
echo $product->name; // echos "Widget #2"
That should work.
精彩评论