This PR was merged into the 8.1 branch.
Discussion
----------
[JsonStreamer] Support date time timezone
| Q | A
| ------------- | ---
| Branch? | 8.1
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Issues | Fix#63099
| License | MIT
Add timezone support to `DateTimeValueObjectTransformer`, similar to what `DateTimeNormalizer` already does in the Serializer component.
A `TIMEZONE_KEY` option can be passed (as a `\DateTimeZone` or `string`) to convert the timezone when encoding/decoding datetimes.
Commits
-------
7a18ee1e77 [JsonStreamer] Add timezone support to DateTimeValueObjectTransformer
This PR was merged into the 8.1 branch.
Discussion
----------
[JsonStreamer] Use composer package for RFC 8259 tests
| Q | A
| ------------- | ---
| Branch? | 8.1
| Bug fix? | no
| New feature? | no
| Deprecations? | no
| Issues | -
| License | MIT
Replaces the hardcoded test cases in `LexerTest` with the [nst/JSONTestSuite](https://github.com/nst/JSONTestSuite) fetched via Composer, following the same approach as #62761 (JsonPath) and #62642 (Yaml).
Commits
-------
046e795c0b [JsonStreamer] Use composer package for RFC 8259 compliance tests
This PR was merged into the 8.1 branch.
Discussion
----------
[Console] Fix allowing invalid `#[Autowire]` references in command arguments
| Q | A
| ------------- | ---
| Branch? | 8.1
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Issues | Related to #63697
| License | MIT
If a command argument has `#[Autowire]` attribute, it should be validated build-time instead of runtime.
Commits
-------
56f4b1f44c [Console] Fix allowing invalid `#[Autowire]` references in command arguments
This PR was merged into the 8.1 branch.
Discussion
----------
[Console] Add validation constraints support to `#[MapInput]`
| Q | A
| ------------- | ---
| Branch? | 8.1
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Issues | -
| License | MIT
This adds automatic validation of `#[MapInput]` DTOs that uses Validator constraints (aligning with `#[MapRequestPayload]` for HTTP controllers).
When Validator is available, constraints on DTO properties are automatically enforced after the input is resolved. When it is not, they are ignored the same as today.
```php
use Symfony\Component\Console\Attribute\Argument;
use Symfony\Component\Console\Attribute\MapInput;
use Symfony\Component\Console\Attribute\Option;
use Symfony\Component\Validator\Constraints as Assert;
class CreateUserInput
{
#[Argument]
#[Assert\NotBlank]
public string $name;
#[Option]
#[Assert\Email]
public ?string $email = null;
}
#[AsCommand('app:create-user')]
class CreateUserCommand
{
public function __invoke(#[MapInput] CreateUserInput $input): int {
// validated
}
}
```
With validation groups:
```php
public function __invoke(
#[MapInput(validationGroups: ['registration'])] CreateUserInput $input
): int {
```
On failure, a `InputValidationFailedException` (wrapping the original Validator's exception that includes the constraint violations' list) is thrown.
Commits
-------
9ceb0fb59c [Console] Add validation constraints support to `#[MapInput]`
This PR was squashed before being merged into the 8.1 branch.
Discussion
----------
[VarExporter] Fix DeepCloner crash with objects using __serialize()
| Q | A
| ------------- | ---
| Branch? | 8.1
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Issues | Fix#63699
| License | MIT
## Summary
Fixes a crash when cloning objects that use `__serialize()/__unserialize()` (like `DateTimeImmutable`), particularly affecting PHP 8.5 where `__wakeup()` is deprecated.
## Problem
When cloning objects with `__serialize()/__unserialize()`:
1. The property data format is flat (`['date' => '...']`) rather than scoped (`['Scope' => ['name' => value]]`)
2. In some cases, `__unserialize()` detection via `method_exists()` could fail for internal classes
3. This caused the flat data to reach the property iteration loop, triggering: "foreach() argument must be of type array|object, string given"
## Solution (2 commits)
### Commit 1: Defensive fix in `DeepCloner.php`
- Adds a type check to skip non-array values in the property loop
- Prevents the crash even if flat `__serialize()` data reaches the loop
- Adds tests for `DateTimeImmutable` cloning scenarios
### Commit 2: Root cause fix in `Exporter.php`
- When `__serialize()` is called, ensures `__unserialize()` is properly detected
- Uses both `method_exists()` AND `ReflectionClass::hasMethod()` for robustness
- Properly updates the cache when `__unserialize()` is detected via reflection
## Test plan
- [ ] Run existing VarExporter tests
- [ ] Test with PHP 8.5 to validate DateTimeImmutable handling
- [ ] Verify the reproduction case from #63699 is fixed
Commits
-------
cb978cd1e3 [VarExporter] Fix DeepCloner crash with objects using __serialize()
This PR was submitted for the 6.4 branch but it was merged into the 8.1 branch instead.
Discussion
----------
[Form] fix deprecation notice in `ValidatorExtensionTest::testPropertiesInitializedWithEarlyReturn` test
| Q | A
| ------------- | ---
| Branch? | 8.1
| Bug fix? | no
| New feature? | no
| Deprecations? | no
| Issues | Fix #...
| License | MIT
Fix deprecation notice triggered in `ValidatorExtensionTest::testPropertiesInitializedWithEarlyReturn`.
The second parameter isn’t necessary for this test, so it can be safely removed.
However, just to be sure, could you confirm this, `@SeverinGloeckle` 🙏?
Deprecation notice screenshot in 8.1
<img width="843" height="400" alt="image" src="https://github.com/user-attachments/assets/e7e2c913-481c-4a94-8c95-0ab7f081ede2" />
Also the [link](https://github.com/symfony/symfony/actions/runs/23364832411/job/67976430562?pr=63712) to that error
Commits
-------
0f89610e87 [Form] fix deprecation notice in `ValidatorExtensionTest::testPropertiesInitializedWithEarlyReturn` test
This PR was merged into the 8.1 branch.
Discussion
----------
[Console] Add optional PSR container parameter to `Application`
| Q | A
| ------------- | ---
| Branch? | 8.1
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Issues |
| License | MIT
This PR adds an optional `$container` parameter (PSR `ContainerInterface`) to `Console\Application`.
When a container is provided, the application automatically wires:
- `event_dispatcher` → `setDispatcher()`
- `console.argument_resolver` → `setArgumentResolver()`
- `console.command_loader` → `setCommandLoader()`
- `console.command.ids` parameter → eagerly loaded commands (when using Symfony's `ContainerInterface`)
- `services_resetter` → `reset()`
- `kernel.environment` / `kernel.debug` parameters → `getLongVersion()` display
This is the first building block toward enabling pure console applications with dependency injection, without requiring HttpKernel or FrameworkBundle. The wiring logic currently duplicated in `FrameworkBundle\Console\Application` becomes available to any PSR container.
```php
$container = /* any PSR ContainerInterface */;
$app = new Application('my-cli', '1.0', $container);
$app->run();
```
All existing behavior is preserved when no container is passed.
Commits
-------
3753b91ac1 [Console] Add optional PSR container parameter to Application
This PR was merged into the 8.1 branch.
Discussion
----------
[Process] Widen the type of `Process::$commandline`
| Q | A
| ------------- | ---
| Branch? | 8.1
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Issues | Follows #62343
| License | MIT
In #62343, we've narrowed the type of of the `$command` array that needs to be passed to the constructor of `Process` to `list<string>`. However, when processing that array, we pipe it through `array_values()` anyway.
This change created some unnecessary busywork in a codebase I'm working on because that codebase didn't fully ensure that the arrays passed to `Process` is actually a list. I'd like to widen the type to `string[]`.
Commits
-------
c990442da8 [Process] Widen the type of Process::$commandline
This PR was merged into the 8.1 branch.
Discussion
----------
[Console] Expose the original input arguments and options and to unparse options
| Q | A
| ------------- | ---
| Branch? | 8.1
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Issues | None
| License | MIT
Adds `RawInputInterface` (extending `InputInterface`) with three methods to support forwarding parsed input to child processes:
- `getRawArguments()`: returns arguments without default values merged in
- `getRawOptions()`: returns options without default values merged in
- `unparse(?array $optionNames = null): array`: converts parsed options back to their CLI representation (`['--opt=value', '--flag']`), suitable for passing to `Process`
`Input` (the base class for `ArgvInput`, `ArrayInput`, `StringInput`) implements the new interface. The interface is also wired into the argument resolver, invokable commands, DI, and `SymfonyRuntime` so that commands can type-hint `RawInputInterface` directly.
### Use case
Forwarding the current command's input to a child process, e.g. in [console-parallelization](https://github.com/webmozarts/console-parallelization):
```php
$options = $input->getRawOptions();
unset($options['main-process-only-option']);
$options['child'] = true;
$process = new Process([
PHP_BINARY, 'bin/console', 'my:command',
...$input->getRawArguments(),
...$input->unparse(array_keys($options)),
]);
```
### Design notes
- **No BC break**: `InputInterface` is untouched. `RawInputInterface extends InputInterface` so type-hinting it gives access to the full input API.
- **`unparse()` returns `list<string>`** (not a concatenated string) so each element maps to a single process argument — no double-escaping.
- **Default values are excluded** from `getRawArguments()`/`getRawOptions()` so only explicitly passed input is forwarded.
Commits
-------
6475fabac0 [Console] Expose the original input arguments and options and to unparse options
This PR was merged into the 8.1 branch.
Discussion
----------
[HttpKernel] Add option to map empty data with `MapQueryString` and `MapRequestPayload`
| Q | A
| ------------- | ---
| Branch? | 8.1
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Issues | -
| License | MIT
When `#[MapQueryString]` or `#[MapRequestPayload]` is used on a nullable/default-valued parameter and the query string or request body is empty, the resolver short-circuits and returns `null` without ever calling the serializer. This prevents custom denormalizers from constructing the object.
This PR adds `bool $mapWhenEmpty = false` to both attributes. When `true`, the resolver passes `[]` to `denormalize()` even when no data is present, giving custom denormalizers a chance to populate the DTO.
```php
public function __construct(
#[MapRequestPayload(mapWhenEmpty: true)] SearchFilters $filters,
) {}
```
**Use case:** a DTO where some fields come from the request and others are injected by a custom denormalizer (e.g. from the security context or session):
```php
class SearchFilters {
public function __construct(
public ?string $keyword = null, // from query string
public int $userId = 0, // set by a custom denormalizer
) {}
}
```
Without `mapWhenEmpty`, an empty query string yields `null`, the denormalizer never runs. With `mapWhenEmpty: true`, denormalization proceeds and the custom denormalizer can populate `$userId`.
Commits
-------
5117bb6f17 [HttpKernel] Add argument `$mapWhenEmpty` to `MapQueryString` and `MapRequestPayload` for always attempting denormalization with empty query and request payload
This PR was merged into the 8.1 branch.
Discussion
----------
[Messenger] Add AmqpPriorityStamp for per-message priority on AMQP transport
| Q | A
| ------------- | ---
| Branch? | 8.1
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Tickets | Fix#41573
| License | MIT
| Doc PR | symfony/symfony-docs#15452
Similarly to #59273 for beanstalkd, this PR adds `AmqpPriorityStamp`, a transport-scoped stamp that sets the AMQP `priority` message attribute when sending through the AMQP transport.
```php
use Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpPriorityStamp;
$bus->dispatch($message, [new AmqpPriorityStamp(5)]);
```
The stamp is picked up by `AmqpSender`, which merges the `priority` attribute into the outgoing `AmqpStamp` before publishing.
Per-message priority is inherently transport-specific. Redis, Doctrine/DBAL, and SQS transports have no native priority concept - a generic stamp would silently become a no-op on those. By scoping the stamp to the AMQP bridge (mirroring the existing `BeanstalkdPriorityStamp` introduced in 8.1), the API is honest about what it does and where.
RabbitMQ queues must be declared with the `x-max-priority` argument *before* priority-bearing messages arrive. This is already supported via the transport options:
```yaml
transports:
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
options:
queues:
messages:
arguments:
x-max-priority: 10
```
`x-max-priority` is already in `Connection::QUEUE_ARGUMENT_ALLOWLIST`, so no additional infrastructure changes are needed.
Pro-Tip: For true priority routing across all transports, configure separate queues and consume them in priority order with `messenger:consume high_priority low_priority`. This PR does not replace that pattern. It complements it for cases where fine-grained per-message priority within a single AMQP queue is needed.
Commits
-------
23991d4abd [Messenger] Add AmqpPriorityStamp for per-message priority on AMQP transport
* 8.0:
Fix merge
[VarDumper] Wrong dumper output for Accept: aplication/json requests
[HttpKernel] Reset router locale to default when finishing main request
Only decrement pendingRequests when it's more than zero
[Dotenv] Fix self-referencing variables with defaults and env key resolution during deferred expansion
Improve Bulgarian translations in validators.bg.xlf
[Cache] Fix ChainAdapter ignoring item expiry when propagating to earlier adapters
[Form] Fix typed property initialization in ValidatorExtension
[Messenger] Fix duplicate pending messages in Redis transport with batch handlers
Fix deprecation notices for "@method" annotations when implementing interfaces directly
* 7.4:
[VarDumper] Wrong dumper output for Accept: aplication/json requests
[HttpKernel] Reset router locale to default when finishing main request
Only decrement pendingRequests when it's more than zero
[Dotenv] Fix self-referencing variables with defaults and env key resolution during deferred expansion
Improve Bulgarian translations in validators.bg.xlf
[Cache] Fix ChainAdapter ignoring item expiry when propagating to earlier adapters
[Form] Fix typed property initialization in ValidatorExtension
[Messenger] Fix duplicate pending messages in Redis transport with batch handlers
Fix deprecation notices for "@method" annotations when implementing interfaces directly
This PR was merged into the 7.4 branch.
Discussion
----------
[VarDumper] Wrong dumper output for Accept: aplication/json requests
| Q | A
| ------------- | ---
| Branch? | 7.4
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Issues | Fix#63601
| License | MIT
`CliDumper::$defaultOutput` is `php://stdout`, which in PHP-FPM is redirected to the error log — not the HTTP response body. Only `php://output` goes through PHP's output buffering into the actual response.
The dumper selection fell back to `CliDumper` for any Accept header that wasn't `html` or `*/*`, so `dump()`/`dd()` output silently disappeared for JSON API requests under PHP-FPM. The same issue also affected the `server`/`tcp` format fallback dumper.
This PR fixes the issue by using `new CliDumper('php://output')` in web SAPI contexts where the Accept header is not HTML/`*/*`, and refactors the `switch(true)` into `match($format)` + an extracted `selectDumperForAccept()` method.
Commits
-------
f663a6ab95 [VarDumper] Wrong dumper output for Accept: aplication/json requests
* 6.4:
[HttpKernel] Reset router locale to default when finishing main request
Only decrement pendingRequests when it's more than zero
[Dotenv] Fix self-referencing variables with defaults and env key resolution during deferred expansion
Improve Bulgarian translations in validators.bg.xlf
[Cache] Fix ChainAdapter ignoring item expiry when propagating to earlier adapters
[Form] Fix typed property initialization in ValidatorExtension
[Messenger] Fix duplicate pending messages in Redis transport with batch handlers
Fix deprecation notices for "@method" annotations when implementing interfaces directly
This PR was merged into the 6.4 branch.
Discussion
----------
[Dotenv] Fix self-referencing variables with defaults and env key resolution during deferred expansion
| Q | A
| ------------- | ---
| Branch? | 6.4
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Issues | Fix#63664, Fix#63668
| License | MIT
The deferred variable expansion introduced in #63496 broke two things:
1. Self-referencing variables with defaults no longer resolve
A common pattern for providing defaults that can be overridden by host environment variables:
```
MY_VAR="${MY_VAR:-default_value}"
DB_HOST="${DB_HOST:-localhost}"
```
After deferred expansion, `populate()` writes the raw string `${MY_VAR:-default_value}` into `$_ENV['MY_VAR']` before resolution. When `resolveLoadedVars()` later tries to resolve it, the lookup finds that non-empty raw string in `$_ENV` and never triggers the default.
2. Env key with variable references breaks `loadEnv()` file selection
`loadEnv()` reads `APP_ENV` between `doLoad()` and `resolveLoadedVars()` to determine which additional `.env.*` files to load. With deferred expansion, the raw unresolved value (e.g. `${APP_ENV:-dev}`) is used for file selection, so `.env.dev` is never loaded.
Commits
-------
92270233e0 [Dotenv] Fix self-referencing variables with defaults and env key resolution during deferred expansion
This PR was squashed before being merged into the 6.4 branch.
Discussion
----------
[HttpKernel] Reset router locale to default when finishing main request
| Q | A
| ------------- | ---
| Branch? | 6.4
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Issues | Fix#61762
| License | MIT
Commits
-------
d70f4c2bd5 [HttpKernel] Reset router locale to default when finishing main request
This PR was merged into the 6.4 branch.
Discussion
----------
[WebProfilerBundle] Only decrement pendingRequests when it's more than zero
| Q | A
| ------------- | ---
| Branch? | 6.4
| Bug fix? | yes
| New feature? | no <!-- if yes, also update src/**/CHANGELOG.md -->
| Deprecations? | no <!-- if yes, also update UPGRADE-*.md and src/**/CHANGELOG.md -->
| Issues | Fix#63667 <!-- prefix each issue number with "Fix #"; no need to create an issue if none exists, explain below -->
| License | MIT
This PR fixes the linked issue by ensuring the `pendingRequests` variable never goes below zero.
Commits
-------
1dfe48b014 Only decrement pendingRequests when it's more than zero
This PR was merged into the 8.1 branch.
Discussion
----------
[Runtime] Add FrankenPhpWorkerResponseRunner for simple response return
| Q | A
| ------------- | ---
| Branch? | 8.1
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Issues | -
| License | MIT
This introduces `FrankenPhpWorkerResponseRunner`, a companion to the existing `FrankenPhpWorkerRunner`. While `FrankenPhpWorkerRunner` handles the full kernel lifecycle (request creation, handling,
termination), this new runner is designed for cases where the application already provides a `Response` object — it simply sends it within the FrankenPHP worker loop.
Usage :
```php
// public/index.php
use App\Kernel;
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
return function (array $context) {
if($context['APP_MAINTENANCE']) {
return new Response('....', 503);
}
$request = \Symfony\Component\HttpFoundation\Request::createFromGlobals();
return $kernel->handle($request);
};
Commits
-------
9b80296b32 Add FrankenPhpWorkerResponseRunner for simple response return in symfony/runtime
This PR was squashed before being merged into the 6.4 branch.
Discussion
----------
Improve Bulgarian translations in validators.bg.xlf
| Q | A
| ------------- | ---
| Branch? | 6.4
| Bug fix? | yes
| New feature? | no <!-- if yes, also update src/**/CHANGELOG.md -->
| Deprecations? | no <!-- if yes, also update UPGRADE-*.md and src/**/CHANGELOG.md -->
| Issues | Fix#59411 <!-- prefix each issue number with "Fix #"; no need to create an issue if none exists, explain below -->
| License | MIT
## Description
Improved several Bulgarian translations in `validators.bg.xlf`.
Changes include:
- Simplified wording (e.g. "Тази стойност" → "Стойността")
- Improved wording for pixel validation messages
- Fixed spacing issue with `px`
- Made translations more consistent with the existing Bulgarian style
Commits
-------
0e245874fd Improve Bulgarian translations in validators.bg.xlf
This PR was merged into the 6.4 branch.
Discussion
----------
[Cache] Fix ChainAdapter ignoring item expiry when propagating to earlier adapters
| Q | A
| ------------- | ---
| Branch? | 6.4
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Issues | Fix#59947
| License | MIT
When `ChainAdapter::getItem()` found a cache miss in adapter N and a hit in adapter N+1, it copied the item back into the earlier adapter via `syncItem()`. However, `syncItem` relied on `metadata['expiry']` to restore the original TTL and that metadata was never stored for items that had an explicit TTL but no tags.
The result: every such item was re-stored with the chain's `defaultLifetime` instead of its original expiry. An item set with `expiresAfter(2)` could end up living 100 seconds in the primary cache.
Commits
-------
61e994fa4f [Cache] Fix ChainAdapter ignoring item expiry when propagating to earlier adapters
This PR was merged into the 6.4 branch.
Discussion
----------
[Form] Fix typed property initialization in ValidatorExtension
| Q | A
| ------------- | ---
| Branch? | 6.4
| Bug fix? | yes
| New feature? | no
| Deprecations? |no
| License | MIT
The early return in the ValidatorExtension constructor added in 37b2f68f4c prevents typed properties from being initialized when a Form constraint already existed, causing PHP errors when the extension is used:
```
Typed property Symfony\Component\Form\Extension\Validator\ValidatorExtension::$validator
must not be accessed before initialization
```
## Changes Made
* Code Fix: Moved property initialization before the early return check in the constructor.
* Test Addition: Added `testPropertiesInitializedWithEarlyReturn()` to verify the fix.
## Solution
This fix ensures:
* All properties are initialized before any early return.
* The original duplicate constraint prevention still works.
* The extension remains fully functional in all use cases.
## Testing
Added test case that triggers the early return condition and verifies that the extension remains functional to prevent future regressions.
Commits
-------
77676c8cc9 [Form] Fix typed property initialization in ValidatorExtension
This PR was merged into the 6.4 branch.
Discussion
----------
[Messenger] Fix duplicate pending messages in Redis transport with batch handlers
| Q | A
| ------------- | ---
| Branch? | 6.4
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Issues | Fix#44400, Fix#49023
| License | MIT
The Redis transport is currently broken when used with `BatchHandlerInterface`. The root cause is in how `Connection::get()` reads pending messages.
When a batch handler calls `get()` repeatedly to accumulate messages before flushing, the transport always passes ID `0` to `XREADGROUP`, which makes Redis return pending entries from the very beginning. Since previously fetched messages haven't been acked yet (that's the whole point of batching), the first pending message is returned over and over. This fills the batch with duplicates of the same message, and when the handler tries to ack them, all but the first fail with `Could not acknowledge redis message`.
The issue has been open since 2021 and people have been working around it with custom transport decorators, message deduplication via UUIDs, and other workarounds — none of which should be necessary.
### The fix
Replace the boolean `$couldHavePendingMessages` flag with two things:
- A **cursor** (`$lastPendingMessageId`) that advances past each returned message, so successive `XREADGROUP` calls iterate through the pending list instead of restarting from the beginning every time.
- An **in-flight registry** (`$inflightIds`) that tracks which message IDs have already been delivered to the worker. When a claim resets the cursor back to `0`, any previously delivered messages are skipped. IDs are removed from the registry when `ack()` or `reject()` is called.
This also fixes#49023: the old code unconditionally advanced `$nextClaim` at the end of `claimOldPendingMessages()`, even after a successful claim. That meant the worker had to wait the full `claim_interval` (default 60s) before it could claim the next abandoned message. Now the timer only advances when there is genuinely nothing left to claim, so multiple abandoned messages can be recovered in sequence.
The `get()` method has been refactored from a single method with recursive self-calls into smaller focused methods (`getPendingMessage()`, `getNewMessage()`, `handleDelayedMessages()`, `xReadGroup()`), which makes the control flow easier to follow and test.
Commits
-------
9f5574568a [Messenger] Fix duplicate pending messages in Redis transport with batch handlers
This PR was merged into the 8.1 branch.
Discussion
----------
[Messenger] Allow configuring the service reset interval in the `messenger:consume` command via the `--no-reset` option
| Q | A
| ------------- | ---
| Branch? | 8.1
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Issues | -
| License | MIT
This PR extends the `--no-reset` option of the `messenger:consume` command to optionally accept a numeric interval, allowing services to be reset every N messages instead of the current all-or-nothing behavior.
`--no-reset` now accepts an optional integer value:
- `--no-reset` (no value) — disables service resetting entirely (same as before)
- `--no-reset=100` — resets services every 100 messages
- Without the flag — resets after every message (default, unchanged)
This is useful when resetting after every message is expensive, but never resetting leads to memory leaks or stale state.
Commits
-------
def4af2d00 [Messenger] Allow configuring the service reset interval in the `messenger:consume` command via the `--no-reset` option
This PR was merged into the 8.1 branch.
Discussion
----------
[Messenger] Add `MessageExecutionStrategyInterface` and refactor `Worker` to use it
| Q | A
| ------------- | ---
| Branch? | 8.1
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Issues | -
| License | MIT
This PR extracts the message dispatching logic from `Worker` into a `MessageExecutionStrategyInterface`, making the execution model pluggable.
Today, `Worker` handles messages synchronously inline. This refactoring introduces:
- **`MessageExecutionStrategyInterface`** — defines `execute()`, `wait()`, `flush()`, `shouldPauseConsumption()`, and `shutdown()`, giving full control over how and when messages are dispatched and acknowledged.
- **`SyncMessageExecutionStrategy`** — the default implementation, preserving the current synchronous behavior.
- **`DeferredBatchMessageQueue`** — replaces the raw `\SplObjectStorage` used for batch handler tracking with a dedicated class that supports time-based flushing.
`Worker` now delegates to the strategy for dispatch and accepts an optional `?MessageExecutionStrategyInterface` constructor argument (defaults to `SyncMessageExecutionStrategy`). Existing behavior is unchanged.
This is a preparatory step for adding parallel message processing via a `--concurrency` option, see https://github.com/symfony/symfony/pull/63650.
Commits
-------
ce5ffb39bb [Messenger] Add `MessageExecutionStrategyInterface` and refactor `Worker` to use it
This PR was merged into the 8.1 branch.
Discussion
----------
[Messenger] Add a `--fetch-size` option to the `messenger:consume` command to control how many messages are fetched per iteration
| Q | A
| ------------- | ---
| Branch? | 8.1
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Issues | -
| License | MIT
Related to #63650.
Currently, receivers always fetch one message at a time, which means one round-trip to the transport per message. This PR allows fetching multiple messages at once, so the transport can fulfill that in a single round-trip when supported (e.g. SQS supports up to 10 messages per `ReceiveMessage` call, Redis supports `XREADGROUP COUNT`, AMQP supports `basic_get` in a loop with a known limit, Doctrine supports `LIMIT`).
A new `--fetch-size` option is added to the `messenger:consume` command:
```
php bin/console messenger:consume async --fetch-size=8
```
Under the hood, this adds a `$fetchSize` argument to `ReceiverInterface::get()` and `QueueReceiverInterface::getFromQueues()`:
```php
// ReceiverInterface
public function get(/* int $fetchSize = 1 */): iterable;
// QueueReceiverInterface
public function getFromQueues(array $queueNames /* , int $fetchSize = 1 */): iterable;
```
The argument is added as a commented-out parameter to preserve backward compatibility with existing custom receivers.
All built-in transports (AMQP, Doctrine, Redis, SQS, Beanstalkd, InMemory, Sync, Scheduler) are updated to support the new argument.
Commits
-------
e4536ac7ef [Messenger] Add a `--fetch-size` option to the `messenger:consume` command to control how many messages are fetched per iteration
This PR was merged into the 8.1 branch.
Discussion
----------
[Contracts] Add `ContainerAwareInterface`
| Q | A
| ------------- | ---
| Branch? | 8.1
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Issues | -
| License | MIT
Related to https://github.com/symfony/symfony/pull/63650.
This PR adds a `ContainerAwareInterface` to `symfony/service-contracts`, providing a standard way for objects to expose their service container.
The immediate use case is the Messenger component: parallel workers need to bootstrap the application and access the container to resolve the message bus. Without a contract, there's no reliable way to get the container from the console `Application` object in a decoupled manner.
The interface is intentionally minimal — a single `getContainer(): ContainerInterface` method — and `FrameworkBundle\Console\Application` implements it, booting the kernel if needed.
Commits
-------
8c158e0520 [Contracts] Add `ContainerAwareInterface`