This week, Symfony 5.4.54, 6.4.48, 7.4.21, and 8.1.9 maintenance versions were released, along with three security fixes for HttpFoundation, ExpressionLanguage, and Yaml. We also released Symfony UX 3.6, which adds three experimental packages: UX Inspector, UX Image, and UX Router. Meanwhile, we kept publishing articles about Symfony 8.2 features, such as proving authentication methods, safer logout, attribute-based workflows, encrypted Doctrine columns, and locking controllers, and we added a new JsonSchema component. Finally, we announced more talks for SymfonyCon Warsaw 2026.

Symfony development highlights

This week, 370 pull requests were merged (257 in code, 68 in docs, 30 in UX and 15 in AI) and 94 issues were closed (44 in code, 39 in docs, 2 in UX and 9 in AI). Excluding merges, 63 authors made additions and deletions. See details for code, docs, UX and AI.

6.4 changelog:

  • ff7d848: [FrameworkBundle] fix the "Cannot unset string offsets" crash in the configuration of workflows
  • 96edbf1: [Notifier, PagerDuty] keep the user payload and send the timestamp as a string
  • 444b0d7: [HttpKernel] use the clock to resolve relative dates in DateTimeValueResolver
  • 3a4fea9: [Uid] fix the date of time-based UUIDs before 1970 that have a fractional second
  • c7f1256: [Yaml] throw a ParseException on keys starting with a NUL byte when parsing mappings as objects
  • b9cfb30: [Yaml] fix parsing timestamps with a fractional second before 1970
  • 0c52335: [Clock] fix DatePoint keeping the current time of day for dates with a relative part
  • e2ccf6b: [Security] fix using OidcTokenHandler when symfony/clock is not installed
  • ed4b1ab: [Clock] fix DatePoint being one hour off when its reference is in the DST fall-back hour
  • 8751e1d: [VarDumper] fix dumping exceptions whose trace frames contain arrays or objects in extra keys
  • 4f802e8: [Messenger] route zero-delay AMQP retries to the original queue
  • 1d4a1f5: [BrowserKit] fix parsing and path matching of cookies
  • b77fba9: [HttpKernel] release the services of the container when the kernel shuts down
  • 8d5fb4f: [DependencyInjection] fix dumping strings like "50% of 100%"
  • 013cf98: [DependencyInjection] fix ignoring env var processors after resolving env vars before compilation
  • b171ec9: [HttpClient] fix RetryableHttpClient::withOptions() changing the base URIs of the original client
  • 0352f11: [Messenger] prevent losing delayed Redis messages on migration failure
  • b09586a: [PropertyAccess] fix using custom PropertyPathInterface implementations
  • c36d807: [HttpKernel] reset the default logger between requests
  • 0e1515b: [Messenger] fix DispatchAfterCurrentBusMiddleware staying in a root dispatch after an \Error
  • f1bf4b4: [HttpClient] don't add the query option again to redirect targets in NoPrivateNetworkHttpClient
  • 58f974c: [FrameworkBundle] fix request context parameters leaking between requests
  • 8f658f2: [Console] fix nested applications exiting on signals handled by the outer application
  • 29db373: [Yaml] fix TypeErrors, a PHP warning and wrong results for !php/const keys and merge keys
  • 0ef3f27: [Form] clear the renderer state when rendering a block fails
  • 7cc3b19: [Form] fix rendering the parent block twice from a form theme block
  • f86b2b7: [VarDumper] fix source excerpts of files with CRLF line endings
  • 1709816: [String] fix UnicodeString::replace() and split() with ignorable characters like U+200B
  • 62200e4: [Console] fix how sections count and redraw their lines
  • 57f8bc9: [Messenger] fix the worker looping without sleeping just before its time limit
  • b5c9ba1: [Cache] fix PhpArrayAdapter::warmUp() when another process creates the cache directory
  • c9a3bf9: [Messenger] fix PostgreSqlConnection throwing from its destructor when the database is gone
  • 0db210c: [Messenger] fix the Doctrine transport not retrying ack() and reject() on deadlocks
  • a741f0a: [Console] fix two state bugs in OutputFormatterStyle
  • 8ed74c1: [Translation] fix FileDumper::dump() when another process creates the target directory
  • 0535ce4: [PropertyAccess] fix using custom PropertyPathInterface implementations on objects
  • 95166de: [Cache] fix RedisTagAwareAdapter returning invalidated items from Relay's memory
  • 6f2f38e: [FrameworkBundle] reset the whole request context, not only its parameters
  • 4be01dd: [TwigBridge] fix form_label() with a literal label and non-literal variables
  • 1f4f37b: [HttpKernel] don't list expired profiles in the profiler search
  • bb9d4e0: [HttpKernel] fix FileProfilerStorage not removing some expired profiles
  • 69fb3bb: [String] fix slice(), splice() and chunk() with very large offsets or lengths
  • 2adcce4: [Serializer] fix normalizing discriminated classes that have no properties
  • 852e221: [Messenger] fix the Amazon SQS connection rethrowing the same error after a failed receive
  • cffda94: [Messenger] fix the Redis transport after a lost connection
  • dd25493: [DependencyInjection] don't initialize lazy services when resetting the container
  • fdb96d9: [Form] fix debug:form listing an overridden option twice
  • 76e1b96: [Form] fix debug:form for types calling addNormalizer(), setDeprecated(), setInfo() or getInfo() on inherited options
  • 55fb4bf: [WebProfilerBundle] fix the form and translation panels with Twig 4
  • faab3cb: [DependencyInjection] release the closures that reference the container when resetting it

