1212namespace Twig \Extra \Html ;
1313
1414use Symfony \Component \Mime \MimeTypes ;
15+ use Twig \Environment ;
1516use Twig \Error \RuntimeError ;
1617use Twig \Extension \AbstractExtension ;
1718use Twig \TwigFilter ;
@@ -30,6 +31,7 @@ public function getFilters(): array
3031 {
3132 return [
3233 new TwigFilter ('data_uri ' , [$ this , 'dataUri ' ]),
34+ new TwigFilter ('html_attr_merge ' , 'twig_html_attr_merge ' ),
3335 ];
3436 }
3537
@@ -38,6 +40,7 @@ public function getFunctions(): array
3840 return [
3941 new TwigFunction ('html_classes ' , [self ::class, 'htmlClasses ' ]),
4042 new TwigFunction ('html_cva ' , [self ::class, 'htmlCva ' ]),
43+ new TwigFunction ('html_attr ' , 'twig_html_attr ' , ['needs_environment ' => true , 'is_safe ' => ['html ' ]]),
4144 ];
4245 }
4346
@@ -124,4 +127,75 @@ public static function htmlCva(array|string $base = [], array $variants = [], ar
124127 {
125128 return new Cva ($ base , $ variants , $ compoundVariants , $ defaultVariant );
126129 }
130+
131+ static function twig_html_attr_merge (...$ arrays ): array
132+ {
133+ $ result = [];
134+
135+ foreach ($ arrays as $ argNumber => $ array ) {
136+ if (!$ array ) {
137+ continue ;
138+ }
139+
140+ if (!twig_test_iterable ($ array )) {
141+ throw new RuntimeError (sprintf ('The "attr_merge" filter only works with arrays or "Traversable", got "%s" for argument %d. ' , \gettype ($ array ), $ argNumber + 1 ));
142+ }
143+
144+ $ array = twig_to_array ($ array );
145+
146+ foreach (['class ' , 'style ' , 'data ' ] as $ deepMergeKey ) {
147+ if (isset ($ array [$ deepMergeKey ])) {
148+ $ value = $ array [$ deepMergeKey ];
149+ unset($ array [$ deepMergeKey ]);
150+
151+ if (!twig_test_iterable ($ value )) {
152+ $ value = (array ) $ value ;
153+ }
154+
155+ $ value = twig_to_array ($ value );
156+
157+ $ result [$ deepMergeKey ] = array_merge ($ result [$ deepMergeKey ] ?? [], $ value );
158+ }
159+ }
160+
161+ $ result = array_merge ($ result , $ array );
162+ }
163+
164+ return $ result ;
165+ }
166+
167+ static function twig_html_attr (Environment $ env , ...$ args ): string
168+ {
169+ $ attr = twig_html_attr_merge (...$ args );
170+
171+ if (isset ($ attr ['class ' ])) {
172+ $ attr ['class ' ] = trim (implode (' ' , array_values ($ attr ['class ' ])));
173+ }
174+
175+ if (isset ($ attr ['style ' ])) {
176+ $ style = '' ;
177+ foreach ($ attr ['style ' ] as $ name => $ value ) {
178+ if (is_numeric ($ name )) {
179+ $ style .= $ value .'; ' ;
180+ } else {
181+ $ style .= $ name .': ' .$ value .'; ' ;
182+ }
183+ }
184+ $ attr ['style ' ] = trim ($ style );
185+ }
186+
187+ if (isset ($ attr ['data ' ])) {
188+ foreach ($ attr ['data ' ] as $ name => $ value ) {
189+ $ attr ['data- ' .$ name ] = $ value ;
190+ }
191+ unset($ attr ['data ' ]);
192+ }
193+
194+ $ result = '' ;
195+ foreach ($ attr as $ name => $ value ) {
196+ $ result .= twig_escape_filter ($ env , $ name , 'html_attr ' ).'=" ' .htmlspecialchars ($ value ).'" ' ;
197+ }
198+
199+ return trim ($ result );
200+ }
127201}
0 commit comments