Что нового в PHP 8 (атрибуты, union types, match)?php-68

PHP 8 принес значительные изменения в язык, которые стоит разобрать подробно.

1. Атрибуты

Атрибуты заменяют докблок-аннотации и предоставляют структурированный способ добавления метаданных к коду.

Пример использования:

#[Route("/api/posts", methods: ["GET"])]
class PostController
{
    #[Autowired]
    private PostRepository $repository;

    #[Cache(3600)]
    public function list(): array
    {
        return $this->repository->findAll();
    }
}

Создание кастомных атрибутов:

#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)]
class Cache {
    public function __construct(
        public int $lifetime
    ) {}
}

2. Union Types

Union Types позволяют указывать несколько допустимых типов для параметров, возвращаемых значений и свойств.

Пример:

function parseValue(string|int|float $value): string|array
{
    if (is_numeric($value)) {
        return (string)$value;
    }
    return explode(',', $value);
}

Особенности:

  • Поддерживаются все типы, кроме void и never
  • Можно комбинировать с nullable (?Type или Type|null)
  • Совместимы с PHPDoc @param и @return

3. Match-выражение

Улучшенная альтернатива switch с более строгой семантикой и возвращаемым значением.

Сравнение с switch:

// Старый вариант
switch ($statusCode) {
    case 200:
    case 204:
        $message = 'OK';
        break;
    case 404:
        $message = 'Not found';
        break;
    default:
        $message = 'Unknown status';
}

// Новый вариант с match
$message = match($statusCode) {
    200, 204 => 'OK',
    404 => 'Not found',
    default => 'Unknown status',
};

Ключевые преимущества:

  • Возвращает значение
  • Строгое сравнение (===)
  • Нет "проваливания" (no fallthrough)
  • Поддерживает выражения в ветках

Другие важные нововведения PHP 8

4. Конструктор свойства

class User {
    public function __construct(
        public string $name,
        protected int $age,
        private bool $isAdmin = false
    ) {}
}

5. Nullsafe оператор

$country = $user?->getAddress()?->getCountry()?->getName();

6. Именованные аргументы

array_fill(start_index: 0, num: 100, value: 50);

7. JIT-компиляция

Включение в php.ini:

opcache.enable=1
opcache.jit_buffer_size=100M
opcache.jit=tracing

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

Пример современного PHP 8 кода:

#[ApiResource]
class ProductController
{
    public function __construct(
        private ProductRepository $repository,
        private CacheInterface $cache
    ) {}

    #[Route('/products/{id}', methods: ['GET'])]
    public function show(int|string $id): Product|Response
    {
        return $this->cache->get(
            key: "product_$id",
            default: fn() => $this->repository->findById($id) ?? throw new NotFoundHttpException()
        );
    }
}

Миграция с PHP 7 на PHP 8

  1. Анализ кода:

    phpstan analyse --level=max
    
  2. Автоматические исправления:

    rector process src --set php80
    
  3. Тестирование:

    composer require phpunit/phpunit:^9.5 --dev
    

Резюмируем:

PHP 8 представляет значительный эволюционный скачок для языка, вводя современные функции вроде атрибутов, union types и match-выражений, которые делают код более выразительным и безопасным. Эти изменения требуют изучения, но значительно повышают качество и поддерживаемость кодовой базы.