7.4 changelog:

  • 3beb3ed: [HttpClient] fix ThrottlingHttpClient when the response has no pause handler
  • 236bac9: [HttpClient] fix CachingHttpClient storing responses as stale when their start_time is old
  • 5847710: [Cache] fix expiresAfter() ignoring the clock of ArrayAdapter
  • 7df17fa: [Security, SecurityBundle] fix security:oidc:generate-token resolving --not-before against the real time
  • 221721a: [Notifier, Bluesky] send the creation date of posts in UTC
  • 69cfe7f: [Security] fix using OidcTokenGenerator when symfony/clock is not installed
  • 2f0851a: [Form] fix CurrencyType with active_at set to null
  • 9fa5dad: [Form] guess the enum type of private properties declared by parent classes
  • d1e28be: [Serializer, Validator] rebuild the container when attributes on non-public members change
  • e16868e: [Console] fix the usages of invokable commands registered as services
  • 2d3aced: [Messenger] fix PhpSerializer::getMessageType() when a stamp holds the message
  • b68d796: [DoctrineBridge] reset a non-lazy manager instead of emptying it
  • d3584fb: [Console] escape pipes and newlines in markdown tables
  • 3a162b9: [Messenger] fix the Beanstalkd transport using the "default" tube after a failed reconnect
  • d574758: [Notifier, Sevenio] fix response code 101 being reported as a success

8.1 changelog:

  • de2d794: [Console] use the clock to resolve relative dates in DateTimeValueResolver
  • c95a1cd: [Messenger] fix PostgreSqlNotifyOnIdleListener missing delayed messages when the timezone is ahead of UTC
  • 3ca8a10: [HttpClient] ignore negative CURLOPT_MAXREDIRS values in GuzzleHttpHandler
  • 3776973: [HttpKernel] document how to keep controller attributes when replacing the controller
  • 462bb74: [Cache, HttpKernel] fix missing #[Cache] headers on 304 responses
  • 0f6aff9: [HttpKernel] document registering after -10000 when the new controller has other parameters
  • 3bba355: [Messenger] fix the Amazon SQS connection losing buffered messages when a receive fails

