Что такое ключевое слово self в PHP?

Ответ

Ключевое слово self в PHP используется внутри класса для ссылки на этот же класс. Оно применяется для доступа к статическим свойствам и методам, а также для указания типа возвращаемого значения.

Основные случаи использования:

  1. Доступ к статическим членам класса:

    class Logger {
        private static array $logs = [];
    
        public static function addLog(string $message): void {
            self::$logs[] = $message; // self ссылается на класс Logger
        }
    }
  2. Указание типа возвращаемого значения (возврат экземпляра текущего класса):

    class Config {
        public static function getInstance(): self {
            return new self(); // Создаёт экземпляр Config
        }
    }

Критически важное отличие self от static (позднее статическое связывание):

  • self всегда ссылается на класс, в котором оно написано.
  • static ссылается на класс, из которого был вызван метод (позволяет переопределение в дочерних классах).

Наглядный пример различия:

class ParentClass {
    public static function createWithSelf(): self {
        return new self(); // Всегда вернёт объект ParentClass
    }

    public static function createWithStatic(): static {
        return new static(); // Вернёт объект вызывающего класса
    }
}

class ChildClass extends ParentClass {}

var_dump(ChildClass::createWithSelf());  // object(ParentClass)
var_dump(ChildClass::createWithStatic()); // object(ChildClass)

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

  • Когда вам гарантированно нужен экземпляр или статический член именно текущего класса, а не его наследника.
  • Для фабричных методов, которые должны создавать объекты определённого, фиксированного класса.

Когда использовать static:

  • Для реализации шаблона «Фабричный метод», где каждый наследник должен создавать свой собственный экземпляр.
  • В абстрактных классах или трейтах, где логика должна работать с классом-потомком.

Ответ 18+ 🔞

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

Вот представь: есть у тебя класс, ну, скажем, Logger. И в нём статическое свойство — массив логов. Так вот, self — это как твоя родная фамилия. Ты где бы ни был, в каком наследнике ни оказался, self всегда будет тыкать пальцем в тот самый класс, где это слово написано в коде. Не там, откуда позвали, а именно там, где его вписали чернилами.

class Logger {
    private static array $logs = [];

    public static function addLog(string $message): void {
        self::$logs[] = $message; // self тут — это навсегда Logger. Точка.
    }
}

Второй случай — когда метод должен вернуть объект этого же самого класса. Типа фабричный метод. Опять self — это железное обещание: «Я, сука, верну именно тот тип, в котором объявлен этот метод».

class Config {
    public static function getInstance(): self {
        return new self(); // Создаст именно Config, даже если вызовут из наследника.
    }
}

А теперь, блядь, самое важное, где все и обламываются. Разница между self и static. Это, чувак, как два брата-близнеца: один упёртый и тупой (self), а другой гибкий и умный (static).

  • self — упёртый. Он смотрит только туда, где его написали. «Я родился в классе ParentClass — значит, я всегда буду про ParentClass».
  • static — хитрая жопа. Он смотрит, откуда его реально позвали. «Ага, меня вызвали из ChildClass — значит, я теперь про ChildClass».

Смотри, вот тебе живой пример, после которого всё встанет на свои места:

class ParentClass {
    public static function createWithSelf(): self {
        return new self(); // Упёртый. Всё равно вернёт ParentClass.
    }

    public static function createWithStatic(): static {
        return new static(); // Хитрый. Вернёт тот класс, откуда позвали.
    }
}

class ChildClass extends ParentClass {}

var_dump(ChildClass::createWithSelf());  // object(ParentClass) — вот, бля, сюрприз!
var_dump(ChildClass::createWithStatic()); // object(ChildClass) — вот это уже логично.

Видишь, что происходит? self нихуя не смотрит на то, что ты вызываешь метод из ChildClass. Он как баран упёрся: «Я написан в ParentClass — значит, создаю ParentClass». А static — тот ещё приспособленец, сразу подстраивается под того, кто его кликнул.

Так когда что юзать?

  • self — когда тебе нахуй не сдались наследники. Ты пишешь железобетонную логику именно для этого класса. Например, у тебя класс MySQLConnection, и его фабричный метод должен создавать только его, а не какую-то левую поделку. Или доступ к статике, которая точно определена в этом классе.
  • static — когда ты проектируешь для расширения. Шаблон «Фабричный метод», абстрактные классы, трейты — там, где каждый уважающий себя наследник должен уметь создавать себя самого, а не своего предка. Вот тут static — твой лучший друг, иначе будет волнение ебать, когда всё поломается.

Короче, запомни: self — это про «где написано», static — про «откуда вызвано». Всё остальное — от лукавого и непонимания базовых принципов. Не усложняй, иди пиши код.