New self vs new static в PHP 2025: что выбрать

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

New self vs new static в PHP: что выбрать в 2025 году?

Представьте: вы пишете на PHP уже несколько лет, чувствуете себя уверенно, и вдруг ваш коллега (или Stack Overflow) задаёт вопрос: «Почему у меня в дочернем классе создаётся объект родительского класса, хотя я вызываю конструктор через new self()?». Знакомо? Если да, то вы не одиноки. Тема new self vs new static — один из самых популярных вопросов на Stack Overflow с тысячами просмотров. И в 2025 году, с выходом новых версий PHP, понимание этой разницы стало ещё важнее.

В этой статье мы разберёмся, чем отличаются self и static при создании объектов, как работает позднее статическое связывание (Late Static Binding) и, главное, когда что использовать. Поехали!

Проблема: self всегда указывает на родителя

Давайте начнём с классического примера. Предположим, у нас есть родительский класс BaseController и дочерний ApiController.

<?php

class BaseController {
    public static function create() {
        return new self(); // Внимание! self — это BaseController
    }
}

class ApiController extends BaseController {
    // Наследует create()
}

$api = ApiController::create();
var_dump($api); // object(BaseController)#1 (0) { }
?>

Мы ожидали получить объект ApiController, а получили BaseController. Почему? Потому что self в контексте родительского класса всегда ссылается на тот класс, где он написан, а не на класс, через который его вызвали. Это раннее статическое связывание. PHP «запоминает» класс во время компиляции, а не во время выполнения.

Решение: используем static

Чтобы исправить ситуацию, нужно заменить self на static. Это включает механизм позднего статического связывания (LSP).

<?php

class BaseController {
    public static function create() {
        return new static(); // static — это класс, который вызвал метод
    }
}

class ApiController extends BaseController {
    // Наследует create()
}

$api = ApiController::create();
var_dump($api); // object(ApiController)#1 (0) { }
?>

Теперь $api — это объект ApiController. static разрешается во время выполнения и указывает на класс, через который был вызван метод. Это и есть позднее статическое связывание.

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

В 2025 году лучшие практики чётко разделяют эти два подхода. Вот основные правила.

Используйте self, когда:

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

Пример: фабричный метод, который гарантированно возвращает объект базового класса.

<?php

class Logger {
    protected static $instance;

    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self(); // Всегда Logger
        }
        return self::$instance;
    }
}
?>

Используйте static, когда:

  • Вы проектируете код для расширения — например, абстрактные классы или трейты.
  • Вам нужно, чтобы дочерние классы могли создавать свои собственные экземпляры.
  • Вы используете паттерн Factory Method или Builder.

Пример: абстрактный класс для сущностей.

<?php

abstract class Entity {
    public static function create(array $data): static {
        $entity = new static();
        // ... валидация и заполнение
        return $entity;
    }
}

class User extends Entity {}
class Post extends Entity {}

$user = User::create(['name' => 'Alice']);
$post = Post::create(['title' => 'Hello']);
?>

Сравнение self и static в таблице

Чтобы закрепить, вот наглядное сравнение:

Критерий self static
Тип связывания Раннее (compile-time) Позднее (run-time)
Наследование Всегда ссылается на класс, где написан Ссылается на класс-вызывающий
Применение Константы, приватные статические свойства, синглтон Фабрики, билдеры, абстрактные методы
Риск Неожиданное создание родительского объекта Требует осторожности при цепочках вызовов

Продвинутые сценарии: цепочки и трейты

В реальных проектах 2025 года вы часто встретите комбинацию self и static. Рассмотрим два популярных случая.

1. Цепочки вызовов (Fluent interface)

В трейтах или абстрактных классах static обязателен для возврата правильного типа.

<?php

trait HasTimestamps {
    protected $createdAt;

    public function setCreatedAt(string $time): static {
        $this->createdAt = $time;
        return $this; // Возвращаем static
    }
}

class Article {
    use HasTimestamps;
}

$article = (new Article())->setCreatedAt('2025-01-01');
var_dump($article); // object(Article)
?>

Если бы мы написали return $this без static в трейте — всё равно работает, так как $this уже является объектом. Но для статических методов возврат static критичен.

2. Паттерн Builder с наследованием

Представьте, что вы строите SQL-запросы. Базовый класс QueryBuilder и дочерний MySqlBuilder.

<?php

class QueryBuilder {
    protected $query = '';

    public static function table(string $name): static {
        $builder = new static();
        $builder->query = "SELECT * FROM $name";
        return $builder;
    }

    public function where(string $condition): static {
        $this->query .= " WHERE $condition";
        return $this;
    }

    public function getQuery(): string {
        return $this->query;
    }
}

class MySqlBuilder extends QueryBuilder {
    public function limit(int $count): static {
        $this-                

Блоги

Рекомендаций по книгам