8.2 changelog:

  • 6e14c85: [FrameworkBundle, Yaml] make lint:yaml validate files against their JSON Schema by default
  • 74299a0: [HttpKernel, Lock] allow {name} placeholders in the keys of the Lock and RateLimit attributes
  • 50c8b43: [Workflow] accept workflows given directly under their section in the JSON schema
  • a726e83: [FrameworkBundle] allow consumeQueuedMessages() to consume all in-memory transports
  • fe5bd4d: [FrameworkBundle, HttpKernel] allow mapping the controller result with the ObjectMapper in the Serialize attribute
  • c0e7148: [FrameworkBundle, SecurityBundle] make KernelBrowser::loginUser() work on stateless access_token firewalls
  • 5c1a024: [FrameworkBundle, HttpFoundation] make MetadataBag read the time from the clock
  • 2f7516e: [Cache] pass the clock service to the MongoDB cache adapters
  • 6aff522: [Mailer, Notifier] allow the webhook parsers to read the time from a clock
  • c3f6524: [Messenger] use the clock in the MongoDB transport and in the --failed-after/--failed-before options
  • 8d9b8c0: use DateTimeImmutable::createFromTimestamp() to build dates from timestamps
  • b502c33: [Security, SecurityBundle] make login links and signed remember-me cookies follow the clock
  • 51316b9: [Config, Workflow] accept the documented places and transitions of workflows in the JSON schema
  • 0d6ac83: [FrameworkBundle, HttpKernel] allow streaming the controller result as JSON in the Serialize attribute
  • 7797255: [KeyManagement] make data key stores read the time from the clock
  • fd4e8f3: [Security] fix the cache lifetimes of DPoP proofs and JWKS when the clock is mocked
  • 0ed9640: [Scheduler] add ExcludeTimeOfDayTrigger to exclude a daily time window
  • d7c444e: [AssetMapper, HttpClient, Messenger, Security] use nullable clock arguments in the classes added in 8.2
  • 49c010b: [FrameworkBundle] add the kernel.test_state tag to keep or reset the state of a service for the duration of a test
  • 4d570c6: [Notifier] read the current time from the clock in PagerDutyOptions and PushyOptions
  • 4c79dbf: [FrameworkBundle, Mailer, Notifier] let MailerAssertionsTrait and NotificationAssertionsTrait see what previous requests sent
  • 7ed6744: [FrameworkBundle, Messenger] let MessengerAssertionsTrait see the messages queued by previous requests
  • 5fb039c: [FrameworkBundle, RateLimiter] reset the rate limiters between tests
  • f8874fe: [FrameworkBundle, Webhook] add KernelBrowser::sendWebhook() and WebhookAssertionsTrait
  • 780381b: [Mailer, Notifier] use the global clock for throttling and for the retry period of dead transports
  • dc402a6: [Mime] use the global clock for the default Date header and for DKIM signatures
  • e0a9136: [Scheduler] use the clock to resolve relative dates in PeriodicalTrigger and debug:scheduler
  • ccbf1b3: [Form] map the fields of a form to the constructor of its data class
  • 1842146: [HttpClient, Scheduler, SecurityBundle] clear the cache pools that hold runtime state at the end of each test
  • 0fb88c4: [FrameworkBundle, HttpKernel] clear the HTTP cache store between tests with the kernel.test_state tag
  • 83141b6: [Config] declare the elements of a node as accepted in the parent node
  • 4b79471: [SecurityBundle] use dedicated cache pools instead of cache.app
  • 879dce6: [Security, TwigBridge] add logout_button(), impersonation_button() and impersonation_exit_button()
  • 0d30611: [FrameworkBundle, HttpKernel] add RateLimitValueResolver to inject the rate limit consumed by the RateLimit attribute
  • 9ae6566: [BrowserKit, Cache, Clock, Scheduler] fix building dates beyond 2038 on 32-bit platforms
  • 259b999: use getTimestamp() and getMicrosecond() instead of formatting dates to read their timestamp
  • af19922: [FrameworkBundle] apply the configure_container closure after the kernel is shut down outside of the client
  • 4bffc1a: [SecurityBundle] cache the OIDC discovery in cache.security.discovery by default
  • 991f1a3: [HttpKernel] make targeted fields like CDN-Cache-Control private when the session is used
  • 62f5ccc: [Notifier] use the global From header of the mailer as the sender of the email channel
  • b9a16a7: [Cache, HttpKernel] add the $target argument to #[Cache] for RFC 9213 targeted cache directives
  • daada0b: [Form, HttpKernel, Mailer, Messenger, RateLimiter, Security, Uid] build the current date from time() so that ClockMock moves it
  • 90b2c15: [Lock] let blocking acquisitions wait longer for a removed lock file to go away
  • dbd7866: [PhpUnitBridge] make ClockMock follow the global MockClock
  • 79d937c: [Form] read the FormField properties of the parent classes of AsFormType data classes
  • c6cd77e: [KeyManagement] add the Encrypted attribute to encrypt Doctrine ORM columns
  • ab831c0: [KeyManagement] convert internationalized domains to their ASCII form in the email projections
  • 05bc0fe: [Console] add ttl param to LockableTrait::lock()
  • 1687c69: [Translation, TwigBridge, TwigBundle] add HtmlTranslator and the trans_html filter
  • 8599306: [Messenger] set the native AMQP message_id and correlation_id properties from identity stamps
  • fe50e06: [Messenger] let InteropSerializer exchange stamps as plain headers
  • 0b1b67f: [Messenger] sign every header written by the decorated serializer
  • 79dfa3b: [SecurityBundle] add security.route_loader to load the routes of all firewalls
  • 0aa80bb: [SecurityBundle, Validator] lower password hashing costs and disable not_compromised_password when framework.test is enabled
  • d877399: [Messenger] add a --stop-when-empty option to messenger:consume
  • 20a9d84: [HttpClient] enable the HTTP recorder by default when framework.test is enabled
  • 295a58a: [Workflow] add a description argument to the AsWorkflow, Place and Transition attributes
  • a9a7ca7: [TwigBridge, TwigBundle] load the Twig extensions declared with attributes as runtimes
  • f0e0d99: [WebProfilerBundle, Workflow] show the descriptions of places and transitions as tooltips in the profiler
  • 02f2f52: [Routing] make router.strict_requirements default to null when not in debug mode
  • 5870b97: [AssetMapper] make missing_import_mode default to strict in debug mode
  • 9c9e2f8: [Security, SecurityBundle] default the issuers of the oidc token handler and the authorization_servers of resource_metadata
  • d1085f6: [FrameworkBundle] default the session storage to mock_file when framework.test is true
  • 178150b: [Messenger] accept a DSN in the outbox option of transports
  • d411146: [Workflow] make description the third argument of the #[Transition] attribute
  • c49410c: [WebProfilerBundle, Workflow] show the description of workflows in the profiler
  • 7ca339b: [Serializer] register the Doctrine bridge collection denormalizer
  • a7d17c7: [Workflow] fix the profiler diagram when a place or a transition has an empty description
  • dfc3b71: [FrameworkBundle, HttpClient, SecurityBundle, Validator] resolve the env vars of framework.test when the container is compiled
  • 64ce78c: [KeyManagement] make reading a self-contained payload a stated choice
  • 2a3b2b2: [BrowserKit] ignore cookies set for another domain and don't send host-only cookies to subdomains
  • 6ed9d92: [KeyManagement] let a data key store bind its wrapping context at the backend
  • ba3f357: [KeyManagement] add timeout and max_duration DSN options to the HTTP bridges
  • 258016a: [KeyManagement] add timeout and max_duration DSN options to the AWS KMS bridge
  • aca49d9: [KeyManagement] do not follow redirects on Azure Key Vault, Google Cloud KMS and Vault requests
  • 19db846: [Messenger] deprecate signing messages per type
  • 9b1d1ce: [PropertyAccess] use the property path cache in isReadable()
  • a793478: [FrameworkBundle, HttpKernel] make the dependencies of the #[Serialize] listener lazy
  • 607cbed: [ObjectMapper] skip the property accessor for plain public properties
  • 2bcfc34: [KeyManagement] make the encrypter of the Doctrine DBAL types lazy
  • 0b77ed9: [TypeInfo] build the default key and value types of a collection lazily
  • b4e79ae: [FrameworkBundle, HttpKernel] cache the return type of the controllers that #[Serialize] streams
  • 812e6d1: [Form, FrameworkBundle] warm up and cache the guesses of TypeInfoFormTypeGuesser
  • 804ecd9: [HttpKernel] add phpstan/phpdoc-parser to the dev dependencies
  • e1fb337: [KeyManagement] let the HashiCorp Vault bridge get its token from a provider
  • 5800f37: [JsonSchema] add the component
  • 169b041: [HttpClient] stop following redirects from https to http
  • a601853: [FrameworkBundle, JsonSchema] add JsonSchemaBundle
  • 333673f: [HttpKernel] avoid duplicating request bodies in profiler data
  • 98ff7a9: [VarDumper] speed up source excerpts of dump() calls in Twig templates
  • 95b0d99: [Workflow] expose declared supports, support strategy and source class on service tags
  • 887f22b: [KeyManagement] add a Scaleway Key Manager bridge
  • 841bbce: [Process] add Process::inheritStdio() to run a process with the standard streams of the current process
  • 07d4939: [Console] exit with 128 + the signal number when a signal stops the application
  • a703f80: [Yaml] parse large inline collections in linear time
  • b344733: [Yaml] speed up line scanning in the block parser
  • 0aece6a: [DoctrineBridge, Messenger] add an inbox to skip the handlers of redelivered messages
  • 2ae5722: [String] speed up ascii(), width() and replace()
  • ac4b3f0: [Console] speed up writing to a section in several steps
  • ddb7194: [Messenger] support sending messages to specific Beanstalkd tubes
  • 1fe49ca: [Console] skip the stty probe when STDIN is not a terminal
  • a57737e: [Console] build the escape sequences of a color only once
  • 1bbd9f6: [Messenger] fix low-deps tests of the Beanstalkd bridge
  • 6d718e2: [Console] add an ASCII fast path to Helper::length() and Helper::substr()
  • 09f6123: [TwigBridge] extract the labels and help messages passed to form functions
  • 284b877: [KeyManagement] register the encrypted Doctrine DBAL types when DoctrineBundle creates a connection
  • 8f12ec0: [JsonSchema] allow PHP 8.2
  • f302106: [Messenger] let workers stop consuming a transport for a while after its messages fail
  • 54b38d3: [Security] start a re-authentication for remember-me users when a vote asks for it
  • 3168173: [Messenger] fix the Redis transport not recreating its consumer group after Redis lost it
  • bdef1ab: [DependencyInjection] reset the container when the kernel shuts down
  • 3ef2671: [Runtime] collect garbage cycles in FrankenPhpWorkerRunner only when needed
  • db9b692: [Messenger] survive a lost database connection in the Doctrine transport
  • b47221a: [Contracts, DependencyInjection] pass a shutdown flag to service reset methods
  • 1715d45: [DependencyInjection] add shutdown-only callbacks to kernel.reset
  • 1eae180: [ErrorHandler] add CSP nonce support to HTML error pages

