Difference between static:: and $this::
Asked Answered
N

2

16

I know there is a difference between static:: and self:: like in this example ( from https://mcmap.net/q/173977/-overriding-class-constants-vs-properties )

<?php
class One
{
    const TEST = "test1";
    function test() { echo static::TEST; }
}
class Two extends One
{
    const TEST = "test2";
}

$c = new Two();
$c->test();

Which returns test2 when static::TEST is used and test1 when self::TEST is used. But it also returns test2 when $this::TEST is used.

static::TEST can be used inside a static method, whereas $this::TEST requires an instance before being used (so non-usable in static methods).

But if one cannot use $this:: in static methods, static:: can be used in non-static methods (like in the example).

So, what is the difference between static:: and $this:: in a non static method?


Optional complete test

<?php
abstract class AOne
{
    const TEST = "test1";
    abstract public function test();
}
class OneStatic extends AOne
{
    public function test()
    {
        return static::TEST;
    }
}
class TwoStatic extends OneStatic
{
    const TEST = "test2";
}
class OneSelf extends AOne
{
    public function test()
    {
        return self::TEST;
    }
}
class TwoSelf extends OneSelf
{
    const TEST = "test2";
}
class OneThis extends AOne
{
    public function test()
    {
        return $this::TEST;
    }
}
class TwoThis extends OneThis
{
    const TEST = "test2";
}

$objects = array(
    'one, static::'     => new OneStatic(),
    'two, static::'     => new TwoStatic(),
    'one,   self::'     => new OneSelf(),
    'two,   self::'     => new TwoSelf(),
    'one,  $this::'     => new OneThis(),
    'two,  $this::'     => new TwoThis(),
);

$results = array();
foreach ($objects as $name=>$object)
    $results[$name] = $object->test();

var_dump($results);
?>

Which yields

  • 'one, static::' => 'test1'
  • 'two, static::' => 'test2'
  • 'one, self::' => 'test1'
  • 'two, self::' => 'test1'
  • 'one, $this::' => 'test1'
  • 'two, $this::' => 'test2'

So self refers to the class where the method is defined, but there's no difference between $this:: and static:: in these non static methods.

Neon answered 29/6, 2014 at 12:48 Comment(2)
Your last conclusion is indeed correct :)Berbera
possible duplicate of OO PHP Accessing public variable from another classUnreasoning
W
7

There are three cases when you CAN'T use $this:: over static::

1. In static methods

public static function test() {
    return $this::MY_CONST;
}

Output:

Fatal error: Uncaught Error: Using $this when not in object context

2. In none-static methods which get called from a non-object context

class A {
    const MY_CONST = 33;
    public function test() {
        return $this::MY_CONST;
    }
}

echo A::test(); // test method here is called without instantiating class A

Output:

Fatal error: Uncaught Error: Using $this when not in object context

Updates:

As of PHP 8 calling a non-static method from a non-object context is not allowed and will yield the following error

Fatal error: Uncaught Error: Non-static method A::test() cannot be called statically

3. When using the special ::class keyword

class A {
    public function test() {
        return $this::class;
    }
}

$a = new A;
echo $a->test();

Output:

Fatal error: Dynamic class names are not allowed in compile-time

Note: in all the three cases static:: will work


For the last case PHP Documentation states that:

Note:

The class name resolution using ::class is a compile time transformation. That means at the time the class name string is created no autoloading has happened yet. As a consequence, class names are expanded even if the class does not exist. No error is issued in that case.

So you can't use $this::class because you can't reference to non-existent classes

PHP 8

The behavior in PHP 8 has changed!

For consistency reasons $this::class now provides the same result as get_class($this) and static::class

https://3v4l.org/iB99O

Wild answered 9/8, 2019 at 5:5 Comment(1)
I indeed forgot about the $this::class which is a good example, as others are actually based off static methods (or "called-like-static", ie anywhere $this doesn't exist)Neon
J
5

There really isn't one. The :: functionality has been expanded over time, so that the left hand side doesn't need to be a class literal but may also be an object instance or string variable with a class name. Sometime around the same time late static binding was introduced with the static keyword. As you said, $this can't be used in static methods, so static is the obvious and only choice here for late static binding.

In an object instance however you could use static:: to refer to the late-static bound class, which will be the class of the current object instance. Or you could use $this:: as a side effect of being able to use an object instance as shorthand for <class of this object $var>::. The end result is the same, it's simply functionality which happens to overlap in this particular point. The internal workings are somewhat different, but I can't think of a case where there would ever be any difference.

Just to complete the Venn diagram:

Only static can do this:

public static function foo() {
    static::FOO;
}

Only $var:: can do this:

$obj = new Foo;
$obj::FOO;

Both can do this:

public function foo() {
    static::FOO;
    $this::FOO;
}
Jagged answered 29/6, 2014 at 12:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.