Skip to content

Commit 5f5383a

Browse files
mlantzclaude
andcommitted
perf: optimize hot-path form rendering
Reduce per-field/per-attribute work on the form rendering path: - AttributeBuilder: replace the 168-element array rebuilt on every attribute with a keyed const map, swapping the O(n) in_array scan for an O(1) isset lookup. Use native str_starts_with instead of Str::of()->startsWith (no Stringable allocation per attribute) and array_unique/implode instead of a Collection. validHtmlAttributes() is kept for backward compatibility, now derived from the const. - FormAssets: cache the core.js read in a static property so it is read from disk once per request instead of on every compileScripts(). - FormMaker: cache the raw default.js/validation.js reads via a static cache; per-call config replacements are unchanged so output is identical. All output-preserving internal refactors. No public API change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 61b4d6c commit 5f5383a

3 files changed

Lines changed: 217 additions & 179 deletions

File tree

src/Builders/AttributeBuilder.php

Lines changed: 177 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,180 @@
22

33
namespace Grafite\Forms\Builders;
44

5-
use Illuminate\Support\Str;
6-
75
class AttributeBuilder
86
{
7+
/**
8+
* Valid HTML attributes as a lookup map for O(1) `isset()` checks.
9+
*/
10+
private const VALID_HTML_ATTRIBUTES = [
11+
'accept' => true,
12+
'accept-charset' => true,
13+
'accesskey' => true,
14+
'action' => true,
15+
'align' => true,
16+
'alt' => true,
17+
'async' => true,
18+
'autocomplete' => true,
19+
'autofocus' => true,
20+
'autoplay' => true,
21+
'bgcolor' => true,
22+
'border' => true,
23+
'charset' => true,
24+
'checked' => true,
25+
'cite' => true,
26+
'class' => true,
27+
'color' => true,
28+
'cols' => true,
29+
'colspan' => true,
30+
'content' => true,
31+
'contenteditable' => true,
32+
'controls' => true,
33+
'coords' => true,
34+
'data' => true,
35+
'datetime' => true,
36+
'default' => true,
37+
'defer' => true,
38+
'dir' => true,
39+
'dirname' => true,
40+
'disabled' => true,
41+
'download' => true,
42+
'draggable' => true,
43+
'enctype' => true,
44+
'for' => true,
45+
'form' => true,
46+
'formaction' => true,
47+
'headers' => true,
48+
'height' => true,
49+
'hidden' => true,
50+
'high' => true,
51+
'href' => true,
52+
'hreflang' => true,
53+
'http-equiv' => true,
54+
'id' => true,
55+
'ismap' => true,
56+
'kind' => true,
57+
'label' => true,
58+
'lang' => true,
59+
'list' => true,
60+
'loop' => true,
61+
'low' => true,
62+
'max' => true,
63+
'maxlength' => true,
64+
'media' => true,
65+
'method' => true,
66+
'min' => true,
67+
'multiple' => true,
68+
'muted' => true,
69+
'name' => true,
70+
'novalidate' => true,
71+
'onabort' => true,
72+
'onafterprint' => true,
73+
'onbeforeprint' => true,
74+
'onbeforeunload' => true,
75+
'onblur' => true,
76+
'oncanplay' => true,
77+
'oncanplaythrough' => true,
78+
'onchange' => true,
79+
'onclick' => true,
80+
'oncontextmenu' => true,
81+
'oncopy' => true,
82+
'oncuechange' => true,
83+
'oncut' => true,
84+
'ondblclick' => true,
85+
'ondrag' => true,
86+
'ondragend' => true,
87+
'ondragenter' => true,
88+
'ondragleave' => true,
89+
'ondragover' => true,
90+
'ondragstart' => true,
91+
'ondrop' => true,
92+
'ondurationchange' => true,
93+
'onemptied' => true,
94+
'onended' => true,
95+
'onerror' => true,
96+
'onfocus' => true,
97+
'onhashchange' => true,
98+
'oninput' => true,
99+
'oninvalid' => true,
100+
'onkeydown' => true,
101+
'onkeypress' => true,
102+
'onkeyup' => true,
103+
'onload' => true,
104+
'onloadeddata' => true,
105+
'onloadedmetadata' => true,
106+
'onloadstart' => true,
107+
'onmousedown' => true,
108+
'onmousemove' => true,
109+
'onmouseout' => true,
110+
'onmouseover' => true,
111+
'onmouseup' => true,
112+
'onmousewheel' => true,
113+
'onoffline' => true,
114+
'ononline' => true,
115+
'onpagehide' => true,
116+
'onpageshow' => true,
117+
'onpaste' => true,
118+
'onpause' => true,
119+
'onplay' => true,
120+
'onplaying' => true,
121+
'onpopstate' => true,
122+
'onprogress' => true,
123+
'onratechange' => true,
124+
'onreset' => true,
125+
'onresize' => true,
126+
'onscroll' => true,
127+
'onsearch' => true,
128+
'onseeked' => true,
129+
'onseeking' => true,
130+
'onselect' => true,
131+
'onstalled' => true,
132+
'onstorage' => true,
133+
'onsubmit' => true,
134+
'onsuspend' => true,
135+
'ontimeupdate' => true,
136+
'ontoggle' => true,
137+
'onunload' => true,
138+
'onvolumechange' => true,
139+
'onwaiting' => true,
140+
'onwheel' => true,
141+
'open' => true,
142+
'optimum' => true,
143+
'pattern' => true,
144+
'placeholder' => true,
145+
'poster' => true,
146+
'preload' => true,
147+
'readonly' => true,
148+
'rel' => true,
149+
'required' => true,
150+
'reversed' => true,
151+
'rows' => true,
152+
'rowspan' => true,
153+
'sandbox' => true,
154+
'scope' => true,
155+
'selected' => true,
156+
'shape' => true,
157+
'size' => true,
158+
'sizes' => true,
159+
'span' => true,
160+
'spellcheck' => true,
161+
'src' => true,
162+
'srcdoc' => true,
163+
'srclang' => true,
164+
'srcset' => true,
165+
'start' => true,
166+
'step' => true,
167+
'style' => true,
168+
'tabindex' => true,
169+
'target' => true,
170+
'title' => true,
171+
'translate' => true,
172+
'type' => true,
173+
'usemap' => true,
174+
'value' => true,
175+
'width' => true,
176+
'wrap' => true,
177+
];
178+
9179
/**
10180
* Build an HTML attribute string from an array.
11181
*
@@ -33,9 +203,9 @@ public function render($attributes, $name = null, $livewireEnabled = false, $liv
33203

34204
foreach ((array) $attributes as $key => $value) {
35205
if (
36-
in_array(strtolower($key), $this->validHtmlAttributes())
37-
|| Str::of($key)->startsWith('data-')
38-
|| Str::of($key)->startsWith('wire:')
206+
isset(self::VALID_HTML_ATTRIBUTES[strtolower($key)])
207+
|| str_starts_with($key, 'data-')
208+
|| str_starts_with($key, 'wire:')
39209
) {
40210
$element = $this->attributeElement($key, $value);
41211

@@ -45,7 +215,7 @@ public function render($attributes, $name = null, $livewireEnabled = false, $liv
45215
}
46216
}
47217

48-
return collect($html)->unique()->implode(' ');
218+
return implode(' ', array_unique($html));
49219
}
50220

51221
/**
@@ -76,173 +246,6 @@ public function attributeElement($key, $value)
76246

77247
public function validHtmlAttributes()
78248
{
79-
return [
80-
'accept',
81-
'accept-charset',
82-
'accesskey',
83-
'action',
84-
'align',
85-
'alt',
86-
'async',
87-
'autocomplete',
88-
'autofocus',
89-
'autoplay',
90-
'bgcolor',
91-
'border',
92-
'charset',
93-
'checked',
94-
'cite',
95-
'class',
96-
'color',
97-
'cols',
98-
'colspan',
99-
'content',
100-
'contenteditable',
101-
'controls',
102-
'coords',
103-
'data',
104-
'datetime',
105-
'default',
106-
'defer',
107-
'dir',
108-
'dirname',
109-
'disabled',
110-
'download',
111-
'draggable',
112-
'enctype',
113-
'for',
114-
'form',
115-
'formaction',
116-
'headers',
117-
'height',
118-
'hidden',
119-
'high',
120-
'href',
121-
'hreflang',
122-
'http-equiv',
123-
'id',
124-
'ismap',
125-
'kind',
126-
'label',
127-
'lang',
128-
'list',
129-
'loop',
130-
'low',
131-
'max',
132-
'maxlength',
133-
'media',
134-
'method',
135-
'min',
136-
'multiple',
137-
'muted',
138-
'name',
139-
'novalidate',
140-
'onabort',
141-
'onafterprint',
142-
'onbeforeprint',
143-
'onbeforeunload',
144-
'onblur',
145-
'oncanplay',
146-
'oncanplaythrough',
147-
'onchange',
148-
'onclick',
149-
'oncontextmenu',
150-
'oncopy',
151-
'oncuechange',
152-
'oncut',
153-
'ondblclick',
154-
'ondrag',
155-
'ondragend',
156-
'ondragenter',
157-
'ondragleave',
158-
'ondragover',
159-
'ondragstart',
160-
'ondrop',
161-
'ondurationchange',
162-
'onemptied',
163-
'onended',
164-
'onerror',
165-
'onfocus',
166-
'onhashchange',
167-
'oninput',
168-
'oninvalid',
169-
'onkeydown',
170-
'onkeypress',
171-
'onkeyup',
172-
'onload',
173-
'onloadeddata',
174-
'onloadedmetadata',
175-
'onloadstart',
176-
'onmousedown',
177-
'onmousemove',
178-
'onmouseout',
179-
'onmouseover',
180-
'onmouseup',
181-
'onmousewheel',
182-
'onoffline',
183-
'ononline',
184-
'onpagehide',
185-
'onpageshow',
186-
'onpaste',
187-
'onpause',
188-
'onplay',
189-
'onplaying',
190-
'onpopstate',
191-
'onprogress',
192-
'onratechange',
193-
'onreset',
194-
'onresize',
195-
'onscroll',
196-
'onsearch',
197-
'onseeked',
198-
'onseeking',
199-
'onselect',
200-
'onstalled',
201-
'onstorage',
202-
'onsubmit',
203-
'onsuspend',
204-
'ontimeupdate',
205-
'ontoggle',
206-
'onunload',
207-
'onvolumechange',
208-
'onwaiting',
209-
'onwheel',
210-
'open',
211-
'optimum',
212-
'pattern',
213-
'placeholder',
214-
'poster',
215-
'preload',
216-
'readonly',
217-
'rel',
218-
'required',
219-
'reversed',
220-
'rows',
221-
'rowspan',
222-
'sandbox',
223-
'scope',
224-
'selected',
225-
'shape',
226-
'size',
227-
'sizes',
228-
'span',
229-
'spellcheck',
230-
'src',
231-
'srcdoc',
232-
'srclang',
233-
'srcset',
234-
'start',
235-
'step',
236-
'style',
237-
'tabindex',
238-
'target',
239-
'title',
240-
'translate',
241-
'type',
242-
'usemap',
243-
'value',
244-
'width',
245-
'wrap',
246-
];
249+
return array_keys(self::VALID_HTML_ATTRIBUTES);
247250
}
248251
}

0 commit comments

Comments
 (0)