-2

This is my test code:

class Base
{
    public $property;

    public function method() {
        return 456;
    }

    public function setProperty($value) {
        $this->property = $value;
    }
}

class Derived extends Base
{
    public function method() {
        return 789;
    }
}

$derived = new Derived;
$derived->setProperty(123);

$base = getParentInstance($derived);

echo $base->property; // Should print 123
echo $base->method(); // Should print 456

Looking around I found a lot of stuff related but nothing direct to the point. I just need to cast an instance to an ancestor class. Is this even possible on PHP?

Up to now I came out with this wrong code that (I think) just instantiate a new Base class:

function getParentInstance($object)
{
    $reflection = new ReflectionObject($object);
    $parent_class = $reflection->getParentClass();
    return $parent_class->newInstanceWithoutConstructor();
}

Another complication is I can only modify Derived... Base is provided by an external framework.

Addendum

Here is the proof what I'm requesting is not absurd. Same code in C++:

class Base
{
public:
    int property;

    int method() {
        return 456;
    }

    void setProperty(int value) {
        this->property = value;
    }
};

class Derived: public Base
{
public:
    int method() {
        return 789;
    }
};

#include <iostream>
int main()
{
    Derived *derived = new Derived;
    derived->setProperty(123);

    Base *base = (Base *) derived; // Is **this** possible in PHP?

    std::cout << base->property; // It prints 123
    std::cout << base->method(); // It prints 456

    return 0;
}
ntd
  • 7,372
  • 1
  • 27
  • 44
  • 2
    Why you need to cast a class to its parent? – sensorario Jun 07 '17 at 15:25
  • 1
    That's not how classes are supposed to be used. What problem are you trying to solve wit this? – tereško Jun 07 '17 at 15:26
  • 1
    There is no instance of the parent (Base) class for that child (Derived). When you create a class `Derived` it is not both Base and Derived being created JUST `Derived` with all of the `Base` stuff in it. So there is no way to get the `Base` class for the `Derived` class. More so what are you attempting to do that would require this? – nerdlyist Jun 07 '17 at 15:28
  • @nerdlyist If defined, `Base::__construct` would be called so I guess a `Base` instance should be present somewhere. – ntd Jun 07 '17 at 15:32
  • If you just want the class name you *could* add a static class member to `Base` like `protected static $className` and then in the constructor do `self::$className = __CLASS__` - you could then have something like `Derived::getParentClassName() { return parent::$className; }` – CD001 Jun 07 '17 at 15:36
  • `Base::__construct` execute code defined in the base **class** inherited by the child. There is no base **instance**. – Alex Blex Jun 07 '17 at 15:41
  • @ntd you are incorrect just because the `__construct()` is created does not mean it is called. You can easily test this `class Base{ function __construct() { echo "Test"; } } class Derived extends Base{ function __construct() { echo "proof"; } } $b = new Derived();` – nerdlyist Jun 07 '17 at 15:46
  • @AlexBlex Then how do you call `$this` in `Base::__construct`? – ntd Jun 07 '17 at 15:47
  • 1
    '$this' refers to current instance (the child). Please ensure you understand difference between instance and class. All inherited properties/methods results with a single instance of the child. – Alex Blex Jun 07 '17 at 15:51
  • @AlexBlex I know the difference between class and instance, thank you. It surprises me the fact that you (and some other) do not know that during instantiation every class in the hierarchy stores its data somewhere (in what I call "parent instance"). I'm just asking if it is *possible* to access that data in PHP, not if it exists. – ntd Jun 07 '17 at 16:12
  • @ntd we know where it is. It is on the `instance` of `Derived` that you have. You saying `Parent Instance` is assuming that there is another object being created for `Base` somewhere which is just not true. – nerdlyist Jun 07 '17 at 16:17
  • @nerdlyist Or, better, it is not accessible in PHP. An instance is nothing more than member data, and every class **must** have it. Maybe PHP implementation mixes that data making impossible to access only the member data of a specific class. In other languages the member data is stacked beginning from the base class, so you just cast. – ntd Jun 07 '17 at 16:38
  • @ntd *An instance is nothing more than member data* can you elaborate? What other langues let you do something like this? – nerdlyist Jun 07 '17 at 16:43
  • @nerdlyist Well, in `class Base { public $property; }` an instance is the value of `$property`. In `class Derived extends Base { public $again; }` an instance is the instance of `Base` and the value of `$again`. – ntd Jun 07 '17 at 16:52
  • If you want join me here. This is getting long and I am not sure I followed that but want to help http://chat.stackoverflow.com/rooms/146092/class-instances – nerdlyist Jun 07 '17 at 16:59

2 Answers2

1

As many mentioned, you shouldn't do it, and technically speaking you can't.

You can simulate the behaviour to get the results you expect as following:

class Derived extends Base
{
    protected $isProxy = false;

    public function method() {
        return $this->isProxy ? parent::method() : 'def';
    }

    /**
     * not really a parent. The same class, which behave like a parent in this specific case
     */    
    public function getParent()
    {
      $this->isProxy = true;
      $base = clone $this;
      $this->isProxy = false;
      return $base;
    }
}

$derived = new Derived;
$derived->setProperty(123);

$base = $derived->getParent();

echo $base->property; // prints 123
echo $base->method(); // prints 456

EDIT:

a simplified example of what else you can do:

function getParentInstance($child) {
  $base = new Base();
  $base->property = $child->property;
  return $base;
}

This works with public properties only. Private/Protected properties should be accessed/assigned using Reflection.

Alex Blex
  • 34,704
  • 7
  • 48
  • 75
  • I really don't see why all this fuss... in other languages this is just a cast. Anyway I cannot use your approach (and @calexandre one, which is similar... just a bit worse). What I provided is a minimal example. – ntd Jun 07 '17 at 15:50
  • Well, I'm afraid it is not the other language. You can try to play with Reflection to copy properties of a child to new instance of the base class, but it is quite slow, and hardly advisable. See my edit. – Alex Blex Jun 07 '17 at 15:55
  • Thank you for your effort. [This question](https://stackoverflow.com/questions/2226103/how-to-cast-objects-in-php) shows several ways of "casting" objects. I was just hoping that, being my classes on the same hierarchy, there was a simpler method. – ntd Jun 07 '17 at 16:25
0

Just figured I would put this here since it sort of answers the question. While there is not an instantiated Base object you still have access to parent data through php's parent.

I am not 100% sure what the attempt here is but you could use it like

//No changes to Base

class Derived extends Base {
    public function method($p=false) {//Adding a flag
        if($p){// true mean you want the parent method
            return parent::method(); //Use parent:: to call that method
        } else { //False do your own thing
            return 'def';
        }
    }
}

$derived = new Derived;

$derived->setProperty(123);

echo $derived->property; // Print 123

echo $derived->method(); // Prints def

echo $derived->method(false); // Prints def

echo $derived->method(true); // Prints 456

This is strange but one way to at least get the expectations.

Also, the easiest way is to not overwrite your parent method when parent and child function differently.

//No changes to Base

class Derived extends Base {
    public function child_method() {
        return 'def';
    }
}

$derived = new Derived;

$derived->setProperty(123);

echo $derived->property; // Print 123

echo $derived->method(); // Prints 456

echo $derived->child_method(false); // Prints def
nerdlyist
  • 2,842
  • 2
  • 20
  • 32