Summary
Block themes render under worker mode — but only the first request each worker
serves gets any CSS. Every later request on that worker prints zero <style> tags.
This is the adapter-side half of ephpm/ephpm#116.
The engine-side crash reported there no longer reproduces (see the ephpm-side
investigation); what remains is WordPress state this adapter has to reset.
Measured (WordPress 6.7.1, Twenty Twenty-Five, worker_count = 1)
|
request 1 |
request 2 on the same worker |
/ response size |
50704 B |
12012 B |
inline <style> blocks |
21 |
0 |
Probing the worker's state right after the first render:
done_count=30 # $wp_styles->done
global_styles_done=1 # wp_style_is('global-styles', 'done')
queue=30
style_engine_stores=1
store=block-supports rules=9
unique_id=3 # wp_unique_id() counter, keeps climbing
Everything lost on request 2: global-styles-inline-css,
core-block-supports-inline-css, wp-block-library-inline-css, and the
per-block wp-block-*-inline-css sheets.
wp_unique_id() climbing is a second-order problem: the markup keeps emitting
fresh wp-container-core-group-is-layout-N / ...-navigation-is-layout-N class
names (observed -16 on one request, -30 on the next) while the CSS defining
them is never printed again — so even a cached stylesheet would not match.
Root cause
Three pieces of WordPress state are per-request in fpm but worker-lifetime here:
$wp_styles / $wp_scripts — WP_Dependencies::$done and ::$queue
WP_Style_Engine_CSS_Rules_Store — static store registry (block-supports)
wp_unique_id()'s static counter
Worker::resetRequestGlobals() resets $wp_query / $wp / $post / the current
user, but not these.
Suggested fix
Reset the dependency + style-engine registries per request, alongside the existing
resetRequestGlobals() work:
- re-instantiate (or clear
done/queue/to_do on) $wp_styles and $wp_scripts
WP_Style_Engine_CSS_Rules_Store::remove_all_stores()
- re-fire the enqueue lifecycle so
wp_enqueue_scripts / wp_enqueue_block_assets
run against the fresh registries
Re-instantiating $wp_styles throws away boot-time wp_register_style()
registrations, so the reset likely has to snapshot the registered set at boot and
restore it per request rather than starting empty.
Repro
e2e/Dockerfile as-is, plus a mu-plugin forcing a block theme:
add_filter('pre_option_template', fn() => 'twentytwentyfive');
add_filter('pre_option_stylesheet', fn() => 'twentytwentyfive');
Run with EPHPM_PHP__WORKER_COUNT=1 — with the default 3 workers, alternating
requests land on different (still-cold) workers and the loss is invisible.
Acceptance
/ renders byte-comparable CSS on every request of a worker's life, and the e2e
suite can drop its bundled classic theme in favour of a stock block theme.
Summary
Block themes render under worker mode — but only the first request each worker
serves gets any CSS. Every later request on that worker prints zero
<style>tags.This is the adapter-side half of ephpm/ephpm#116.
The engine-side crash reported there no longer reproduces (see the ephpm-side
investigation); what remains is WordPress state this adapter has to reset.
Measured (WordPress 6.7.1, Twenty Twenty-Five,
worker_count = 1)/response size<style>blocksProbing the worker's state right after the first render:
Everything lost on request 2:
global-styles-inline-css,core-block-supports-inline-css,wp-block-library-inline-css, and theper-block
wp-block-*-inline-csssheets.wp_unique_id()climbing is a second-order problem: the markup keeps emittingfresh
wp-container-core-group-is-layout-N/...-navigation-is-layout-Nclassnames (observed
-16on one request,-30on the next) while the CSS definingthem is never printed again — so even a cached stylesheet would not match.
Root cause
Three pieces of WordPress state are per-request in fpm but worker-lifetime here:
$wp_styles/$wp_scripts—WP_Dependencies::$doneand::$queueWP_Style_Engine_CSS_Rules_Store— static store registry (block-supports)wp_unique_id()'s static counterWorker::resetRequestGlobals()resets$wp_query/$wp/$post/ the currentuser, but not these.
Suggested fix
Reset the dependency + style-engine registries per request, alongside the existing
resetRequestGlobals()work:done/queue/to_doon)$wp_stylesand$wp_scriptsWP_Style_Engine_CSS_Rules_Store::remove_all_stores()wp_enqueue_scripts/wp_enqueue_block_assetsrun against the fresh registries
Re-instantiating
$wp_stylesthrows away boot-timewp_register_style()registrations, so the reset likely has to snapshot the registered set at boot and
restore it per request rather than starting empty.
Repro
e2e/Dockerfileas-is, plus a mu-plugin forcing a block theme:Run with
EPHPM_PHP__WORKER_COUNT=1— with the default 3 workers, alternatingrequests land on different (still-cold) workers and the loss is invisible.
Acceptance
/renders byte-comparable CSS on every request of a worker's life, and the e2esuite can drop its bundled classic theme in favour of a stock block theme.