Symfony UX development highlights

3.x changelog:

  • ffa16a1: add the imgix bridge
  • 0c11c5b: [Pagination] remove deprecated defaultPriorityMethod from the adapter iterator
  • 06ffa84: [TwigComponent] fix the pre-lexer mistaking <twig: inside a {% %} tag for a component
  • 76699ed: [Router] fall back to the application default locale
  • 2da3999: [Toolkit, Shadcn] fix the focus lost on open in dropdown-menu and combobox, and two timing-dependent tests
  • b840256: [Translator] dump the translations when the container is built without a dump directory
  • cc32e1d: [Toolkit, Shadcn] add command recipe
  • fe443da: [Router] dump the routes when the container is built without a dump directory
  • 3b1eb08: [Translator] register the configured dump directory with AssetMapper
  • 5621270: [Inspector] reposition overlays when the panel opens or closes
  • 1f43043: [Toolkit] run the browser tests on 8 shards
  • 801a64f: [LiveComponent] export the ComponentHooks type
  • 2154649: [Toolkit] fix GitHub kits pinned to a tag prefixed with "v"
  • 1da0fad: [LiveComponent] return StimulusAttributes from live_action() so it can be spread in a Twig component tag
  • 380c328: update the tooling dependencies
  • 0322691: [Toolkit] fix the preview wiring tests with strict AssetMapper imports
  • 804d42b: [Turbo] reset the broadcast listener between requests

