This week, Symfony 5.4.56, 6.4.50, 7.4.23, and 8.1.11 maintenance versions were released with many security fixes for components such as Routing, Serializer, Config, and HtmlSanitizer. We also published the first beta of Symfony 8.2 so you can test it before its final release, along with Symfony UX 3.7. Meanwhile, we kept publishing articles about Symfony 8.2 features, such as validated and compiled expressions, polymorphic form collections, firewalls and routes, and the HTTP recorder. Finally, we announced more talks for SymfonyCon Warsaw 2026.
Symfony development highlights
This week, 346 pull requests were merged (248 in code, 25 in docs, 58 in UX and 15 in AI) and 78 issues were closed (34 in code, 8 in docs, 31 in UX and 5 in AI). Excluding merges, 71 authors made additions and deletions. See details for code, docs, UX and AI.
- 23cf114: [Filesystem] reserve temporary removal directories atomically
- caf9928: [Finder] fix Glob::toRegex() producing an invalid regex for bracket expressions containing a slash
- 7f1482a: [Filesystem] fix failure handling in remove()
- b064b76: [PropertyInfo] don't turn intersection types into class names in PhpDocExtractor
- c640add: [Validator] only extract property types when auto-mapping applies
- 068efbb: [Messenger] don't reuse a serialized body after replacing the message
- 7695f4f: [AssetMapper] fix the resolution of relative paths in downloaded packages
- 19b92eb: [Console] fix ProgressBar::start() computing the percentage against the previous max
- 649cd4c: [HttpFoundation] preserve the base URL for query and fragment references
- aa69832: [Console] fix ProgressBar::setMaxSteps() not updating the percentage
- 01ed10b: [HttpFoundation] fix UrlHelper for fragments behind a trusted X-Forwarded-Prefix and for an empty path
- 79b33e6: [HttpFoundation] don't match the script name in Request::getBaseUrl() when it's only the start of a path segment
- d1b9a53: [Console] fix ProgressBar frames on undecorated output when resuming, restarting or finishing
- eaa322e: [HttpFoundation] don't use the script name as Request::getBaseUrl() when it's not in the URI path
- b8f1e4b: [Console] fix ProgressBar::finish() with a lowered max and restarting a finished bar without a max
- 82dbdc5: [Console] fix ProgressBar's percent after finish() on undecorated output without a max
- da991c8: [HttpFoundation] fix Request::getBaseUrl() for a percent-encoded or wrong-case script name after a prefix, and behind an alias
- e7731c2: [HttpClient] use the first Location header in CurlHttpClient, like curl does
- 2a92802: [HttpClient] fix a typo in NoPrivateNetworkHttpClient
- 49ef16a: [HttpClient, Validator] add the deprecated IPv6 site-local range to the private subnets
- d9cf54e: [HttpClient] reject CURLOPT_COOKIE and CURLOPT_UNRESTRICTED_AUTH in extra.curl
- 51acd09: [HttpFoundation] cap the size of the cache of IpUtils
- 04a3c0f: [Security] clear the pre-authenticated token when REMOTE_USER is empty
- 14e4da4: [Serializer] ignore undeclared property paths with brackets
- 034d631: [HtmlSanitizer] don't treat link, meta, plaintext and referrerpolicy as safe
- c789ea0: [Yaml] bound the number of nodes that collection aliases expand to
- e705ed7: [Yaml] don't copy long nested blocks at each nesting level
- 694764f: [Serializer] apply ignored attributes to setter and adder aliases
- 7d5e934: [Serializer, Templating] limit the caches that grow with each input value
- 21519f5: [Serializer] limit the caches that grow with unknown keys
- 5bb1245: [Cache] fix fetching long keys with MemcachedAdapter::getItems()
- e5bd836: [HtmlSanitizer] block html and body in allowSafeElements() and don't let links expose window.opener
- 31bc475: [HtmlSanitizer] don't treat elements and attributes that act on the page as safe
- 6d8614f: [Mime] fix SMimeSigner splitting a CRLF across chunk boundaries
- b5fe879: [Console] count wide characters as two columns when wrapping and truncating
- 00aa137: [Security] reject X509 DNs whose identifier attribute appears more than once
- 1864a2f: [Serializer, Templating] limit more caches that grow with the input
- a6f835e: [Security] fix the login throttling message announcing one minute too many
- 9f5cec4: [Cache] declare the temporary tag set as KEYS[2] in RedisTagAwareAdapter::doInvalidate()
- 2e55a52: [Console] fix the width of tags and URLs when wrapping text
- 674545d: [Form] let a validation_groups callable return false to disable validation
- 45aca59: [PropertyAccess] limit the caches that grow with the input
- c7d3ce7: [Console] fix wrapping escaped tags and text wider than 138 columns
- c627e0a: [Serializer] don't call methods named after the attributes being denormalized
- 1a76d40: [Serializer] don't read untyped attributes with deep_object_to_populate
- 55d0d57: [Config] do not treat NAN as a placeholder
- 9ac524c: [Messenger] stop the worker when a signal arrives before it is created
- d665e11: [Serializer] refuse an object where an array of objects is expected
- 668813b: [Finder] fix three bracket expressions Glob::toRegex() gets wrong
- c85f170: [Routing] prevent {_route} host placeholders from matching the path in compiled matchers
- fa23457: [HttpFoundation] check IPv4-mapped, 6to4, Teredo and NAT64 addresses as their IPv4 address in IpUtils::isPrivateIp()
- 955c5f7: [Finder] fix Glob::toRegex() when the delimiter is a slash
- e97dce2: [Config] fix EnumNode error messages for INF, NAN and enum cases without a backing value
- db565c4: [Console] fix formatting and wrapping text with escaped tag delimiters
- 8830d20: [HttpClient] keep the response decorated by TraceableResponse usable until it is freed
- ef6619e: [HttpKernel] release the cache lock when a revalidated response is not stored
- 08e8c43: [HttpKernel] fix HttpCache serving a 503 after validating a no-cache entry when the cache is locked
- 9c26840: [HttpKernel] fix the HttpCache lock being released when another process stores the same URL
- e29fc56: [HttpKernel] fix HttpCache serving must-revalidate responses stale while another process revalidates them
- 69af7ac: [PropertyInfo] fix resolving the PHPDoc types of trait methods and of classes using traits
- e56bdaf: [Config] fix DirectoryResource checking the parent directory on Windows
- 4826097: [PropertyInfo] fix reading the PHPDoc of trait members and of inherited promoted properties
- 976be73: [PropertyInfo] fix reading the PHPDoc of abstract trait methods after a docblock on the use statement
- 9bfe9de: [Form] review Macedonian (mk) translations
- a03e46c: [PropertyInfo] return null for docblock types the extractors can't resolve
- 391a217: [TypeInfo] leave out type aliases that cannot be resolved
- 30134ac: [TypeInfo] fix resolving unions and intersections that PHPStan accepts
- c81b5a5: [ObjectMapper] skip readonly and asymmetric visibility target properties without a property accessor
- d965619: [Console] fix ProgressBar::iterate() keeping the max of a previous iterable for a non-countable one
- 9993e29: [HttpKernel] accept only form, JSON and XML payloads in #[MapRequestPayload] unless acceptFormat lists other formats
- 797bc53: [HttpKernel, Security] split methods on commas in #[IsGranted], #[IsCsrfTokenValid] and #[IsSignatureValid]
- 5abe91a: [HttpClient] fix an inverted check in AmpResolverV5::query()
- 535cd2c: [JsonStreamer] resolve the templates of inherited properties through @extends tags
- 0b56f20: [ObjectMapper] fix mapping nested objects into properties typed self or parent
- ff0c98c: [JsonStreamer] resolve the templates of inherited properties when the streamed class is not generic
- 1ff94e3: [TypeInfo] preserve distinct union and intersection members
- 21fe0c2: [PropertyAccess, Serializer] declare the return type of __set() in tests
- f1f902e: [CI] add the repository of patched packages to components that declare their own repositories
- 01c80f1: [JsonStreamer] resolve the templates of nested generic types
- ca2e539: [JsonStreamer, TypeInfo] fix resolving unions that repeat the same type
- 0289e9b: [JsonStreamer, TypeInfo] keep list shapes as lists
- e988ca1: [PropertyInfo] fix resolving self, static, parent and $this with getType() and getTypeFromConstructor()
- 4eb5db8: [TypeInfo] fix resolving the PHPDoc types of trait members
- f31a677: [PropertyInfo] preserve inherited templates in trait contexts
- 014d734: [PropertyInfo] fix resolving the PHPDoc types of trait methods
- c624482: [TypeInfo] fix resolving the PHPDoc types of a class with the imports of its traits
- 3649d29: [TypeInfo] cover untagged template inheritance with tests
- 82521c9: [FrameworkBundle] restore the APP_*_DIR server variables set by MicroKernelTraitTest
- ab1554d: [TypeInfo] fix infinite recursion in Type::fromValue() with cyclic values
- 54ec44b: [TypeInfo] fix infinite recursion when a type alias import uses another case for the class name
- 67ba9c7: [TypeInfo] throw UnsupportedException on malformed type strings
- e404c60: [Tui] fix ProgressBarWidget when changing the max and with negative values
- 0c89917: [Messenger] fix infinite retries when a serializer throws a decoding failure
- 08429a6: [HttpClient] conflict with amphp/amp < 3.0.1, whose weakClosure() breaks on PHP 8.4
- e8ec379: [Tui] fix the tracked rects of subtrees rendered without position tracking
- 2289eca: [Tui] restore the widget style after a sub-element styled with applyElement()
- 391af62: [Tui] keep styled underlines (SGR 4:n) on wrapped lines
- 0206c07: [Tui] restore inherited styles after a styled sub-element
- a36a9c8: [HttpClient] fix the allow list of NoPrivateNetworkHttpClient letting its whole address family through
- a9ccde1: [DependencyInjection, HttpKernel] fix type-hinting file_locator and services_resetter with their deprecated HttpKernel types
- 11bb979: [DependencyInjection] fix parent::buildContainer() tracking the default bundles.php of a MicroKernelTrait kernel
- cfb39d9: [HttpKernel] fix the bundle cache dumped by parent::dumpContainer() in a MicroKernelTrait kernel
- 4fb2135: [HttpClient] record the Content-Length of the redacted response body
- 23be38f: [Ldap, SecurityBundle] fix ldap_users_only when a firewall uses several LDAP authenticators
- 3de4762: [Ldap, Security, SecurityBundle] hash the presented password when the user is unknown, disabled or has no password
- 7f91bba: [Messenger] let a signed message whose serializer throws be replayed from the failure transport
- 4cb50b0: [ObjectMapper] don't silently skip properties that need a property accessor
- 2852065: [Messenger] add keepalive support to the MongoDB transport
- 06c62e2: [Process] poll with short sleeps first when waiting for a process whose pipes are closed
- 934b8d8: [DependencyInjection, FrameworkBundle] generate config/reference.php and config/schema.json in apps without FrameworkBundle
- 90726b1: require v3.8.1 of the contracts
- 1c587ae: [Validator] sync the non-public subnets of the Ip constraint with IpUtils::PRIVATE_SUBNETS
- 887c705: [JsonSchema] preserve serialized item types in list-shaped arrays
- c8c256c: [FrameworkBundle] lint and compile the $if expressions of #[RateLimit]
- a40ceb6: [DependencyInjection] fix resolving env placeholders with many env vars
- 118b93e: [FrameworkBundle] check that controller.expression_language lists every Expression property of the HttpKernel attributes
- 870cc11: [ObjectMapper] resolve self and parent property types when inferring nested mappings
- 32671bf: [TypeInfo] collect contravariant templates and reduce redundant unions in Type::map()
- c369931: [TypeInfo] keep distinct members when mapping unions and intersections
- a665460: [TypeInfo, Serializer, JsonStreamer] resolve templates inherited through @extends, @implements and @use tags
- 230bc59: [JsonSchema] preserve generic class arguments in schema definitions
- 018ac05: [Cache, FrameworkBundle, Routing, Serializer, Translation, TwigBundle, Validator] preserve legacy types of moved services
- aec1cf9: [Routing, TwigBundle] keep routing.loader a DelegatingLoader in apps that use TwigBundle
- 1b85ece: [HttpKernel] allow extending #[RateLimit] and #[Lock] attributes
- 87b281e: [TypeInfo] don't let the templates of a child class shadow the ones of its untagged parent
- cb61480: [JsonSchema] cover untagged template inheritance with tests
- b40f882: [PropertyInfo, TypeInfo] inherit PHPDoc types from parent classes, interfaces and abstract trait methods
- 359dd45: [Config] compare the content of directory entries modified in the same second as the cache
- b43ddac: [DependencyInjection, Routing, Translation] track loaded files before reading them
Symfony UX development highlights
- ba3a593: [Css] add the preflight option
- e4a6b60: [Css] import the stylesheet from assets/styles/app.css in the docs
- 520fe19: [Css] accept CSS strings in values
- ec1c84e: [Css] support every Panda token category
- 8b6c31d: [Css] accept any value for a token category with no token, like Panda
- f068be9: [Css] stop naming shared reference types after another property
- 3621ca7: [Dropzone] add a
dropzone:pre-addevent to transform files before they are added - 95331c6: [Dropzone] expose the state with data-state and toggle the parts with hidden instead of inline styles
- c8f5331: [Dropzone] render the preview of each file from a template in multiple mode
- 9214a93: [Dropzone] make the clear button, file name and image preview targets optional in single-file mode
- 0815db6: [Dropzone] add a placeholder option to DropzoneType
- 524e3ac: [Dropzone] leave out the dropped files that do not match accept and the files over max_files, and dispatch
dropzone:reject - e8fd5ba: [Dropzone] use one code path for the single-file and multiple modes
- 6f1e923: [Turbo] render the HTML attributes of <
twig:Turbo:Stream:From> - 2b43f3d: [Turbo] keep the EventSource of a permanent <turbo-mercure-stream-source> open across visits
- 3953093: [Turbo] add a subscribe option to turbo_stream_from() and <
twig:Turbo:Stream:From> - 02b843f: [Turbo] add an option to ignore the cookie domain errors of private stream sources
- 69537a6: [Autocomplete] only add the clear button to a select that has an empty option
- fa72762: [Autocomplete] support the preferred_choices option in Ajax-powered fields
- cc106fd: [Toolkit] make the Toolkit non-experimental
- fec6497: [LiveComponent] document IDOR / write-surface sharp edges on LiveProp
- 1cd5c50: [LiveComponent] fix NoModificationAllowedError when closing the error modal twice
- fdaec26: [TwigComponent] add a shorthand for dynamic props named like a variable
- 2da2b0d: [LiveComponent] reject overflown dates during date hydration
- 1f50b9e: [Pagination] fix the Doctrine ORM count when a parameter is only used by the SELECT or the ORDER BY
- 05e6bfd: [LiveComponent] fix (de)hydration of DTO collections containing a null item
- 7b5be77: [Autocomplete] exclude already selected options from remote results
- 8941ddc: [Turbo] document that the minimal frame layout drops the page metadata on promoted frame visits with Turbo 8
- bc9175f: [LiveComponent] fix hydrating entities typed with a resolve_target_entities interface
- 9f10d3a: [LiveComponent] prevent LiveCollectionTrait from reusing removed indexes
- cf7e2e5: [LiveComponent] fix dehydrating arrays of entities with a Uuid identifier
- 993b4e8: [LiveComponent] rebuild Component on reconnect when props changed in between
- db19581: [Toolkit, Shadcn] fix sidebar-12 and sidebar-15 overwriting the DatePicker component
- 312fdc8: [Toolkit] fix Stimulus identifiers not prefixed in subdirectory installs
- cd50ccc: [Autocomplete, Pagination] add the locales the other package already ships
Symfony AI development highlights
- 8f8b1a5: [AI Bundle, Store, Pinecone] replace probots-io/pinecone-php by the HTTP client
- 0cb0a2a: [Agent, Firecrawl] add firecrawl_search tool
- 12ad4bb: [AI Bundle] reuse MCP schema constraints for agent tools
- b2e53e7: [Platform, Ollama] add text-to-speech support
- a8f8a39: [Platform, Mistral] add Voxtral text-to-speech
- 3d68886: [Store, Sqlite] escape full-text search terms
- 7fd2b19: [Platform, ClaudeCode, Codex] reject tool definitions the CLIs cannot call
- 6747a33: [Platform] expose the provider on InvocationEvent and ResultEvent
- 68aefc8: [Platform, ClaudeCode, Codex] keep every user message and text block after the last assistant message
- 6afdd17: [Platform, OpenRouter] add decision models through the Decisions API
- d23c5ad: [Platform, Codex] refuse a turn without turn.completed
- ffd1569: [Platform] describe exclusive bounds with numeric exclusiveMinimum and exclusiveMaximum
- bc4b967: [Mate] keep the skill copy when .claude/skills links to .agents/skills
- 1fc422e: [Platform, ClaudeCode, Codex] stop returning tools the CLI already ran as tool calls for the caller
Newest issues and pull requests
- [SecurityBundle] Add reusable token_handlers and default the audience to the resource of resource_metadata
- [Notifier][MicrosoftTeams] Add Adaptive Card support for Workflows webhooks
- [Filesystem] Add tempDir() to create a unique temporary directory
- [Form][TwigBridge] Support a localized message for empty expanded choices
- [Form] Infer HTML minlength from validation constraints
Symfony Jobs
These are some of the most recent Symfony job offers:
-
Symfony Developer at Bas Software
Full-time - €4,000 – €5,000 / month
Netherlands Remote + part-time onsite (Eindhoven, Netherlands)
View details -
Backend Symfony Developer at MyUnisoft
Full-time - €45,000 – €50,000 / year
France Remote + part-time onsite (Montigny-le-Bretonneux, France)
View details -
Symfony Developer at SensioLabs
Full-time - €35,000 – €49,000 / year
France Remote + part-time onsite (Paris, France)
View details -
Symfony Developer at Bas Software
Full-time - €5,500 – €6,500 / month
Netherlands Remote + part-time onsite (Eindhoven, Netherlands)
View details -
Backend Symfony Developer at Reiz Tech
Full-time - €5,500 / 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
- Blazingly PHPast Symfony 8.1
- Ask your notes a question, built with Symfony AI
- An MCP server with Symfony AI, so your AI assistant can create PHP playgrounds
- 7.7x Smaller and Encrypted at Rest: Compressing Symfony Messenger Payloads Without Touching a Handler
- Passkeys in 17 Files: Hybrid Login, Recovery Codes and Revocation in Symfony 8.1
- Your Doctrine repositories don't need to extend anything
- The PHP Side of RAG: A Typed Client and Queue-Safe Sync for Laravel and Symfony
- EasyAdmin Is Getting a Brand New Design
- The New EasyAdmin Design: Make It Yours
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.