5

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.

petesiss
  • 994
  • 13
  • 30
  • could you perhaps write a function/constructor for Foo that took a Bar instance and populated the Foo instance – gunnx May 22 '12 at 19:00
  • Why do you want to do this? It sounds like there is something insidious going on.. – Explosion Pills May 22 '12 at 19:01
  • I don't think you can cast custom objects in PHP. Perhaps you might find some information in [this post](http://stackoverflow.com/a/2232065/1028949) helpful. – jeffjenx May 22 '12 at 19:02
  • If a `Foo` should be able to construct itself from a `Bar`, then you should add this functionality into the `Foo` constructor. – goat May 22 '12 at 19:09
  • Thanks. The context is a third party lib that gives me an object, but its woefully short of useful methods. Sounds like there's not a good way to do it so directly. – petesiss May 22 '12 at 19:14

3 Answers3

2

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)
        )
    );
}
jnrbsn
  • 2,498
  • 1
  • 18
  • 25
  • Yeah... I wouldn't do that convertObject. I guess it didn't seem so magical to consider as in this case the properties of the base class which we have the object for are a suitable initiation state for the extending object.... but of course that's not always the case. I think I will just go for making Bar a property of Foo. – petesiss May 22 '12 at 21:12
1

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;
     }
}

Will
  • 24,082
  • 14
  • 97
  • 108
  • Thanks. Yeah realising it's not reasonable to expect to be able to do it as per my question, I think I'll do something along those lines. Probably drop the 'extends' and have Bar as a property of Foo. – petesiss May 22 '12 at 21:14
0

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.

Erutan409
  • 730
  • 10
  • 21