HardТеория4 min

Новое в Symfony 8.0

Breaking changes, удалённые deprecated, новые возможности, PHP 8.4, миграция

PHP 8.4 Requirements

Symfony 8.0 требует PHP 8.4+. Это первая мажорная версия, использующая возможности PHP 8.4.

Новые возможности PHP, используемые в Sf8

Возможность Версия PHP Использование в Symfony
Readonly properties 8.1 DTOs, Value Objects
Enums 8.1 Statuses, Types в формах
Fibers 8.1 Асинхронные компоненты
Readonly classes 8.2 Immutable DTO
#[\Override] 8.3 Переопределение методов
Property hooks 8.4 Validated properties
Asymmetric visibility 8.4 public private(set)
<?php

declare(strict_types=1);

namespace App\Entity;

// PHP 8.4: Property hooks
final class Product
{
    public string $name {
        set(string $value) {
            if (strlen($value) < 3) {
                throw new \InvalidArgumentException('Name too short');
            }
            $this->name = trim($value);
        }
    }

    // PHP 8.4: Asymmetric visibility
    public private(set) int $viewCount = 0;

    public function incrementViews(): void
    {
        $this->viewCount++;
    }
}
<?php

declare(strict_types=1);

namespace App\DTO;

// PHP 8.2: Readonly class
final readonly class CreateUserDTO
{
    public function __construct(
        public string $email,
        public string $name,
        public string $password,
    ) {
    }
}

Breaking Changes: Sf7.4 -> Sf8.0

Symfony 8.0 -- мажорная версия. Все deprecated из Sf7.x удалены.

Удалённые deprecated

Security

<?php

// REMOVED: Old Guard authenticator
// Symfony\Component\Security\Guard\AuthenticatorInterface -- REMOVED

// USE: New authenticator system
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
# REMOVED: anonymous configuration
security:
    firewalls:
        main:
            anonymous: true  # REMOVED in Sf8

# USE: PUBLIC_ACCESS in access_control
security:
    firewalls:
        main:
            lazy: true
    access_control:
        - { path: ^/public, roles: PUBLIC_ACCESS }

Routing

<?php

// REMOVED: Annotations
// use Symfony\Component\Routing\Annotation\Route; -- REMOVED

// USE: PHP Attributes
use Symfony\Component\Routing\Attribute\Route;

#[Route('/api/users', name: 'api_users')]
final class UserController
{
    // ...
}

Forms

<?php

// REMOVED: getBlockPrefix() default behavior changed
// Type names are now fully qualified class names by default

// REMOVED: Various deprecated form options
// 'allow_extra_fields' option in resolver -- behavior changed

DependencyInjection

<?php

// REMOVED: ContainerAwareInterface
// use Symfony\Component\DependencyInjection\ContainerAwareInterface; -- REMOVED

// USE: Constructor injection
final class MyService
{
    public function __construct(
        private readonly SomeDependency $dependency,
    ) {
    }
}
# REMOVED: XML/annotations for service configuration
# All services configured via YAML or PHP attributes only

HttpKernel

<?php

// REMOVED: Various deprecated event names
// use Symfony\Component\HttpKernel\Event\GetResponseEvent; -- REMOVED
// USE: Symfony\Component\HttpKernel\Event\RequestEvent

// REMOVED: Deprecated controller resolver methods

Изменения поведения

1. Конфигурация: только YAML и PHP

# Sf7.4: XML, YAML, PHP, Annotations supported
# Sf8.0: Only YAML and PHP attributes

# routes/routes.yaml -- OK
# routes/routes.xml  -- NO LONGER SUPPORTED for exam
# @Route annotation  -- NO LONGER SUPPORTED

2. Default autoconfigure: true

В Sf8 autoconfigure включено по умолчанию для всех сервисов. Ранее нужно было явно указывать.

3. Error handling

<?php

// Sf8: handle_all_throwables enabled by default
// All errors (including PHP errors) converted to exceptions

Новые возможности Symfony 8.0

1. #[MapRequestPayload] и #[MapQueryString]

<?php

declare(strict_types=1);

namespace App\Controller;

use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
use Symfony\Component\HttpKernel\Attribute\MapQueryString;

final class ApiController extends AbstractController
{
    // Automatically deserialize JSON body to DTO
    #[Route('/api/users', methods: ['POST'])]
    public function create(
        #[MapRequestPayload] CreateUserDTO $dto,
    ): JsonResponse {
        // $dto is validated and populated from request body
        return new JsonResponse(['id' => 1], 201);
    }

    // Automatically map query parameters to DTO
    #[Route('/api/users', methods: ['GET'])]
    public function list(
        #[MapQueryString] UserFilterDTO $filter,
    ): JsonResponse {
        // $filter populated from ?page=1&limit=20&sort=name
        return new JsonResponse([]);
    }
}

2. Console PHP attributes

<?php

declare(strict_types=1);

namespace App\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Attribute\Argument;
use Symfony\Component\Console\Attribute\Option;

#[AsCommand(name: 'app:greet')]
final class GreetCommand extends Command
{
    // New in Sf8: define args via attributes
    #[Argument(description: 'Who to greet')]
    private string $name;

    #[Option(shortcut: 'u', description: 'Uppercase output')]
    private bool $uppercase = false;

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $text = "Hello, {$this->name}!";
        $output->writeln($this->uppercase ? strtoupper($text) : $text);
        return Command::SUCCESS;
    }
}

3. Service Locators и #[AutowireLocator]

<?php

declare(strict_types=1);

namespace App\Service;

use Psr\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\Attribute\AutowireLocator;

final class HandlerDispatcher
{
    public function __construct(
        #[AutowireLocator([
            'create' => CreateHandler::class,
            'update' => UpdateHandler::class,
            'delete' => DeleteHandler::class,
        ])]
        private readonly ContainerInterface $handlers,
    ) {
    }
}

4. Clock Component

<?php

declare(strict_types=1);

use Symfony\Component\Clock\ClockInterface;

final class TokenGenerator
{
    public function __construct(
        private readonly ClockInterface $clock,
    ) {
    }

    public function generate(): Token
    {
        return new Token(
            value: bin2hex(random_bytes(32)),
            expiresAt: $this->clock->now()->modify('+1 hour'),
        );
    }
}

5. Messenger как отдельная тема

В Sf7.4 Messenger был частью Miscellaneous. В Sf8 -- полноценная тема с транспортами, middleware, retry strategy.

Миграция с 7.4 на 8.0: ключевые шаги

Шаг Действие
1 Обновить PHP до 8.4+
2 Исправить все deprecation warnings в Sf7.4
3 Заменить аннотации на PHP-атрибуты
4 Удалить XML-конфигурацию, перевести на YAML
5 Обновить Security: новая система authenticators
6 Удалить использование ContainerAwareInterface
7 Обновить composer.json до symfony/*: ^8.0
8 Запустить тесты, исправить ошибки

Проверка deprecations

# Run tests with deprecation detection
SYMFONY_DEPRECATIONS_HELPER=max[total]=0 php bin/phpunit

# Show all deprecations
php bin/console debug:container --deprecations

Итоги

  • Sf8.0 требует PHP 8.4+ (property hooks, asymmetric visibility)
  • Все deprecated из Sf7.x удалены (Guard, аннотации, XML)
  • Только YAML и PHP-атрибуты для конфигурации
  • Новые возможности: #[MapRequestPayload], Console attributes, Clock
  • Service Locators (#[AutowireLocator]) -- новая тема DI
  • Messenger выделен в отдельную тему экзамена
  • Миграция: исправить все deprecated в 7.4, затем обновить до 8.0