From c072ff85b393873a03c1c7437d322a270e42722d Mon Sep 17 00:00:00 2001 From: Dylan Pulver Date: Thu, 3 Sep 2026 20:24:11 +0300 Subject: [PATCH] Fix html_attr dropping style declarations whose value is zero InlineStyle::getValue() skipped a declaration when empty($value) was true, which also matches 0, 0.0 and '0'. Those are ordinary CSS values (opacity: 0, z-index: 0, margin: 0, flex-grow: 0), so they were silently dropped, and a style map containing only such declarations omitted the attribute entirely. The sibling SeparatedTokenList::getValue() already uses an explicit null/false test, so class token lists keep a 0 while style declarations did not. The numeric-key branch of InlineStyle itself never consulted empty(), so {style: ['opacity: 0']} printed while {style: {opacity: 0}} did not. --- CHANGELOG | 1 + extra/html-extra/HtmlAttr/InlineStyle.php | 4 +++- extra/html-extra/Tests/HtmlAttrTest.php | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 47e3a7240..85043d29f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ # 3.29.0 (2026-XX-XX) + * Fix `html_attr` dropping `style` declarations whose value is `0`, `0.0` or `'0'` * Fix the `default` filter fallback emitting an undefined variable warning when it uses the null-safe operator * Fix the `matches` operator silently treating PCRE execution errors as non-matches * Add the `HtmlExtension::htmlAttrValue()` method to resolve a single HTML attribute value the way the `html_attr` function renders it diff --git a/extra/html-extra/HtmlAttr/InlineStyle.php b/extra/html-extra/HtmlAttr/InlineStyle.php index 8aa36b646..8f16967bd 100644 --- a/extra/html-extra/HtmlAttr/InlineStyle.php +++ b/extra/html-extra/HtmlAttr/InlineStyle.php @@ -55,7 +55,9 @@ final class InlineStyle implements MergeableInterface, AttributeValueInterface { $style = ''; foreach ($this->value as $name => $value) { - if (empty($value) || true === $value) { + // `0`, `0.0` and `'0'` are valid CSS values, so only the values that carry + // no declaration at all are skipped here + if (null === $value || false === $value || true === $value || '' === $value || [] === $value) { continue; } if (is_numeric($name)) { diff --git a/extra/html-extra/Tests/HtmlAttrTest.php b/extra/html-extra/Tests/HtmlAttrTest.php index 38898b86f..b1318d70c 100644 --- a/extra/html-extra/Tests/HtmlAttrTest.php +++ b/extra/html-extra/Tests/HtmlAttrTest.php @@ -157,6 +157,27 @@ class HtmlAttrTest extends TestCase ], ]; + yield 'zero style declaration values are printed' => [ + 'style="opacity: 0; z-index: 0; margin: 0;"', + [ + ['style' => ['opacity' => 0, 'z-index' => '0', 'margin' => 0.0]], + ], + ]; + + yield 'null, false and empty string style declaration values are omitted' => [ + 'style="color: red;"', + [ + ['style' => ['a' => null, 'b' => false, 'c' => '', 'd' => true, 'color' => 'red']], + ], + ]; + + yield 'style attribute is omitted when every declaration is omitted' => [ + '', + [ + ['style' => ['a' => null, 'b' => false, 'c' => '']], + ], + ]; + yield 'merging style attributes overrides by key' => [ 'style="color: blue; font-size: 14px;"', [