02 Oct 2026
Symfony Blog
New in Symfony 8.2: Performance Improvements
At Symfony, we're obsessed with performance, and Symfony 8.2 has plenty of examples. Over the last few weeks, Nicolas Grekas profiled the Symfony Demo application and a custom API project, turning every finding into a pull request. The result is faster web…
02 Oct 2026 7:27am GMT
01 Oct 2026
Drupal.org aggregator
Talking Drupal: Talking Drupal #572 - The PHP Foundation
Today we are talking about PHP, Open Source, and Sustainability with guest Elizabeth Barron. We'll also cover AI Image Classification as our module of the week.
For show notes visit: https://www.talkingDrupal.com/572
Topics
- What is the PHP Foundation
- Language Health Challenges
- Paid Core Developers
- Release Infrastructure
- Foundation Role and Perception
- Docs and Onboarding
- Elizabeth Open Source Journey
- Future of PHP Ecosystem
- Listener Elephant Question
- Ecosystem Security Hub
- Advisory Board Limits
- Modernizing Mailing Lists
- Foundation Awareness Push
- PHP Ambassadors Strategy
- State of PHP Survey
- Five Year Vision
- Community Hour Podcast
- php.net Design Refresh
Resources
- The PHP Foundation
- Derek Rethans
- xdebug
- Infrastructure
- https://github.com/derickr
- Drupal advocacy
- Link to community hour podcast
- Php 8.5
- Contribute to php article
Guests
Elizabeth Barron - thephp.foundation elizabethbarron
Hosts
Nic Laflin - nLighteneddevelopment.com nicxvan John Picozzi - epam.com johnpicozzi Tim Sharp - tea-sharp
MOTW Correspondent
Martin Anderson-Clutz - mandclu.com mandclu
- Brief description:
- Have you ever wanted the images in your Drupal media library to be automatically given alt text, a rich description, and a set of tags, using AI? There's a recipe for that.
- Module name/project name:
- Brief history
- How old: created in Sep 2025 by Artem Dmitriiev (a.dmitriiev) of 1xINTERNET
- Versions available: 1.1.1, which requires Drupal 11.2 or newer
- Maintainership
- Actively maintained, latest release and commit just two weeks ago
- Security coverage
- Number of open issues: 9 open issues, 4 of which are bugs
- Usage stats:
- No usage data reported
- Module features and usage
- This recipe applies core's image media type recipe, then adds a description field and a tags field to image media, with tags in a new "Image Classification" vocabulary
- Editors get a "Generate Alt Text" button right on the image field, using the Field Widget Actions module, with a prompt written like an accessibility expert: under 100 characters, no "image of", flag logos and graphics
- On save, a vision model writes a description of up to eight sentences: colours, photo versus illustration, people, mood, and key objects, using the media name for context
- Tagging is chained off that description rather than the image itself, picking or creating up to five short, general terms, and reusing similar existing tags where it can
- It also adds bulk actions to the media admin view, so you can run description and tagging across an existing library, not just new uploads
- Description and tag filters are added to the media library, including the widget, so editors can search for "sunny summer day" right in the image picker
- The stated bigger goal is making images discoverable by RAG and other AI search, so good descriptions become a foundation for future AI features. In fact, this recipe is a dependency for the Canvas AI Image Search recipe, which is still a work in progress
- Under the hood, images get scaled to 400px wide and converted to PNG before they're sent to the model, which keeps things faster and cheaper
- One caveat: your site needs an AI provider with a default "chat with image vision" model, and the recipe checks for that when it's applied
- Also worth a look: the description prompt asks the model to describe people's age, gender, and ethnicity, and even name them if it can, so you may want to review that prompt for your privacy policies, and adjust accordingly
- Like any recipe, it provides a robust starting point for some compelling features, and every prompt and setting can be tweaked after you apply it
- Lastly, I wanted to mention that in today's Driesnote in here in Rotterdam, Dries demoed being able to use MCP to search images on his own site. A recipe like this could help you make sure the images in your media library have sufficient metadata to make that work reliably
01 Oct 2026 6:00pm GMT
The Drop Times: Bento Showcase Brings Visual Hierarchy to Drupal Views
A Drupal View does not have to end as a row of equal cards. Bento Showcase tests how far sorting, promotion, and image treatment can carry a structured content collection toward a composed page.
01 Oct 2026 11:01am GMT
Nuvole: Cutting Drupal's site:install time from 20 minutes to 90 seconds with an AI coding agent
A large multilingual Drupal site took almost 20 minutes to self-install, which made every CI run painfully slow. Here's how a PHP profiler and an AI coding agent got it down to 90 seconds.
"We'll add the tests later" only works if the tests are fast enough that nobody is tempted to skip them. On one of our large multilingual Drupal projects, they weren't.
The site has content in 100 languages, around 200 modules, 400+ configured fields, and up to 5,000 config files once translations are counted in. Running drush site:install against that configuration took close to 20 minutes. Add Docker startup and the actual end-to-end test run on top, and a single CI pipeline run could easily take an hour. Copying the production database into CI wasn't an option, so a full config-driven install was the only realistic way to get a clean site to test against for every change.
An install that slow doesn't just cost CI minutes, it costs feedback loops. So we set out to find out exactly where the 20 minutes were going, fix what we could, and see how AI could shorten the loop of measuring, fixing and re-measuring.
Setting up a profiler
The first step was getting visibility into what Drupal was actually doing during install. We used SPX, a lightweight PHP profiler that's easy to drop into a Docker-based PHP image alongside xdebug or blackfire alternatives, without much of the performance overhead those carry.
Once enabled, SPX gives you a web UI to browse individual profiles and see wall time, memory and call counts per function:

