Avatar ·

Когда использовать self, а когда $this в PHP? Разбираемся с областью видимости

Всем привет! Я начал изучать ООП в PHP и постоянно путаюсь, когда нужно использовать `self`, а когда `$this`. Вроде бы оба обращаются к свойствам и методам класса, но разница явно есть, и я не могу её уловить. Вот простой пример, который меня сбивает с толку. У меня есть класс с константой и обычным свойством: ```php class Car { const WHEELS = 4; public $color = 'red'; public function showInfo() { echo "Колёс: " . self::WHEELS . "\n"; echo "Цвет: " . $this->color . "\n"; } } $car = new Car(); $car->showInfo(); ``` Почему для константы я пишу `self::WHEELS`, а для свойства `$this->color`? Что будет, если я попробую написать `self::$color` или `$this::WHEELS`? Работает ли это в наследовании? Например, если я создам дочерний класс и переопределю константу или свойство, как тогда правильно обращаться? Я уже начитался про статические методы и свойства, но всё равно чувствую, что не до конца понимаю логику. Объясните, пожалуйста, на понятных примерах, когда какой вариант применять. Особенно интересует, как это связано с наследованием и переопределением. Заранее спасибо! <<>>

Ключевое различие: статический контекст vs контекст объекта

Главное правило, которое нужно запомнить: $this работает с экземпляром класса (объектом), а self — с самим классом (статический контекст). Отсюда вытекают все остальные различия.

Когда использовать $this?

$this — это псевдопеременная, которая ссылается на текущий объект. Она доступна только внутри нестатических методов. С её помощью вы обращаетесь к:

  • Нестатическим свойствам ($this->color)
  • Нестатическим методам ($this->drive())

Важно: $this всегда ссылается на тот объект, из которого был вызван метод. Это особенно важно при наследовании.

Когда использовать self?

self — это ссылка на текущий класс в том месте, где он написан (лексическая область видимости). Используется для доступа к:

  • Константам класса (self::WHEELS)
  • Статическим свойствам (self::$count)
  • Статическим методам (self::create())

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

Вот тут и проявляется хитрость. Смотрите, что произойдёт, если мы создадим дочерний класс:

class Car {
    const WHEELS = 4;
    public static $count = 0;
    
    public function showSelf() {
        echo self::WHEELS;     // Всегда выведет 4 (из класса Car)
        echo self::$count;     // Всегда выведет значение из класса Car
    }
    
    public function showStatic() {
        echo static::WHEELS;   // Выведет то, что определил поздний класс
        echo static::$count;   // Позднее статическое связывание
    }
}

class Truck extends Car {
    const WHEELS = 6;
    public static $count = 10;
}

$truck = new Truck();
$truck->showSelf();    // Выведет: 4 и 0 (self замкнут на Car)
$truck->showStatic();  // Выведет: 6 и 10 (static использует позднее связывание)

Что нельзя делать?

  • $this::WHEELS — работать будет, но это плохая практика. $this тут используется как ссылка на класс объекта, но логически это сбивает с толку.
  • self::$color — ошибка! Нельзя использовать self для доступа к нестатическому свойству.
  • $this->WHEELS — тоже ошибка. Константы не являются свойствами объекта.

Простое правило для запоминания

Задайте себе два вопроса:

  1. «Это значение может быть разным для разных объектов?» → используйте $this (свойство объекта).
  2. «Это значение одинаково для всех объектов или вообще не привязано к объекту?» → используйте self (константа, статическое свойство/метод).

И помните про static:: — если вы пишете код, который должен работать с наследованием и подменой значений в дочерних классах, используйте позднее статическое связывание вместо self.

<<>>

Дополнение про статические методы и self

Стоит добавить важный нюанс: self часто используется внутри статических методов, где $this вообще недоступен. Например:

class Logger {
    private static $log = [];
    
    public static function add($msg) {
        self::$log[] = $msg; // Нельзя написать $this->log
    }
    
    public static function show() {
        foreach (self::$log as $entry) {
            echo $entry . "\n";
        }
    }
}

Logger::add('Ошибка');
Logger::show();

Ещё один частый сценарий — фабричные методы или синглтон:

class Database {
    private static $instance = null;
    
    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self(); // self() создаёт экземпляр текущего класса
        }
        return self::$instance;
    }
}

Обратите внимание на new self() — это создание объекта того класса, где написан код. Если вам нужно, чтобы при наследовании создавался объект дочернего класса, используйте new static().

Итог: $this — для работы с данными конкретного объекта, self — для работы с классом как с сущностью (константы, статика). Не путайте их, и ваш код будет предсказуемым.