views:

134

answers:

3

If I have this code, the string "test" is echoed. This is in PHP 5.3. Is this some oversight that shouldn't be relied on, or is it some way of achieving multiple inheritence in PHP?

class Test1
{
    function getName()
    {
        return $this->name;
    }
}

class Test2
{
    public $name = 'test';

    function getName()
    {
        return Test1::getName();
    }
}

$test = new Test2;
echo $test->getName();

EDIT:

As has been pointed out the comments by GZipp this is actually documented behaviour. See this page: http://us2.php.net/manual/en/language.oop5.basic.php and the heading "Example #2 Some examples of the $this pseudo-variable".

Classes A and B have a similar relationship to my two test classes above and the lines

$b = new B();
$b->bar();

Show more or less the same result as my example.

A: 

Just to be clear - this isn't inheritance. Test2 does not extend Test1. You statically referenced a public method of the Test1 class.

Nonetheless, the fact that it returns 'test' is interesting to say the least. And I see where it is giving off the idea of inheritance.

If you can't find a decent answer, I'd submit your code as a bug.

UPDATE

It looks like under the hood, even though you statically referenced a method of Test1, it's still being called as Test2. In the end, this is undefined behavior, and ask noted above does throw a strict warning. Still very odd, and I personally agree that it shouldn't work. But just to shed a little more insight which object it is using.

class Test1 {
    function getName() {
        echo get_class($this);
        return $this->name;
    }
}
// ...
$test = new Test2;
echo $test->getName();

// echoes 'Test2 test'
Jason McCreary
`$this` in `Test1::getName` is an object of class `Test2`... Which shouldn't be possible since neither class extends the other. I'm leaning on the bug standpoint, but it appears it has existed for a while (since at least 5.2.2, the oldest test install I have)...
ircmaxell
Well no it's not inheritence. But it seems to provide some method of allowing horizontial code reuse in the kind of way that traits would, but more limited. I hope they don't "fix" it because it could occasionally be useful
Gnuffo1
Use the heck out of it if you have a need. It's definitely interesting. Just understand that the general consensus so far is that it's a bug. Which could some day be fixed.
Jason McCreary
A: 

This has to be a bug. 100%.

  1. This is not inheritance.
  2. You should not be able to call Test1::getName() as getName() in Test1 is not static
  3. Even if you could call it, $this->name should not have access to the value in Test2.

This breaks so many rules of OO I am seriously baffled how this got through testing. Well don for finding it!!

Codemwnci
The only time this should work is if Class3 extends Class2 which extends Class1. Then you could access one of Class1's method from within Class3 by calling `Class1::method()`, just like you can access an unknown parent's class by calling `parent::method()`... But as it stands, it's pretty bad...
ircmaxell
@ircmaxwell - why are you referencing parent class methods statically in your inheritance examples? You do know that `::` in PHP is a static reference, right? Your comment above, regarding inheritance, should really use `$this->method();`. You don't need to extend a class to reference one of it's methods statically. You simply need to define that method as `static`
Jason McCreary
Well, you can access a parents method by calling `parent::method()`, which is useful if you need to access an overridden method (For example, the parent defines `getName`, and the instantiated class overrides it. You can access the parent's version by calling `parent::getName()`)... What I'm saying, is that this may have been intended to let you access a inherited class's method by class name (in case you have deep inheritance)...
ircmaxell
Well, 2 is expected behavior in PHP. The problem is 3.
nikic
+1  A: 

PHP allows calling non-static methods as if they were static - that's a feature. PHP passes $this as an implicit parameter to such calls. Much like it does when calling a method the normal way.

But obviously PHP doesn't check whether the statically called class inherits the current one - and that's the bug.

This is how you could think of what PHP does:

function Test1->getName($this = null) {
    return $this->name;
}

function Test2->getName($this = null) {
    return Test1->getName($this);
}

$test = new Test2;
echo $test->getName($test);

This behavior is wrong. The correct Test2->getName would be:

function Test2->getName($this = null) {
    return $this instanceof Test1 ? Test1->getName($this) : Test1->getName();
}
nikic