1
class Foo {
    protected static $a = 1;
    protected $b = 2;

    public function func() { return 'foo' . static::$a . $this->b; }
}
class Bar extends Foo {
    protected static $a = 3;
    protected $b = 4;

    public function func() { return 'bar' . static::$a . $this->b; }
}

$obj = new Bar();
$obj->func(); // returns of course 'bar34'

Is there any option in PHP to call func() from Foo class? In C++ I would cast $obj to Foo and simply call func()

Bar* obj = new Bar();
Foo* obj2 = (Bar*) obj;
obj2->func(); // returns 'foo14';
l00k
  • 1,525
  • 1
  • 19
  • 29
  • 1
    More or less the same question as https://stackoverflow.com/questions/16471076/cast-derived-class-to-base-class-in-php (in short, no) – iainn May 22 '18 at 13:33
  • @iainn best answer - if really there is no build-in solution – l00k May 22 '18 at 13:38

4 Answers4

2

If you want to get down and dirty with Reflection then it's possible, but I'd strongly argue that this shouldn't be used anywhere near any production code. If you've got an instance of a child class, then you've got it for a reason, and if it's overridden a parent method then that has also happened for a reason.

Assuming you already know all this, then with that disclaimer out of the way, this should work in any remotely recent version of PHP:

class Foo { public function func() { echo 'I am the parent'; } }
class Bar extends Foo { public function func() { echo 'I am the child'; } }

// Create instance of child class
$bar = new Bar;

// Create reflection class
$reflected = new ReflectionClass(get_class($bar));

// Get parent method
$method = $reflected->getParentClass()->getMethod('func');

// Invoke method on child object
$method->invokeArgs($bar, []);

// I am the parent

See https://3v4l.org/NP6j8

iainn
  • 16,826
  • 9
  • 33
  • 40
  • That is it. Thank you. I guessed there is some solution with reflections. I have to agree that it is little dirty however less dirty than copying values to parent class object. – l00k May 22 '18 at 15:09
1

This isn't exactly what I'd call pretty, but it works and is relatively similar to what you described for C++; It works by calling get_parent_class() and then abusing PHP's ability to create objects from strings.

<?php

class Foo {
    public function func() { echo 'foo'; }
}
class Bar extends Foo {
    public function func() { echo 'bar'; }
}

$obj = new Bar();
$obj->func(); // Prints 'bar'

$parentClassString = get_parent_class($obj);
$newObj = new $parentClassString; // Gotta love PHP for magic like this
$newObj->func(); // Prints 'foo'

See this snippet to see it in action.

EDIT

It's a lot of work, but you could use so called Late Static Binding, perhaps more clearly explained in Jokerius's answer here. This requires you to write a crapload of custom code though, which I don't think is preferential. Overall the short answer seems to be: it isn't really possible.

Loek
  • 4,037
  • 19
  • 35
  • I have posted simpliest code I can imagine. In real code I have a lot of fields (also static). I can copy values however it is not pretty nice. – l00k May 22 '18 at 13:22
  • 1
    Answer is good, however question is probably not enough precise. – l00k May 22 '18 at 13:26
  • To my surprise, my answer works amazingly well. https://3v4l.org/VdWVE The other answers here are probably better though, especially leigh Bicknell's. – Loek May 22 '18 at 13:32
  • Not really :( should return "foo14" - member field should be preserved – l00k May 22 '18 at 13:36
  • Yeah I think that's just how PHP works. I'll take another minute to look at it, but the internet seems to agree on the fact that you just can't do this simply. – Loek May 22 '18 at 13:37
  • 1
    Updated answer. Seems pretty hard to do this. – Loek May 22 '18 at 13:46
1

This to me looks like a design issue more than anything else. However if I were to handle this in a way that were easily readable and without rethinking my design I would do:

<?php
class Foo {
    public function func() { return 'foo'; }
}
class Bar extends Foo {
    public function func() { return 'bar'; }
    public function parentFunc() { return parent::func(); }
}

$obj = new Bar();
$obj->parentFunc(); // returns of course 'foo'

Loek's answer also works, but doesn't call the method on the objects parent. It just calls the method on the classes parent. It all depends on the functionality you are looking for.

You could also do something like:

<?php
class Foo {
    public function func() { return 'foo'; }
}
class Bar extends Foo {
    public function func($parent = false) { 
        if ($parent) {
            return parent::func();
        }
        return 'bar';
    }
}

$obj = new Bar();
$obj->func(true); // returns of course 'foo'

Which is similar but without the need for the extra method.

Personally though I feel this issue likely requires a rethink in code design more than a coding solution.

-- edit --

To elaborate on 'a rethink in code design', I would ask myself "Why do I need an object that has two methods with the same name, but different functionalities? Is this not a job for two different objects? Trace the issue backwards until you find the design issue. Or the point at which the decision needs to be made as to which object your framework requires.

Leigh Bicknell
  • 854
  • 9
  • 19
  • 1. Like I commented @davithuroyan answer: Yeah, I know that possibility. However I wonder is there something like class casting or some usability with reflection classes. 2. Second solution is not good becouse Base class is framework Core class and I cant change parameters list. – l00k May 22 '18 at 13:34
  • Comment to edit section: In C++ it is possible. I we say that language defines our design possiblities then I agree. I'm not so fluent in english to elaborate why I need this :) – l00k May 22 '18 at 13:46
  • Possible, and sensible are two very very different things in the programming world. This is the type of thing i'd resort to if I had very little time and was given a 'just get it done' request. Even then I'd do it with a mind of refactoring it later. Language features can limit our applications design possibilities, but they do not define the best/most sensible design. – Leigh Bicknell May 22 '18 at 13:52
0

I don't know should it help you but try to add this function in Bar class

public function callParent($function){
    return parent::$function();
}

and call

echo $obj->callParent("func");

[UPDATED] Also you can write cast function yourself something like this

public function castAs($newClass) {
    $obj = new $newClass;
    foreach (get_object_vars($this) as $key => $name) {
        $obj->$key = $name;
    }
    return $obj;
}
Davit Huroyan
  • 302
  • 4
  • 16
  • Yeah, I know that possibility. However I wonder is there something like class casting or some usability with reflection classes. – l00k May 22 '18 at 13:32
  • 1
    no there is no class casting as I know but beside of this you can write cast function yourself see updated answer – Davit Huroyan May 22 '18 at 13:33