OOP: new static() vs new self()
Glossary overview

OOP: new static() vs new self()

new static — это способ создать объект класса с учётом позднего статического связывания (Late Static Binding). Механизм появился в PHP 5.3 и решает одну конкретную проблему: в каком именно классе мы находимся при вызове через наследование.

new self vs new static

new self()new static()
ПривязкаК классу, где написан кодК классу, на котором вызван метод
Учитывает наследованиеНетДа
Когда определяется классНа этапе компиляцииНа этапе выполнения

Простой пример

class Animal
{
    protected string $type = 'animal';

    public static function create(): static
    {
        return new static();
    }

    public function getType(): string
    {
        return $this->type;
    }
}

class Dog extends Animal
{
    protected string $type = 'dog';
}

class Cat extends Animal
{
    protected string $type = 'cat';
}

Результат вызовов:

Animal::create();           // объект Animal
Dog::create();              // объект Dog (не Animal!)
Cat::create();              // объект Cat (не Animal!)

Dog::create()->getType();   // "dog"

Если заменить new static() на new self(), то Dog::create() и Cat::create() вернут Animal — потому что self жёстко указывает на класс, в котором написан метод.

Где это используется в Laravel

1. Eloquent-модели

Внутри базового класса Model множество методов используют new static:

// Illuminate\Database\Eloquent\Model

public static function query()
{
    return (new static)->newQuery();
}

public static function create(array $attributes = [])
{
    $model = new static($attributes);
    $model->save();
    return $model;
}

Благодаря этому User::create([...]) создаёт именно User, а Post::query() работает именно с моделью Post.

2. Фабричные методы

// Пример из Collection
public static function make($items = [])
{
    return new static($items);
}

3. Builder-паттерн

class FormRequest
{
    public static function fromRequest(Request $request): static
    {
        $instance = new static();
        $instance->merge($request->all());
        return $instance;
    }
}

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

class BaseRepository
{
    protected static string $model;

    public static function findOrFail(int $id): Model
    {
        return (new static)->getModel()::findOrFail($id);
    }

    protected function getModel(): string
    {
        return static::$model;
    }
}

class UserRepository extends BaseRepository
{
    protected static string $model = User::class;
}

class PostRepository extends BaseRepository
{
    protected static string $model = Post::class;
}

// Вызов
UserRepository::findOrFail(1); // ищет User
PostRepository::findOrFail(1); // ищет Post

Бонус: static как тип возврата

Начиная с PHP 8.0 можно указывать static как return type:

class Builder
{
    public function where(string $column, mixed $value): static
    {
        // ...
        return $this;
    }
}

Это говорит: метод возвращает экземпляр того же класса (или дочернего), на котором был вызван. Полезно для fluent-интерфейсов и цепочек вызовов.

Итого

  • new self() — всегда создаёт объект класса, где написан код.
  • new static() — создаёт объект класса, на котором вызван метод (учитывает наследование).
  • В Laravel new static — основа работы Eloquent, коллекций и фабричных методов.
  • Используйте new static, когда пишете базовый класс, от которого будут наследоваться другие.