This week, we published new articles about upcoming Symfony 8.2 features, including targeted Cache-Control, relative ordering for listeners and services, the compiled event dispatcher, the three new validation constraints, and single-use signed URLs. Meanwhile, Symfony 8.2 development continued at a rapid pace, introducing features such as workflows defined with PHP attributes, a new Lock attribute to reject or delay concurrent requests, and a RecorderHttpClient to record and replay HTTP exchanges. Finally, we unveiled more talks for SymfonyCon Warsaw 2026.
Symfony development highlights
This week, 258 pull requests were merged (160 in code, 71 in docs, 19 in UX and 8 in AI) and 82 issues were closed (33 in code, 42 in docs, 3 in UX and 4 in AI). Excluding merges, 63 authors made additions and deletions. See details for code, docs, UX and AI.
- d3919f8: [Process] fix waitUntil() missing output read by its own status updates
- 82fbe9c: [Yaml] do not treat quoted << keys as merge keys
- 1bc6505: [Messenger] fix matching AMQP publisher confirms to their message after a timeout
- 49611f7: [Security] don't copy the security token of the session into logs
- 6235cfb: [Cache, FrameworkBundle] cache the parsed expressions of # attributes
- 3c8d7d6: [SecurityBundle] fix warming up the cache of access_control expressions
- 601ce74: [HttpKernel] release the container build lock on failure path
- 1e8ead8: [AssetMapper] clean up the asset being created when a compiler fails
- 771b526: [SecurityBundle] make the expression language of #[IsGranted] lazy
- 949faad: [ExpressionLanguage] fix evaluating a parsed expression again after a null-safe operator short-circuited it
- 0217edf: [FrameworkBundle] don't let Monolog override php_errors.log: false
- 38c5ae7: [Filesystem] fix touch() with zero timestamp
- f432353: [HttpKernel] fix HttpCache serving a 500 instead of a 503 for stale ESI entries when the cache is locked
- 1b08799: [HttpKernel] fix HttpCache leaking an output buffer when an ESI fragment fails
- da096fa: [PsrHttpMessageBridge] use the empty string instead of null as an array offset
- 18f7886: [Console] build the lazy signal listeners before a signal can interrupt an autoload
- 2629d0e: [WebProfilerBundle] serve phpinfo() as plain text when PHP runs with the CLI SAPI
- ba2e860: [Process] don't let inherited SYMFONY_DOTENV_VARS override explicitly passed env vars
- db791ab: [HttpFoundation] don't warn about request input keys nested too deeply
- acc4071: [Clock] fix MockClock::sleep() with dates before 1970
- 24e1c0a: [Clock] fix new DatePoint() being one hour off during the DST fall-back hour
- 1be85cf: [Yaml] fold trailing and leading white space of multi-line quoted scalars
- 0bdf8ee: [ExpressionLanguage] fix nesting level of chained ternaries
- eced0f6: [ExpressionLanguage] bound the depth of the parsed node tree
- 2f69ab0: [ExpressionLanguage] fix discrepancies between evaluated and compiled expressions
- c0c4229: [DependencyInjection] make service locator ids reproducible
- 8e46cef: [TwigBundle] sort the templates listed by TemplateIterator
- 1ea8278: [DependencyInjection] make the ids of anonymous services independent from the project directory
- 2bd517c: [HttpFoundation] send the RFC 10036 Incremental header from EventStreamResponse
- b518954: [Runtime] fix leaked server globals in FrankenPHP worker test
- 26b3c90: [ExpressionLanguage] don't reuse an expression parsed with other flags
- a2ca9b4: [Validator] consider an empty string as valid in Week
- bd1a782: [FrameworkBundle] test the meta files written by the JsonStreamer cache warmer in debug mode
- 734ce68: [Console] fix variadic arguments and options typed with a backed enum, a uid or a date
- 457d278: [Form] keep the placeholder of a required ChoiceType selectable when a parent form is optional
- 9937a54: [FrameworkBundle] make the controller.expression_language service lazy
- 1072e56: [VarExporter] fix DeepCloner snapshots changed by later writes to an ArrayObject
- e3b770e: [Messenger] do not treat ack failures as handling failures
- 194b95e: [FrameworkBundle] inject the clock into comparison and range validators
- f4899d5: [Tui] fix text ending with invalid UTF-8 measuring 0 columns or being emptied
- 5feee05: [HttpKernel] allow using #[Serialize] on controller classes
- f8b2ffc: [JsonStreamer] keep streamable resources for the compiler passes that run after StreamablePass
- e0d18eb: [KeyManagement] restrict composite reads to configured members
- 273e48b: [KeyManagement] pin Azure key versions on writes
- 859020c: [KeyManagement] add KMIP bridge
- 686b834: [Security] dispatch RateLimitExceededEvent from LoginThrottlingListener
- 3173408: [HttpFoundation, HttpKernel] forward incremental responses without buffering them in HttpClientKernel
- b0125f6: [Console] support lists of backed enums in #[Argument] and #[Option]
- 8a493a7: [Config] rename NodeDefinition::inlineEnvVars() to resolvesAtCompileTime()
- 95ca94b: [Security, SecurityBundle] allow #[IsGranted] to vote on #[MapRequestPayload] arguments
- c42f004: [FrameworkBundle, HttpKernel] make #[RateLimit] reject requests before resolving controller arguments
- 95c7e93: [HttpKernel] allow setting a condition when the #[RateLimit] attribute should be applied
- 35de7c5: [ExpressionLanguage, FrameworkBundle, HttpKernel, Security, SecurityBundle] compile expressions when warming up the cache
- 9373c9a: [Messenger] allow specifying additional AMQP queue bindings
- 5eab3e2: [HttpKernel] throw when the expression_language.compiled tag is put on a service that is not an expression language
- 03922e1: [ExpressionLanguage] add ConstantFunctionProvider and deprecate the unrestricted constant() and enum()
- c796481: [Messenger] send batches with the Redis, MongoDB, AMPHP SQL and AMQP transports, and through the outbox
- 1997d84: [ExpressionLanguage] reuse parsed expressions instead of fetching them from the cache pool on every evaluation
- 139d67a: [WebProfilerBundle] skip the profiler link log when the profiler routes are not loaded
- 2bb7fc8: [HttpFoundation] document Request::getETags() as list<string>
- 39a6508: [Workflow] add argument $context to WorkflowInterface::getMarking()
- 7ff6f5f: [DependencyInjection] let a service declare one of its methods as a service
- 73cdaa6: [Workflow] allow to define workflow with PHP attributes
- 6001aa3: [HttpClient] add RecorderHttpClient to record and replay HTTP exchanges
- 1de136d: [Messenger] add InteropSerializer to exchange messages with another application
- 5ea1992: [TypeInfo] add Type::getMismatches() to tell where and why a value is not accepted
- 6f9f3e7: [FrameworkBundle, HttpKernel, SecurityBundle, Workflow] deprecate ExpressionCacheWarmer and fail the warmup on invalid listed expressions instead
- 409def4: [Scheduler] add the schedules configuration to make a schedule stateful or locked
- 33d4f2b: [Messenger] add WorkerRestarter and let
messenger:stop-workersstop the workers of some transports only - d8b2519: [DoctrineBridge, Serializer] denormalize into the declared Doctrine collection class
- e786255: [FrameworkBundle, HttpFoundation] deprecate saving session attributes changed without calling set()
- 42be462: [HttpKernel, Lock] add #[Lock] attribute to reject or delay concurrent requests
- d586d6e: [Messenger] allow configuring the lock factory of the deduplication middleware
- 4cf7e95: [FrameworkBundle, SecurityBundle] declare the value resolvers that must run before EntityValueResolver
- 7387fd4: [DoctrineBridge] order event listeners with before/after constraints
- c3830aa: [Messenger] order handlers with before/after constraints
- d32d757: [Clock] speed up DatePoint and MonotonicClock
- 31ed3bf: [DependencyInjection] log the env vars resolved while the container is compiled
- 90fec98: [Security, SecurityBundle] order firewall listeners with before/after constraints
- e590ce0: [Workflow] add #[Place(initial: true)] to define the initial marking
- c617a98: [Messenger] keep the sender stamps of the messages stored in an outbox
- dd1d713: [FrameworkBundle, HttpKernel, SecurityBundle, Workflow] add the
lint:expressionscommand - 9971ab4: [Messenger] forward only the messages received from the outbox of their target
- 9d9fec6: [HttpFoundation, HttpKernel] treat 308 like 301 for caching
- 59e7da2: [Messenger] do not attribute settlement failures to the dispatched message
- a75608c: [MonologBridge] filter the logs written to the console by channel
- 6183043: [FrameworkBundle, HttpKernel] add LockValueResolver to inject the lock acquired by the Lock attribute
- b5e7742: [ExpressionLanguage] fix the fallback of CompiledExpressionLanguage
- c0d7f14: [Security] log the access tokens rejected by OidcTokenHandler at the debug level
- d4f0e27: [Workflow] allow extending the AsWorkflow, Place and Transition attributes
- b419636: [ExpressionLanguage] add ExceptionInterface
- b741346: [Config] make cache meta files reproducible
- e42875f: [Config, TwigBundle] avoid redundant directory traversal when discovering templates and checking resources
- 4b402cd: [Console] wrap exception messages on word boundaries
Symfony UX development highlights
- b9b03f5: [Toolkit] fix the kit linter crashing on Twig 4
- 019871a: [Toolkit] fix the drawer snapshots broken by twig-tailwind-extra 1.5.0
- 443dc9f: [Toolkit, Shadcn] add toast recipe
- 243862c: [CalendarLink] fix UTC detection for VTIMEZONE
- 5e9614c: [Notify] add support for MercureBundle ^0.5 and Mercure ^0.8
- 14b9ca5: [Toolkit, Shadcn] fix a caller's aria-label being ignored on pagination, breadcrumb and questionnaire
- 262c26b: [Notify] add support for MercureBundle ^0.6
- f14d1f6: [Turbo] add support for MercureBundle ^0.6 and Mercure ^0.9
- b013fd9: [TwigComponent] skip mount hooks on anonymous components when nobody listens to mount events
- 697f5de: [TwigComponent] find the host template of embedded blocks without scanning the call stack
- b51158e: [Icons] cache the rendered HTML of repeated icons in IconRenderer
- 31d53a2: [TwigComponent] avoid copying the render variables when nobody listens to PreRenderEvent
- defb393: [LiveComponent] sort the template map written by the cache warmer
- cb6101b: [LiveComponent] reset the deterministic id counters between requests
- 7da41b9: [Image] add KeyCDN Secure Token support
- c7050de: [Toolkit, Shadcn] fix the sheet width and the overlay exit transition
Symfony AI development highlights
- 3f38535: [Platform] guard that every result type is classified for assistant content
- 64bbcde: [Agent] add Progress stage constants
- cf37f28: [Platform, Generic] count streaming usage snapshots once per request
- 9a78e47: [Platform] allow nested items and properties in the JsonSchema type
- 55278a8: [Platform] use impacts instead of carbon for Albert
Newest issues and pull requests
- [RFC] A Store component: stop using the cache as a persistent storage
- [JsonSchema] Add the component
- [Validator] Add the skipOnEmpty option to the Expression constraint
- [KeyManagement] Make reading a self-contained payload a stated choice
- [KeyManagement] Have the Doctrine type authenticate the context it knows
Symfony CLI
Symfony CLI is a must-have tool when developing Symfony applications on your local machine. It includes the Symfony Local Server, the best way to run local Symfony applications. This week Symfony CLI released its new 5.22.0 and 5.21.0 versions with the following changes:
- Tail application logs when the project path contains glob characters (@fabpot)
- Fetch Upsun service versions and PHP extensions on demand (@fabpot)
- Remove duplicate PHP extensions from generated cloud configuration (@fabpot)
- Only replay the latest application log file and follow the other ones once written to (@fabpot)
- Prefer the TCP mapping when a Docker port is also published over UDP (@fabpot)
- Compare Docker Compose service versions semantically (@fabpot)
- Let .env files override variables loaded from .env files by a parent process (@fabpot)
- Resolve relative watched paths from the project directory in
server:status(@fabpot) - Accept bracketed IPv6 addresses for the server --listen-ip flag again (@fabpot)
- Let variables computed from Docker or tunnels win over exported ones again (@fabpot)
- Do not log preload links as errors when the connection does not support push (@fabpot)
- Detect the ini scan directory from the PHP binary actually run (@fabpot)
- Only add a trailing separator to watched directories in
server:status(@fabpot) - Load the project php.ini in local web server PHP workers again (@fabpot)
- Use t.Setenv in the Docker Compose services test (@fabpot)
- Unescape the ini scan directory reported by php-cgi's HTML phpinfo output (@fabpot)
- Render cloud configuration templates before writing them so a failure never leaves a truncated file (@fabpot)
- Mirror Symfony Dotenv for an exported empty APP_ENV and the local environment (@fabpot)
- Map Docker Compose service versions to a version Upsun supports instead of keeping retired or unknown ones (@fabpot)
- Load nothing from .env files when the project has none instead of reading the current directory and forcing APP_ENV=dev (@fabpot)
- List APP_ENV in SYMFONY_DOTENV_VARS when loaded from .env files so nested runs do not inherit it (@fabpot)
- Let .env files override variables loaded from .env files by a parent process when looking up a single variable (@fabpot)
- Keep APP_ENV out of SYMFONY_DOTENV_VARS so --env and PHPUnit's forced APP_ENV win over .env files (@fabpot)
- Identify the CLI with a User-Agent header when querying the Upsun meta registry (@fabpot)
- Fetch Upsun service versions and PHP extensions on demand from the Upsun meta registry (@fabpot)
- Drop variables computed from Docker or tunnels from an inherited SYMFONY_DOTENV_VARS so Dotenv cannot replace them (@fabpot)
- Document precisely how .env loading differs from Symfony Dotenv and interacts with Docker and PHPUnit (@fabpot)
- Document how the CLI builds the environment of PHP processes (@fabpot)
- Detect the ini scan directory from the PHP binary actually run instead of the CLI one (@fabpot)
- Compare Docker Compose service versions semantically instead of as strings (@fabpot)
- Add helpful error message when home directory is unwritable (@crydotsnake, @fabpot)
- fix: let system level set env win (@shyim)
- Tail all application log files instead of only the dev one (@fabpot)
- Advertise local server config, workers and logs on server start (@crydotsnake, @fabpot)
- Add an opt-in proxy serving the Docker Mercure hub on the web server origin (@fabpot)
- Add first-class Meilisearch support to Docker services detection (@fabpot)
- Send a Server header from the local web server and the proxy (@fabpot)
- Add beanstalkd support to Docker services detection (@jmsche)
- Detect Mailtrap Local as a mail catcher (@tsokolovs, @fabpot)
- Quote values in
var:export--multiline when needed (@nussjustin-hmmh, @fabpot) - Let .env.local and environment-specific .env files override .env values (@fabpot)
- Let exported environment variables take precedence over .env files (@shyim, @nussjustin-hmmh, @fabpot)
- Honor the Mercure SERVER_NAME when exposing the Mercure hub URL (@fabpot)
- Fix PHP binary symlinks failing with file exists when the target path contains a symlink (@fabpot)
- Pass the completing shell to Symfony Console completion instead of relying on SHELL (@fabpot)
- Honor Doctrine default_connection when reading the database server version (@fabpot)
- Allow IPv6 addresses for the server --listen-ip flag (@fabpot)
- Parse Symfony log timestamps with a negative timezone offset (@fabpot)
- Initialize new config directory to store binary instead of the legacy one (@crydotsnake)
- Make sure application log uses local timezone (@crydotsnake)
- Use PidFile.Command() to display the worker command (@fabpot)
- Remove mail catcher test that does not exercise the detection (@fabpot)
- Quote values needing it in
var:export--multiline instead of adding a --quote flag (@fabpot) - Preflight release credentials and recover packages (@fabpot)
- Prefer existing env vars over values from .env files (@nussjustin-hmmh)
- Point the temp directory error at the CLI directory instead of the home directory (@fabpot)
- Let exported variables override the project environment in
lsp:check(@fabpot) - Keep exported variables out of SYMFONY_DOTENV_VARS, even when empty (@fabpot)
- Detect Mailtrap Local as a mail catcher (@tsokolovs)
- Initialize new config directory to store binary instead of the legacy one (@crydotsnake)
- Make sure application log uses local timezone (@crydotsnake)
- Advertise local server config, workers and logs on server start (@crydotsnake)
- Add helpful error message when home directory is unwritable (@crydotsnake)
- Add flag for quoting variables exported in
var:export(@nussjustin-hmmh)
Symfony Jobs
These are some of the most recent Symfony job offers:
-
Symfony Developer at Steward
Part-time / Temporary - $8,700 – $13,000 / month
Full remote
View details
You can publish a Symfony job offer for free on symfony.com.
SymfonyCasts Updates
SymfonyCasts is the official way to learn Symfony. Select a track for a guided path through 100+ video tutorial courses about Symfony, PHP and JavaScript.
This week, SymfonyCasts published the following updates:
They talked about us
- The Mate Journey: how an idea became a tool
- Your Symfony Logs Are a Shadow Database of Personal Data
- RAG beyond hello world: retrieval is a pipeline
- Applying Kaizen Thinking to Symfony Debugging
- Indexing sets the ceiling for retrieval
- Don't blame AI before you check your architecture
- What This Architecture Cost, Measured Rather Than Argued
- PicassoBundle : le composant image qui manquait à Symfony
- L'authentification à double facteur avec Symfony (2FA)
- Как мы перестраивали e‑commerce‑платформу в production: от legacy‑системы к Next.js и Symfony
Upcoming Symfony Events
- Symfony/PHP Meetup Mannheim by SensioLabs: Mannheim, Germany (October 22, 2026)
- Meetup #1 - Symfony Québec: Montréal, Canada (October 28, 2026)
Call to Action
- Follow Symfony on X, on Mastodon, on Bluesky and on Threads and share this article.
- Subscribe to the Symfony blog RSS and never miss a Symfony story again.