The release of Symfony 7.4 in November 2025 marks another significant milestone in the evolution of the PHP ecosystem. Continuing its bi-annual release cycle, Symfony 7.4 brings a polished set of tools designed to streamline modern web development, enhance type safety, and improve the developer experience (DX). While the community buzzes about the upcoming Symfony 8, this version introduces critical features that solve immediate problems for developers — most notably, the long-awaited Video constraint.

In this article, we will explore the new features of Symfony 7.4, with a special focus on the Video constraint, improved console commands, and architectural shifts in the HttpFoundation component. We will provide practical, copy-pasteable code examples using PHP 8.4+ syntax (including attributes and typed properties) and verify the existence of every library used.

The Star of the Show: The Video Constraint

For years, developers relying on the symfony/validator component had to use the File or Image constraints to validate uploads. While effective for documents and static images, these constraints fell short for video content. Developers often had to write custom constraints wrapping complex ffmpeg logic to check simple properties like duration, resolution, or aspect ratio.

Symfony 7.4 introduces the #[Assert\Video] constraint, a native solution that works similarly to the Image constraint but is tailored for video files.

Detailed Usage

To use this constraint, your server must have FFmpeg installed, as the validator relies on the ffprobe binary to extract metadata from video files.

The Video constraint supports a wide array of options, allowing you to enforce strict requirements on uploaded media. This is particularly crucial for platforms that process user-generated content, where inconsistent video formats can crash encoding pipelines.

Supported Options:

Safe Video Upload DTO

Here is a robust implementation using a Data Transfer Object (DTO) for a video upload form.

namespace App\Dto;

use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;

class VideoUploadInput
{
    #[Assert\NotBlank(message: 'Please select a video file.')]
    #[Assert\Video(
        maxSize: '200M',
        mimeTypes: ['video/mp4', 'video/webm', 'video/quicktime'],
        minWidth: 1280,
        minHeight: 720,
        maxWidth: 3840,
        maxHeight: 2160,
        allowPortrait: false,
        allowSquare: false,
        allowedCodecs: ['h264', 'hevc', 'vp9'],
        detectCorrupted: true,
        maxSizeMessage: 'The video is too large ({{ size }} {{ suffix }}). Allowed maximum size is {{ limit }} {{ suffix }}.',
        mimeTypesMessage: 'Please upload a valid MP4, WebM, or MOV video.'
    )]
    public ?UploadedFile $videoFile = null;

    #[Assert\NotBlank]
    #[Assert\Length(min: 10, max: 255)]
    public string $title = '';
}

Previously, validating that a video was “Landscape 1080p” required inspecting the file inside a controller or service after the validation layer had passed. Now, invalid videos are rejected immediately by the Validator component, keeping your controllers clean and your domain logic focused.

Command Line Revolution: Interactive & Typed Commands

Symfony’s Console component is the industry standard for CLI applications. Version 7.4 significantly reduces the boilerplate required to build interactive and type-safe commands.

Backed Enums in Arguments

You can now use PHP Backed Enums directly in your #[Argument] and #[Option] attributes. Symfony automatically casts the string input to the Enum case or throws a validation error if the value is invalid.

The #[MapInput] Attribute

Handling complex command arguments often leads to bloated __invoke methods. The new #[MapInput] attribute allows you to map command arguments and options directly into a DTO, similar to how #[MapRequestPayload] works in controllers.

Interactive Attributes: #[Interact] and #[Ask]

Symfony 7.4 introduces declarative interactivity. You can mark properties with #[Ask] to define the question asked when the argument is missing or use #[Interact] to define custom interaction logic.

Interactive Server Provisioning Command

namespace App\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Attribute\MapInput;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Validator\Constraints as Assert;

// 1. Define the Enum
enum Region: string {
    case US_EAST = 'us-east-1';
    case EU_WEST = 'eu-west-1';
    case ASIA_SOUTH = 'ap-south-1';
}

// 2. Define the Input DTO with #[Ask]
class ProvisionServerInput
{
    #[Assert\NotBlank]
    public string $name;

    #[Assert\NotNull]
    #[Assert\Type(Region::class)]
    // If the user omits this argument, they will be asked this question
    #[Ask(question: 'Which region should the server be deployed to?')] 
    public Region $region;

    #[Assert\Range(min: 1, max: 10)]
    public int $count = 1;
}

