petesiss
petesiss

Reputation: 994

In PHP, create object of an extending class from an instance of the base class

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

Answers (3)

Erutan409
Erutan409

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

Will
Will

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

jnrbsn
jnrbsn

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

Related Questions