Problem
ACFML translates ACF field definitions — labels, instructions, placeholders,
prepend/append, choices, message — on every request that loads a field group.
ACFML\Strings\FieldHooks implements IWPML_Frontend_Action, so it registers on
the front end with no is_admin() guard, and hooks acf/load_field. Every field
then reaches Config::getForField():
Fns::filter( Relation::propEq( 'namespace', 'field' ), self::DATA )
An unmemoised linear scan over a static array, run through WPML's functional
library, allocating a closure per element. On a plain page view none of those
strings is rendered, so the whole walk is wasted.
Measured (sloneek, 117 field groups / 972 top-level fields)
|
time |
| baseline |
1.09–1.13 s |
| group-level translation off |
1.04–1.14 s (no change) |
| field-level translation off |
0.29 s |
A PHP-FPM slowlog profile over 3973 slow requests put 71 % of deepest-frame
samples inside wpml/fp + wpml/collect.
Staging, same box, alternating the file to hold load constant:
| round |
without |
with |
| 1 |
9.70 s |
4.17 s |
| 2 |
8.97 s |
4.28 s |
| 3 |
10.58 s |
3.99 s |
Rendered HTML is byte-identical once per-render uniqueId() values are
normalised.
Why this is not #107
#107 was closed unmerged, on
the reasoning that a shared workaround would outlive the bug it worked around.
That reasoning was right there and does not carry here:
|
#107 (acf/load_reference leak) |
this |
| who pays |
one caller outside WPML's own plugins |
every front-end render |
| fixed upstream |
yes, acfml 3.0-b.1 |
no |
| workaround lifetime |
would outlive the bug |
bug has no end date |
Verified rather than assumed: on proficiohub (acfml 3.0-b.1, sitepress
5.0.0-b.1, wpml-string-translation 5.0.0-beta.1), Strings/FieldHooks.php,
Config.php, Translator.php, Traversable/Entity.php,
Traversable/FieldGroup.php, Transformer/Translate.php and Package.php are
byte-identical to 2.2.4, and the bundled wpml/fp library has identical MD5s.
The same comparison does pick up 3.0-b.1's one change — the remove_filter
hook-name fix from #107 — so it is not a blind diff.
Proposal
A StarterBase opt-in property, default off, following $site_icon_tags
and its siblings:
/**
* @var bool Skip ACFML's translation of ACF field definitions on plain
* front-end views. Admin, REST, AJAX and WP-CLI keep it.
*/
protected $acfml_skip_frontend_field_translation = false;
Default off is not timidity. Switching it on is wrong for any site that renders
ACF field definitions to visitors:
- a theme calling
acf_form() — placeholders, instructions and labels become
visitor-facing
- a template printing a
select/radio/checkbox choice label rather than
its value
Neither is detectable from the kit, so the consuming site has to assert it. The
sloneek filter asserts exactly that in its own comment.
Retiring it later is a one-line default flip once ACFML memoises Config::getFor().
Prior art in this repo
Downstream implementation and full evidence: portadesign/sloneek#103.
Problem
ACFML translates ACF field definitions — labels, instructions, placeholders,
prepend/append, choices, message — on every request that loads a field group.
ACFML\Strings\FieldHooksimplementsIWPML_Frontend_Action, so it registers onthe front end with no
is_admin()guard, and hooksacf/load_field. Every fieldthen reaches
Config::getForField():An unmemoised linear scan over a static array, run through WPML's functional
library, allocating a closure per element. On a plain page view none of those
strings is rendered, so the whole walk is wasted.
Measured (sloneek, 117 field groups / 972 top-level fields)
A PHP-FPM slowlog profile over 3973 slow requests put 71 % of deepest-frame
samples inside
wpml/fp+wpml/collect.Staging, same box, alternating the file to hold load constant:
Rendered HTML is byte-identical once per-render
uniqueId()values arenormalised.
Why this is not #107
#107 was closed unmerged, on
the reasoning that a shared workaround would outlive the bug it worked around.
That reasoning was right there and does not carry here:
acf/load_referenceleak)Verified rather than assumed: on
proficiohub(acfml 3.0-b.1, sitepress5.0.0-b.1, wpml-string-translation 5.0.0-beta.1),
Strings/FieldHooks.php,Config.php,Translator.php,Traversable/Entity.php,Traversable/FieldGroup.php,Transformer/Translate.phpandPackage.phparebyte-identical to 2.2.4, and the bundled
wpml/fplibrary has identical MD5s.The same comparison does pick up 3.0-b.1's one change — the
remove_filterhook-name fix from #107 — so it is not a blind diff.
Proposal
A
StarterBaseopt-in property, default off, following$site_icon_tagsand its siblings:
Default off is not timidity. Switching it on is wrong for any site that renders
ACF field definitions to visitors:
acf_form()— placeholders, instructions and labels becomevisitor-facing
select/radio/checkboxchoice label rather thanits value
Neither is detectable from the kit, so the consuming site has to assert it. The
sloneek filter asserts exactly that in its own comment.
Retiring it later is a one-line default flip once ACFML memoises
Config::getFor().Prior art in this repo
Downstream implementation and full evidence: portadesign/sloneek#103.