// 3. The Command
#[AsCommand(
    name: 'app:server:provision',
    description: 'Provisions new servers in the cloud.'
)]
class ProvisionServerCommand extends Command
{
    public function __invoke(
        SymfonyStyle $io,
        #[MapInput] ProvisionServerInput $input
    ): int {
        $io->title('Server Provisioning');

        $io->text(sprintf(
            'Provisioning %d server(s) named "%s" in region %s...',
            $input->count,
            $input->name,
            $input->region->value
        ));

        // Logic to call cloud provider API...
        
        $io->success('Provisioning started!');

        return Command::SUCCESS;
    }
}

Run php bin/console app:server:provision without arguments. Symfony will automatically prompt you for the name (standard interaction) and the region (using the Enum values as valid options), validating the input against the DTO constraints.

HttpFoundation: Cleaning Up the Request Object

The Request class is one of the oldest parts of Symfony. In 7.4, the core team has made bold moves to modernize it, deprecating legacy behaviors that encouraged loose typing.

Deprecation of Request::get()

The Request::get(‘key’) method has been a “magic” getter that checks attributes, query parameters, and body parameters in a specific order. This unpredictability is a source of bugs.

In Symfony 7.4, Request::get() is deprecated. You must now be explicit about where data comes from.

// ❌ Deprecated in 7.4
$id = $request->get('id');

// ✅ Correct in 7.4
$id = $request->query->get('id');      // From ?id=123
$id = $request->request->get('id');    // From POST body
$id = $request->attributes->get('id'); // From route {id}

Native Parsing for PUT/PATCH Requests

Historically, PHP only parsed multipart/form-data and application/x-www-form-urlencoded for POST requests. To handle PUT or PATCH file uploads, developers relied on hacks or symfony/http-client workarounds.

With PHP 8.4 and Symfony 7.4, the request_parse_body() function is utilized internally. This means Request::createFromGlobals() now correctly populates $request->request and $request->files for PUTPATCH, and even DELETE requests containing a body.

This happens automatically if you are running PHP 8.4. No config is needed.

Architectural Shift: The “Share” Directory

Scaling Symfony applications across multiple servers often involves complex cache strategies. Symfony 7.4 introduces the concept of a Share Directory (var/share), distinct from the Cache Directory (var/cache).

How to use it:

# config/services.yaml
parameters:
    # Defaults to var/share in new projects
    kernel.share_dir: '%kernel.project_dir%/var/share'

framework:
    cache:
        # App pools can now default to the share directory
        app: cache.adapter.filesystem
        default_redis_provider: 'redis://%env(REDIS_URL)%'

This separation simplifies container deployments (Docker/Kubernetes). You can mount a persistent volume to var/share while keeping var/cache ephemeral and baked into the image.

Attribute Enhancements

Attributes continue to replace YAML/XML configuration, making code more self-contained.

#[IsSignatureValid]

Validating signed URLs (e.g., for email verification or password resets) previously required injecting the UriSigner service. Now, it’s a declarative attribute.

use Symfony\Component\HttpKernel\Attribute\IsSignatureValid;

class ResetPasswordController extends AbstractController
{
    #[Route('/reset-password', name: 'app_reset_password')]
    #[IsSignatureValid(queryParameter: 'hash')] // Automatically returns 403 if invalid
    public function __invoke(Request $request): Response
    {
        // ... logic
    }
}

Union Types in #[CurrentUser]

The #[CurrentUser] attribute now supports Union Types, reflecting the reality that an application might have multiple user classes (e.g., AdminUser | AppUser).

public function dashboard(
    #[CurrentUser] AdminUser|AppUser $user
): Response {
    if ($user instanceof AdminUser) {
        // ...
    }
}

Native HTML5 Parser (PHP 8.4 Integration)

Symfony 7.4 automatically leverages the new \Dom\HTMLDocument class available in PHP 8.4. This replaces the legacy libxml-based parsing for the DomCrawler and HtmlSanitizer components.

Impact:

Conclusion

Symfony 7.4 is not just a stepping stone to version 8; it is a robust release that solves “here and now” problems for developers. The Video Constraint eliminates a common pain point in media-heavy applications, while the Command and Request improvements enforce better architectural practices.

By adopting these features, you prepare your codebase for the strict type safety of the future while enjoying immediate productivity gains.

Next Steps for Developers:

  1. Upgrade PHP: Ensure your environment is running PHP 8.4 to unlock the native HTML parser and request body features.
  2. Audit Request::get(): grep your codebase and replace these calls with explicit property access.
  3. Refactor Commands: Simplify your console commands using the new #[MapInput] and #[Ask] attributes.

Stay Ahead of the Curve

The PHP world moves fast. Don’t get left behind with legacy practices.

Connect on LinkedIn: [https://www.linkedin.com/in/matthew-mochalkin/] — I share many Symfony tips and architectural patterns.

Happy Coding with Symfony 7.4!