Skip to content

Labels without escaping - #202

Merged
lippserd merged 4 commits into
mainfrom
escaped-form-labels
Jul 30, 2026
Merged

lippserd merged 4 commits into
mainfrom
escaped-form-labels

Conversation

@jrauh01

@jrauh01 jrauh01 commented Jul 17, 2026 •

Copy link
Copy Markdown
Contributor

LabelDecorator and FieldsetDecorator previously wrapped every label in Text, which HTML-escapes its content on render. Passing a ValidHtml label therefore caused its markup to be double-escaped, and passing any ValidHtml that does not implement __toString() caused a fatal error during the string coercion in Text::setContent().

Both decorators now detect ValidHtml labels and use them directly, wrapping only plain strings in Text. FieldsetDecorator requires its own fix because fieldset elements bypass LabelDecorator entirely and render the legend themselves.

The $label property, setLabel(), and getLabel() type annotations are updated to reflect that labels may be either a plain string or a ValidHtml object.

Tests cover string escaping, HtmlString pass-through, and a render-only ValidHtml implementation (no __toString()) for both decorators.

Comment thread src/FormDecoration/LabelDecorator.php Resolved
@jrauh01
jrauh01 requested a review from lippserd July 20, 2026 08:09
@jrauh01 jrauh01 changed the title Add escaped option to skip HTML escaping on labels Labels without escaping Jul 21, 2026

@lippserd lippserd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address the following before merging.

Please update the public label contract to include ValidHtml. LabelDecorator and the new test accept it, but FormElement::getLabel() and BaseFormElement::$label/setLabel() still advertise strings, so PHPStan considers the new branch impossible and statically checked callers cannot use the feature. Document that strings are escaped while ValidHtml is trusted markup.

Please apply the same ValidHtml pass-through to fieldset legends. Fieldsets bypass LabelDecorator, and FieldsetDecorator still wraps every label with Text::create(); browser verification showed HtmlString markup being escaped, while a render-only ValidHtml implementation fails during string conversion. Add coverage for string, HtmlString, and render-only ValidHtml labels.

Please update the PR description to match the final design. It still promises the removed escaped option and accessors, while the current diff uses automatic ValidHtml detection.

Comment thread src/FormDecoration/LabelDecorator.php Resolved
@jrauh01

jrauh01 commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

I extended FieldsetDecorator with the same ValidHtml pass-through that LabelDecorator already had, updated the public label contract to include ValidHtml, and added tests for string escaping, HtmlString pass-through, and a render-only ValidHtml implementation in FieldsetDecoratorTest.

@jrauh01
jrauh01 requested a review from lippserd July 27, 2026 13:55

@lippserd lippserd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to the requested changes:

SubmitElement::setLabel() with no @param annotation inherits the widened ValidHtml contract but its pipeline is string-only. A caller who passes a ValidHtml object based on the advertised interface will silently break form submission detection: hasBeenPressed() compares the stored object against a string with ===, which is always false for objects. Add a narrowing @param string $label annotation to SubmitElement::setLabel(). A brief docblock sentence explaining that the label populates a value attribute and must therefore be a plain string is sufficient.

I am not fully aware of the deprecated DivDecorator usage but its fieldset path throws TypeError for non-Stringable ValidHtml. I would apply the same guard used in FieldsetDecorator: replace Text::create($label) with $label instanceof ValidHtml ? $label : new Text($label).

Comment thread src/FormElement/BaseFormElement.php Resolved
@jrauh01

jrauh01 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Narrowed the type via @param annotation for SubmitElement::setLabel() and added the conditional logic to render ValidHtml for fieldset legends via DivDecorator.

@jrauh01
jrauh01 requested a review from lippserd July 29, 2026 12:29
@lippserd
lippserd force-pushed the escaped-form-labels branch from 367741e to 62ca031 Compare July 30, 2026 13:52

@lippserd lippserd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please clean up the commit history.

@jrauh01
jrauh01 force-pushed the escaped-form-labels branch 3 times, most recently from 27cc91d to cb4f4fa Compare July 30, 2026 15:02
@jrauh01

jrauh01 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Commit history is clean now.

jrauh01 added 4 commits July 30, 2026 17:06
Previously all labels were wrapped in `Text`, which HTML-escapes the content
even when the caller passed a pre-rendered `ValidHtml` object. Plain strings
are still escaped. `ValidHtml` instances are now passed through directly.
`SubmitElement::setLabel()` keeps a `string`-only override: the label
populates the `value` attribute and must match the submitted form value,
so it cannot be a `ValidHtml` object.
`LabelDecorator` already handles `ValidHtml` labels by using them as-is
and only wrapping plain strings in `Text`. `FieldsetDecorator` was calling
`Text::create()` unconditionally, which coerces the label to string via
`setContent()`, breaking any `ValidHtml` implementation that does not also
implement `__toString()`.
Wrapping the label in `Text` cast it to a string first. A fieldset legend
therefore raised `Object of class X could not be converted to string` for any
`ValidHtml` label lacking `__toString()`, and rendered the markup of the
stringable ones literally. The label branch never had this problem because
`Html::tag()` passes `ValidHtml` content through on its own.
@jrauh01
jrauh01 force-pushed the escaped-form-labels branch from cb4f4fa to 9d4869d Compare July 30, 2026 15:06
@lippserd
lippserd merged commit aecd750 into main Jul 30, 2026
13 checks passed
@lippserd
lippserd deleted the escaped-form-labels branch July 30, 2026 15:09
@lippserd lippserd added this to the v0.12.0 milestone Oct 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants