Reputation: 994
If I have a class Foo which extends class Bar, and an instance of Bar, is there anyway to use that instance of Bar to 'populate' a new instance of Foo?
Essentially, I am only able to retrieve Bar, but I want to use Foo in my code as it gives me lots of additional methods.
I saw quite a few solutions come up to similar questions but they all seemed to be python or c#.
I cant use ReflectionClass::newInstanceArgs as I cant access the data that went in to the constructor of Bar to create it in the first place.
Upvotes: 5
Views: 4995
Reputation: 752
I just wrote something this example is based on for extending custom drivers that may need additional support package(s) for the aforementioned core driver file.
// File: parent.php
class parent_class() {
protected $_protected_object; // set in constructor
protected $_protected_var = "Something dynamic";
public function __construct() {
// keep in mind that if you don't override this constructor, this will execute when extended
}
public function create_resource() {
$this->_protected_object = new SomeObjectInstance();
}
public function child_class() {
static $child = null;
if (is_null($child)) {
$file = "child.php";
if (!\file_exists($file)) {
throw new Exception("Couldn't load '$file', doesn't exist");
} else {
require_once($file);
$child = new child_class();
$child->__overload("_protected_object", $this->_protected_object)->__overload("_protected_var", $this->_protected_var);
}
}
return $child;
}
}
// File: child.php
class child_class extends parent_class {
protected function __overload($index, &$value) {
$this->$index =& $value;
return $this;
}
public function __construct() {
// REMEMBER: if you don't declare this method, the parent's constructor will execute
}
public function extended_func() {
// code
}
public function etc() {
// code
}
}
// Code instantiating objects:
$parent = new parent_class();
$parent->create_resource();
var_dump($parent);
/**
* Would output something like this:
*
* object(parent_class)#1 (2) {
* ["_protected_object":protected]=>
* object(SomeObjectInstance)#2 (*) {
* %OBJECT PROPERTIES%
* }
* ["_protected_var":protected]=>
* string(17) "Something dynamic"
* }
*/
$child = $parent->child_class();
var_dump($child);
/**
* Would output something like this:
*
* object(child_class)#3 (2) {
* ["_protected_object":protected]=>
* &object(SomeObjectInstance)#2 (*) {
* %OBJECT PROPERTIES%
* }
* ["_protected_var":protected]=>
* &string(17) "Something dynamic"
* }
*/
Notice that the second var_dump() for $child outputs the same information as $parent, except that you can now see said properties are preceded with ampersands (&), denoting that there are now references. So, if you change anything in the parent class instance in regard to those two properties, it will be reflected in the child class instance.
Upvotes: 0
Reputation: 24739
There is no built-in way to easily do what you want to. The interfaces of these classes must be redesigned a bit. Perhaps something like:
<?php
class Bar
{
...
}
class Foo extends Bar
{
public static function fromBar(Bar $bar)
{
$foo = new self();
... (copy data here) ...
return $foo;
}
}
Upvotes: 1
Reputation: 2533
The recommended way to accomplish this would be through dependency injection. Your constructor for Foo
could accept an instance of Bar
, and then you'd have to write the code to load the state of the new Foo
object from the Bar
object.
If PHP had a feature that does exactly what you describe, it would be problematic, because even if Foo
extends Bar
, the two classes could still be very different. I don't see how PHP could be smart enough to know how to automagically turn an instance of one class into an instance of another class.
With that said, under the correct circumstances, the (very hacky) "solution" below could do what you describe. I would not recommend actually using it. I mainly just wanted to show that you'd have to resort to some weird stuff to do this.
function convertObject($object, $newClass)
{
return unserialize(
preg_replace(
'/^O\:\d+\:"[^"]+"/',
'O:'.strlen($newClass).':"'.$newClass.'"',
serialize($object)
)
);
}
Upvotes: 2