В чем разница между self::, $this и static:: в PHP?

Ответ

Эти ключевые слова решают разные задачи при работе со свойствами и методами класса.

Ключевое слово Контекст Что ссылается
$this Только внутри нестатического метода. На текущий объект (экземпляр класса). Позволяет обращаться к нестатическим свойствам и методам.
self:: Внутри класса (в статическом или нестатическом методе). На класс, где оно написано (раннее связывание). Используется для статических свойств, методов и констант.
static:: Внутри класса (в статическом или нестатическом методе). На класс, который вызвал метод во время выполнения (позднее статическое связывание, LSB).

Практический пример с наследованием:

class ParentClass {
    protected static $value = 'Parent';

    public static function getSelf() {
        return self::$value; // Всегда ссылается на ParentClass
    }

    public static function getStatic() {
        return static::$value; // Ссылается на класс, из которого вызван метод
    }

    public function showThis() {
        return $this; // Возвращает текущий объект
    }
}

class ChildClass extends ParentClass {
    protected static $value = 'Child';
}

// Использование self:: vs static::
echo ParentClass::getSelf();   // 'Parent'
echo ParentClass::getStatic(); // 'Parent'

echo ChildClass::getSelf();    // 'Parent' (self:: берется из ParentClass)
echo ChildClass::getStatic();  // 'Child'  (static:: определилось в runtime как ChildClass)

// Использование $this
$obj = new ChildClass();
var_dump($obj->showThis()); // object(ChildClass)#1 {...}

Ключевые выводы:

  1. Используйте $this-> для работы с состоянием конкретного объекта.
  2. Используйте self::, когда вы уверены, что не хотите, чтобы метод/свойство переопределялись в дочерних классах (например, для финальных утилитных методов).
  3. Используйте static:: для реализации шаблона «Шаблонный метод», когда родительский класс определяет общий алгоритм, но часть шагов делегирует дочерним классам.

Ответ 18+ 🔞

А, ну вот, опять эти три засранца — $this, self:: и static::. Сидят в классе, как три мартышлюшки на одной ветке, и каждый тянет одеяло на себя. Давай разберёмся, кто из них на что ссылается, а то голова уже болит.

Смотри, $this — это самый простой пацан. Он работает только внутри обычных, нестатических методов. Его задача — показывать пальцем на текущий объект, который ты создал через new. Хочешь обратиться к свойству этого объекта или вызвать его метод? $this->property, $this->method() — и всё, вопросов нет. Он как бы говорит: «Эй, работаем с вот этой конкретной штукой в памяти, а не с абстрактной хернёй».

Дальше идёт self::. Этот уже похитрее, хитрая жопа. Он ссылается на класс, в котором он написан. Это называется раннее связывание. Компилятор видит self:: и сразу лезет в тот класс, где эта строчка кода прописана. Чаще всего его используют для статических свойств, методов и констант. Но вот в чём подвох: если ты унаследуешься от этого класса и переопределишь что-то, self:: всё равно будет тыкать в родителя, будто ничего и не менялось. Доверия к нему — ноль ебать.

Ну и король подставы — static::. Это позднее статическое связывание, или LSB, если по-умному. Он определяет, на какой класс ссылаться, не в момент написания кода, а в момент его выполнения. То есть он смотрит: «Ага, метод вызвали из класса ChildClass? Значит, и статические штуки надо брать из ChildClass, даже если сам метод описан в ParentClass». Удобно для всяких шаблонных методов, когда родитель говорит «делай как я», но часть логики отдаёт на откуп детям.

Вот смотри на примере, тут всё как на ладони:

class ParentClass {
    protected static $value = 'Parent';

    public static function getSelf() {
        return self::$value; // Туповато, всегда будет 'Parent'
    }

    public static function getStatic() {
        return static::$value; // Умно, подстроится под того, кто вызвал
    }

    public function showThis() {
        return $this; // Просто вернёт текущий объект, без затей
    }
}

class ChildClass extends ParentClass {
    protected static $value = 'Child'; // Переопределили статику
}

// Тестируем self:: vs static::
echo ParentClass::getSelf();   // 'Parent' — логично
echo ParentClass::getStatic(); // 'Parent' — тоже логично

echo ChildClass::getSelf();    // 'Parent' — вот тут охуеть! self:: не увидел переопределения.
echo ChildClass::getStatic();  // 'Child'  — а static:: увидел и отработал как надо.

// А $this просто делает свою работу
$obj = new ChildClass();
var_dump($obj->showThis()); // object(ChildClass)#1 {...} — вот он, целый объект.

Итог, чтобы не запутаться:

  1. $this-> — твой брат-близнец для работы внутри объекта. Нестатичные свойства, методы — всё через него.
  2. self:: — упрямый осёл, который смотрит только туда, где написан. Используй, если точно уверен, что переопределять ничего не будут (или если тебе на это похуй).
  3. static:: — хитрая лиса, которая подстраивается под того, кто реально вызвал метод. Незаменимая вещь для грамотного наследования, когда родительский класс оставляет детям «дырки» для своей логики.