Generics в PHP: зачем нужны и как использовать для начинающих

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

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

Начать курс

Введение в Generics в PHP: что это и зачем нужно

Если вы только начинаете изучать PHP или уже пишете на нём какое-то время, то наверняка сталкивались с ситуацией, когда функция или класс должны работать с разными типами данных. Например, вы создаёте коллекцию, которая может хранить и числа, и строки, и объекты. Без Generics (обобщённых типов) вам приходится либо писать отдельные классы для каждого типа, либо использовать смешанные типы (mixed), что снижает безопасность кода.

Generics — это механизм, который позволяет создавать классы, интерфейсы и функции, работающие с различными типами данных, сохраняя при этом строгую типизацию. В PHP поддержка Generics появилась начиная с версии 8.0, а в PHP 8.1 и 8.2 была значительно улучшена. В этой статье мы разберём, зачем они нужны, как их использовать и какие преимущества они дают.

Зачем нужны Generics в PHP?

Проблема отсутствия обобщённых типов

Представьте, что вы пишете класс для работы со списком элементов. Без Generics вам придётся использовать тип mixed или писать отдельные классы для каждого типа данных:

<?php
// Без Generics — небезопасно
class SimpleList {
    private array $items = [];

    public function add($item): void {
        $this->items[] = $item;
    }

    public function get(int $index): mixed {
        return $this->items[$index] ?? null;
    }
}

// Использование
$list = new SimpleList();
$list->add(42);
$list->add('строка'); // Ошибка не возникнет, но логика нарушена
$value = $list->get(0); // Тип mixed — IDE не подскажет, что это int
?>

Как видите, такой подход приводит к потенциальным ошибкам: вы можете случайно добавить строку туда, где ожидаются только числа, и IDE не сможет предупредить вас об этом.

Преимущества Generics

  • Типобезопасность: компилятор и IDE проверяют соответствие типов на этапе написания кода.
  • Повторное использование: один класс или функция работают с любыми типами данных.
  • Читаемость кода: сразу понятно, какие типы ожидаются.
  • Меньше ошибок: исключаются случайные несоответствия типов.

С Generics тот же класс списка становится безопасным и универсальным:

<?php
// С Generics — безопасно
class GenericList {
    private array $items = [];

    public function add(T $item): void {
        $this->items[] = $item;
    }

    public function get(int $index): ?T {
        return $this->items[$index] ?? null;
    }
}

// Использование
$intList = new GenericList();
$intList->add(42);
// $intList->add('строка'); // Ошибка типов!

$stringList = new GenericList();
$stringList->add('Привет');
$value = $stringList->get(0); // IDE знает, что это string
?>

Как использовать Generics в PHP

Базовый синтаксис

Generics в PHP объявляются с помощью угловых скобок <> и буквы T (от Type) — это общепринятое имя для переменной типа. Вы можете использовать любые имена, но T — стандарт.

Пример с классом:

<?php
class Container {
    private T $value;

    public function __construct(T $value) {
        $this->value = $value;
    }

    public function getValue(): T {
        return $this->value;
    }
}

$intContainer = new Container(100);
$stringContainer = new Container('Hello');
?>

Ограничения типов

Иногда нужно, чтобы тип параметра соответствовал определённому интерфейсу или классу. Для этого используются ограничения (bounds):

<?php
interface Printable {
    public function print(): string;
}

class Document implements Printable {
    public function print(): string {
        return 'Документ';
    }
}

// Ограничение: T должен реализовывать Printable
class Printer {
    public function printItem(T $item): void {
        echo $item->print();
    }
}

$printer = new Printer();
$printer->printItem(new Document()); // Работает
// $printer->printItem(123); // Ошибка: int не реализует Printable
?>

Generics в функциях

Обобщённые типы можно использовать не только в классах, но и в обычных функциях:

<?php
function identity(T $value): T {
    return $value;
}

echo identity(42); // int
echo identity('текст'); // string
?>

Множественные параметры типа

Если нужно работать с несколькими типами, используйте запятые:

<?php
class Pair {
    public function __construct(
        private T $first,
        private U $second
    ) {}

    public function getFirst(): T {
        return $this->first;
    }

    public function getSecond(): U {
        return $this->second;
    }
}

$pair = new Pair(1, 'один');
echo $pair->getFirst(); // int
echo $pair->getSecond(); // string
?>

Практические примеры использования Generics

Коллекции и репозитории

Одно из самых частых применений Generics — создание типобезопасных коллекций:

<?php
class Collection {
    private array $items = [];

    public function add(T $item): void {
        $this->items[] = $item;
    }

    public function filter(callable $callback): Collection {
        $result = new Collection();
        foreach ($this->items as $item) {
            if ($callback($item)) {
                $result->add($item);
            }
        }
        return $result;
    }

    public function all(): array {
        return $this->items;
    }
}

// Использование с конкретным типом
$userCollection = new Collection();
$userCollection->add(new User('Alice'));
$userCollection->add(new User('Bob'));

$filtered = $userCollection->filter(fn(User $user) => $user->name !== 'Alice');
?>

DTO и Value Objects

Generics отлично подходят для создания объектов-значений (Value Objects):

<?php
class Result {
    private function __construct(
        private readonly bool $success,
        private readonly mixed $data,
        private readonly ?string $error
    ) {}

    public static function success(T $data): self {
        return new self(true, $data, null);
    }

    public static function error(string $error): self {
        return new                

Блоги

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