And for any profiled request, a flame-graph-style view of the whole call stack, with a sortable table of the most expensive functions underneath:

That's already useful for a single page load. The interesting part is what happens when you point the same tooling at an AI coding agent.
Giving the coding agent access to the profiler
SPX ships an MCP server extension, SPX-MCP, which exposes recorded profiles through a query-like interface instead of a browser UI. Wiring it into an agent's MCP configuration means the agent can list profiles, drill into a specific request, and pull out the functions responsible for the most time, without a human first opening a flame graph and squinting at it.
In practice, that turns "let me go look at a profile and tell you what's slow" into a two-line prompt:

The agent found the three most recent profiles, correctly noticed they were regular page requests rather than the site:install run we actually cared about, and then, once pointed at the right report, broke a 1.44s homepage request down into HTML rendering, Twig compilation, class loading, cache reads and database time, each with concrete function names attached. That's the same triage a developer would do manually, just a lot faster to get to.
The problem with profiling a 20-minute install
Profiling a single page request is straightforward. Profiling a 20-minute drush site:install is a different story: turning SPX on for the whole run produced one profile with almost 590 million calls, weighing about 8GB on disk. That's too large to open in the web UI, and too large for an agent to reason about as a single blob.
The fix was to split it into one profile per install stage instead of one profile for the whole run. Drupal's installer runs through a fixed sequence of tasks (install_select_language, install_config_import_batch, install_import_translations, and so on), and there's a single place in core, install_run_tasks(), where each task starts and finishes. A small patch there starts and stops an SPX profile around each task and tags it with the task name as metadata:
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ function install_run_tasks(&$install_state, ?callable $callback = NULL) {
$task = array_shift($tasks_to_perform);
$install_state['active_task'] = $task_name;
$original_parameters = $install_state['parameters'];
+ if (function_exists('spx_profiler_start')) {
+ spx_profiler_start();
+ spx_profiler_full_report_set_custom_metadata_str(json_encode(['task' => $task_name]));
+ }
$output = install_run_task($task, $install_state);
...
+ if (function_exists('spx_profiler_stop')) {
+ spx_profiler_stop();
+ }
} while (!$finished);
That turned one unmanageable profile into a dozen small ones, each scoped to a single install task, each cheap enough for the agent (or a human) to load and query directly. For the rare case of a profile that's still too big, there's also SPXQ, a small command-line viewer for browsing multi-gigabyte SPX reports without loading the whole thing into memory or a browser tab.
Finding the bottlenecks
With per-task profiles available, the workflow became a loop: profile a task, find the most expensive function, patch it, install again, and check the diff in timing. Doing that by hand, task by task, function by function, is tedious, exactly the kind of repetitive, well-defined work an AI coding agent is good at once it has direct access to the profiling data instead of having to be told what's in it.
One of the clearest examples was the install_config_import_batch task, which turned out to spend the majority of its time in EntityStorageBase::doPostSave():

Digging into that call chain showed that saving a single field config was re-triggering hooks and cache invalidation for every entity type's base field definitions, even when only one bundle had actually changed, and it was doing that for every field, of which there were hundreds. After each fix, the agent could re-run the install, pull the new profile, and confirm the improvement against the previous run.
What actually moved the needle
After several rounds of this profile-fix-reprofile loop, here's what made the biggest difference:
-
Cache collection names.
StorageComparer::getAllCollectionNames()was rescanning the sync directory to find language collections on every one of ~2,800 config items during import, even though the answer never changes. Computing it once per import and reusing it cut that step by 27.6%. -
Cache translatable default config.
LocaleConfigManager::getTranslatableDefaultConfig()rebuilt a typed-config wrapper once per language, even though the shipped default config is identical across languages. Caching it by config name cut this by 53.5%. -
Cache
isSupported(). The sibling of the fix above, doing two redundant config reads per language for an answer that doesn't depend on the language. Caching it removed another 5.4%. -
Scan the translation directory once. The same, usually-empty translation folder was being scanned for every project/language pair, up to 1,580 times. Listing it once and matching filenames against that list brought the whole step down to about 6ms.
-
Skip no-op config saves.
LocaleConfigManager::updateConfigTranslations()was saving configuration even when nothing had actually been translated, reloading language override objects for all languages on every save. A simple!empty($processed)guard removed 237 unnecessary saves. -
Stop force-rebuilding a cold field map. After every container rebuild,
FieldDefinitionListener::onFieldDefinitionCreate()was forcing a full field map rebuild for every single field created during install. Skipping that while the map is still cold cutgetBaseFieldDefinitions()calls by 76%, with no change to the resulting field map. -
Narrow the cache clear on field save. This was the single biggest win. Saving one field config was wiping every entity type's base field definitions and triggering a full plugin rediscovery. Clearing only the affected bundle's field definitions during install cut this step by 18%, verified as producing byte-identical definitions across 37 entity/bundle combinations.
-
No-op the translation status key/value store during install. A small wrapper turns
locale.translation_statuslookups into an in-memory no-op during install (and passes through normally afterwards), avoiding repeated database hits for a value that's meaningless mid-install. -
Disable remote translation lookups for CI installs. During an automated test install there's no reason to reach out to the localization server, so
translation.import_enabledand related settings are simply turned off for that environment. This one is a CI-only optimization, not something you'd want in production. -
Skip irrelevant entity hooks during install. Some
entity_update/entity_inserthooks have no meaning during install or fixture loading. Guarding them behind$entity->isSyncing()avoided pointless work; for example, the Search API module's_search_api_view_crud_event()handler.
None of these are exotic. Individually, most are the kind of "don't redo work you already did" fix any Drupal developer would recognize once pointed at it. What changed was how quickly they could be found and verified: instead of manually reading flame graphs and re-running a 20-minute install to check each guess, the agent could query a profile, propose a targeted fix, and confirm the improvement against the previous measurement, over and over.
The result
After all of it: 1 minute 30 seconds, down from roughly 19 minutes 30 seconds. A 13x improvement on the number that mattered most for us, the total install time inside the CI pipeline.
Not every fix here is something we'd apply blindly to every project. Some, like skipping remote translation downloads, only make sense for a throwaway test install. Others touch Drupal core behaviour around cache invalidation and are being submitted as proper core patches with issues on drupal.org, since they need review and testing well beyond "it worked for us".
Bonus
Both the SPXQ profile viewer and a reusable "profile, analyze, fix, validate" skill for coding agents are public:
- SPXQ, a CLI profile viewer for large SPX reports.
- A Claude/Codex skill that walks an agent through profiling, analyzing, fixing and validating performance issues on its own.
- The core patches from this round of optimization are collected in this gist.
Tags: Drupal Planet, Drush, Performance, AI, Drupal, DrupalCon
01 Oct 2026 11:00am GMT
Symfony Blog
New in Symfony 8.2: More Flexible Serialization Groups
Serialization groups let you choose which properties to include when serializing an object. You might use one group for public API responses and another for admin exports. Symfony 8.2 adds new ways to configure these groups, and lets controllers and Messenger…
01 Oct 2026 7:27am GMT
30 Sep 2026
Symfony Blog
SymfonyCon Warsaw 2026: The PHP Runtime I Want to See
Ready to take your PHP and Symfony skills to the next level? SymfonyCon Warsaw 2026 lands in Poland on November 26-27! Gather with developers worldwide for two high-impact days offering 3 parallel tracks and exciting community events. Mark your calendar…
30 Sep 2026 2:57pm GMT