7

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.

Gnuffo1
  • 3,478
  • 11
  • 39
  • 53
  • 2
    WTH? That actually works! (I tried it)... It shouldn't (based on PHP's inheritance rules, and static calls). The call to `Test1::getName()` does generate an `E_STRICT` error, but if you have error_reporting to not show strict errors, it works... I think you stumbled across a bug... But then again, it also works on 5.2... Very Weird... – ircmaxell Oct 02 '10 at 12:54
  • I'd say its a bug..... I expected 100% that the return would be `NULL` – Wrikken Oct 02 '10 at 13:00
  • Not that weird. Objects are pretty much just arrays with a funny syntax and a way to tell PHP which functions to call by default. You can even add instance variables on the fly. With that said, though, i wouldn't rely on this misfeature -- it may get "fixed" later on, if PHP ever fixes its type system. (LOL...who am i kidding? Use the hell out of it. They never fix anything.) – cHao Oct 02 '10 at 13:02
  • @Wrikken: Wouldn't it give a fatal error (using `$this` in a non-object context, since the call is static)? @cHao: But this breaks all encapsulation with the object (since it's then possible for `$this` to not refer to the class where the method is defined (or extended from)... In other words: This is VERY dangerous... – ircmaxell Oct 02 '10 at 13:07
  • @ircmaxell: depends, in a static method, there is no `$this`, so either a fatal error as it is a reserved variable, or 'normal' variable (i.e. a normal non-existing variable => NULL) would be more appropriate in my view then this _'let's see if we can find a object in the backtrace to put in it's place'_. But I see your point: a quick tests indicates using `$this` in a 'normal' function _does_ trigger a fatal error, so indeed, that would be the more consistent behavior. – Wrikken Oct 02 '10 at 13:11
  • @Wrikken: Well, if I change `Test1::getName` to static, it fatals out... – ircmaxell Oct 02 '10 at 13:13
  • @ircmaxell: indeed, and as it is called statically instead of on an instance (which produces a meager E_NOTICE error), I'm indeed swayed to your point that _called_ statically there should be a fatal error at the point of `$this`, just like when it's _defined_ static. – Wrikken Oct 02 '10 at 13:16
  • I'm going to submit a but report on this... – ircmaxell Oct 02 '10 at 13:20
  • @ircmaxell - but it might get fixed! – Gnuffo1 Oct 02 '10 at 13:21
  • behaviour confirmed on PHP 5.3 on Windows. Weird and definitely a bug! – Pekka Oct 02 '10 at 13:24
  • 4
    But report submitted: http://bugs.php.net/bug.php?id=52969 – ircmaxell Oct 02 '10 at 13:24
  • @Gnuffo1: It's a bug that breaks basic object paradigms, namely encapsulation and inheritance. So it's pretty important that it is fixed... – ircmaxell Oct 02 '10 at 13:25
  • 1
    Call it a bug if you wish, but it is documented behavior. http://us2.php.net/manual/en/language.oop5.basic.php – GZipp Oct 02 '10 at 13:46
  • @GZipp: Can you tell me where this is documented? I couldn't find anything on the linked page. – NikiC Oct 02 '10 at 13:51
  • Example #2 Some examples of the $this pseudo-variable - specifically the lines $b = new B(); $b->bar(); – Gnuffo1 Oct 02 '10 at 13:51
  • 3
    Well if this really is known and documented I must draw the conclusion that PHP really does suck in this point. – NikiC Oct 02 '10 at 13:53
  • It's not a bug. It's PHP we're talking about here. As has been pointed out, there's no inheritance but a static method call. The $this transition is rather unkown. But it's only a bug if you're orientated on crusty OOP definitions. This behaviour is actually closer to what proper OO languages like Python and Javascript can accomplish. It's a documentation failure. – mario Oct 02 '10 at 14:32
  • Example #2 (functionally identical to op's code), pointed out by Gnuffo1, and: "$this is a reference to the calling object (usually the object to which the method belongs, but possibly another object, if the method is called statically from the context of a secondary object)." Both near the top of the page. I'm perplexed at ignoring a warning, then complaining about a "bug". – GZipp Oct 02 '10 at 14:49
  • It doesn't show a warning unless E_STRICT is on (which most people I've seen don't run). And even if it's documented, I think it's still a bug (and a fairly big one at that)... – ircmaxell Oct 02 '10 at 19:06
  • @ircmaxell: If you hadn't declared your field as public, it'd violate encapsulation. As it stands, it's just PHP doing weird stuff with `$this`. Javascript does something similar (and equally annoying at times) if you say `var thisIsntThis = someObject.otherFunction; thisIsntThis();`. It's an artifact of how the language handles objects and methods. I'd still call it a bug, but it's semi because of how the language was defined, and it's probably too late to fix it now.. – cHao Oct 02 '10 at 19:42

3 Answers3

1

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
  • 71,546
  • 23
  • 135
  • 174
  • `$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 Oct 02 '10 at 13:02
  • 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 Oct 02 '10 at 13:02
  • 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 Oct 02 '10 at 13:12
1

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
  • 100,734
  • 37
  • 191
  • 225
0

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
  • 54,176
  • 10
  • 96
  • 129
  • 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 Oct 02 '10 at 13:28
  • @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 Oct 02 '10 at 13:35
  • 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 Oct 02 '10 at 13:37
  • Well, 2 is expected behavior in PHP. The problem is 3. – NikiC Oct 02 '10 at 13:46