Symfony AI development highlights

Changelog:

  • 6d26b49: use a generic type in TypedResultTrait instead of a assertions
  • 375c0a8: [Platform, HuggingFace] update README for Inference Providers
  • f232795: [Agent, Platform] add optional class argument to asObject() for static analysis
  • 06ec5a5: [Documentation, Platform] fix streaming, delta and asynchronous job docs
  • d262c2e: [Documentation, Platform] rework the job runner docs
  • 3b48d5e: [Store] ignore indented adornment lines when detecting RST headings
  • 7dc95e4: [Platform] throw MaxOutputTokensException when the limit cuts a tool call
  • 065c013: [Platform, Gemini] report token usage of streamed results

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.2 and 5.22.1 versions with the following changes:

  • Install the Upsun CLI from upsun/cli and restore hidden cloud command aliases (@fabpot)
  • Restore the upstream hidden aliases of cloud commands like cloud:logs, which the generator was dropping (@fabpot)
  • Read hidden aliases of cloud commands from the upstream command list instead of hardcoding them (@fabpot)
  • Read cloud command aliases from the upstream command list, which also covers commands implemented in Go (@fabpot)
  • Download the Platform.sh/Upsun CLI from upsun/cli as platformsh/cli is archived and stuck at 5.9.0 (@fabpot)
  • fix: LSP check doc link is dead (@tucksaun)
  • Send X-Forwarded headers from the Mercure proxy (@dunglas)
  • Drop the trailing slash from DEFAULT_URI to match the routing recipe (@nicolas-grekas)

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:

Upcoming Symfony Events

Call to Action

Published in #A week of symfony