fix(Helpers): valueless field objects read as empty, not raw arrays#68
Merged
Conversation
formatFields('option') on timber-kit >=1.8 surfaces options-page groups
via local-store walks; a field with no saved value arrives WITHOUT a
'value' key and fieldFormatter passed the raw ACF field-definition
array into templates - {{ x|typography }} then fatals on array input
(swallowed by the component fallback as 'template not found').
Missing 'value' key now returns FALSE (mapFields drops the key, isset
guards in templates work as pre-1.8); present-but-null value keeps the
documented repeater/flexible pass-through.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014N3Z17XG2gsXYRRqAjv4xP
f23586f to
236fa27
Compare
…eview P2) The null pass-through contract is only for explicit value => null (block previews); a field object with no value key at all must read as empty for every type. Negative tests added. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014N3Z17XG2gsXYRRqAjv4xP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Helpers::fieldFormatter()leaked raw ACF field-definition arrays into Twig templates for fields that were surfaced without a saved value. Templates piping such a value through a string filter (|typography,|e, …) fatalled withTypographyExtension::applyTypography(): Argument #1 ($string) must be of type Stringable|string|null, array given— and becauseStarterBase::render_namespaced_twig_template()catches any\Throwableand renders the alert fallback, the visible symptom was a completely misleading "Component template X.twig not found".How it was found (real-world repro)
Discovered on mairateam during the doc-efficiency alignment (mairateam#37), immediately after upgrading
parisek/timber-kit1.7.7 → 1.16.0:_xt()(fixed by the upgrade itself; the helpers ship since 1.8).contact-image.twig:118—{{ content.form_title|typography }}received an array.Root cause chain
getFieldObjectsForOptions(), 1.8+),formatFields('option')surfaces options-page groups that older versions never returned — including groups whose fields have no saved value in the DB (typical on dev/staging databases, fresh installs, or newly added option groups).valuekey entirely, or with an explicitvalue => null.isset()is false in both cases, so the raw definition (key,label,sub_fields,conditional_logic,_valid, …) flowed throughmapFields()(non-empty → kept) into$context['content']— where templateisset/truthiness guards happily pass an array and string filters fatal.Base.php:isset( $global_fields['form_title'] ) ? … : '') was written against "absent when unfilled" semantics. 1.16 silently changed that to "present as raw definition array".The fix — decision table
$fieldempty / not arrayFALSEFALSE(unchanged)typekey$fieldvaluekey missing (any type)FALSE→mapFields()drops the key; templateissetguards behave exactly like pre-1.8value => null, scalar/group/…FALSEvalue => null,repeater/flexible_content$fieldtest_repeater_null_value_returns_field_as_is+test_flexible_content_null_value_returns_field_as_is)valuepresent & non-nullCallers verified:
mapFields()dropsFALSEvia its! empty( $value )check → key absent, matching pre-1.8 behaviour.FALSEinto the nested value rather than pruning the key — safer than leaking arrays and consistent with existing nested behaviour.Review trail
Independent Codex review ran over the first iteration and found P2: the repeater/flexible exemption used
!isset()alone, so those two types with a missingvaluekey still leaked raw definitions. Fixed in the follow-up commit:array_key_exists( 'value', $field )now draws the line — missing key = empty for every type; the null pass-through applies only to an explicitvalue => nullon repeater/flexible. Codex also confirmed: guard order is sound for every type branch below it, no legitimate top-levelgroup value => nullflow needs the old pass-through, and top-level callers handleFALSEcorrectly.Tests
3 regression tests added to
FieldFormatterTest:test_typed_field_without_value_returns_false— options-page field object shape (with_name/_valid), novaluekey →FALSEtest_scalar_field_with_null_value_returns_false—textandgroupwithvalue => null→FALSEtest_repeater_and_flexible_with_missing_value_key_return_false— the Codex P2 negative caseFull suite: 848 tests, 1 527 assertions, green (4 pre-existing deprecations).
Downstream impact & rollout
🤖 Generated with Claude Code
https://claude.ai/code/session_014N3Z17XG2gsXYRRqAjv4xP