Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
957a3e8
First sketching on templates
sirreal Dec 23, 2025
afc63eb
DROPME: Collect examples
sirreal Feb 2, 2026
75ecbe7
Introduce test suite based on existing examples
sirreal Feb 2, 2026
52836b9
yield + nowdoc
sirreal Feb 2, 2026
07a42d5
lints
sirreal Feb 2, 2026
336524f
test tweaks
sirreal Feb 2, 2026
c1a9050
Fix numeric array indexes
sirreal Feb 3, 2026
978d835
Add test for HTML that could produce a tag after modification
sirreal Feb 3, 2026
81f731e
Add test to prevent attribute mis-interpretation after replacement
sirreal Feb 3, 2026
cdedfb7
Move serialize_token to tag processor class (for normalization of tex…
sirreal Feb 3, 2026
e8f420c
Add attribute behavior testing
sirreal Feb 3, 2026
75cf458
working proof-of-concept
sirreal Feb 3, 2026
bf7f038
class cleanup and lints
sirreal Feb 4, 2026
df9deab
Add message on disallowed type
sirreal Feb 4, 2026
5a9cb43
Cleanup test + lints
sirreal Feb 4, 2026
f30d721
Add multiple template tests
sirreal Feb 4, 2026
63fa332
Improve test names and specificity
sirreal Feb 4, 2026
a451ed4
Improve test structure and names
sirreal Feb 4, 2026
48ff319
Refactor WP_HTML_Template to use composition over inheritance
sirreal Feb 4, 2026
e04d7c0
Remove unused preg_attribute_replace_callback method from WP_HTML_Tem…
sirreal Feb 4, 2026
65f62a9
Add more tests
sirreal Feb 4, 2026
5d58abf
lints
sirreal Feb 4, 2026
d57d0c4
Rework interface
sirreal Feb 4, 2026
bd8740e
Update tests for new API
sirreal Feb 4, 2026
4eaf11d
Add placeholder compilation skeleton to WP_HTML_Template
sirreal Feb 4, 2026
8229efa
Implement text placeholder extraction in compile()
sirreal Feb 4, 2026
ca24391
Add attribute placeholder extraction with context promotion
sirreal Feb 4, 2026
49978d4
Add validation warnings to bind()
sirreal Feb 4, 2026
d56435e
Refactor render() to use compiled placeholder data
sirreal Feb 4, 2026
2b4bf1a
Fix attribute text escaping and add trailing segment handling
sirreal Feb 4, 2026
f5f272d
Revert "Move serialize_token to tag processor class (for normalizatio…
sirreal Feb 5, 2026
11ab281
Switch to use HTML Processor
sirreal Feb 5, 2026
06a28c6
Use assertEqualHTML in tests
sirreal Feb 6, 2026
5862ee4
Add newline in PRE tests
sirreal Feb 6, 2026
0bda31b
Update PRE leading newline test
sirreal Feb 6, 2026
d99bb22
Add more test cases
sirreal Feb 6, 2026
b89a3ff
Remove special handling for PRE, LISTING tags
sirreal Feb 6, 2026
2fcc211
Test tweaks and notes
sirreal Feb 6, 2026
3e2757f
HTML API: Add $edits and $placeholder_names properties to WP_HTML_Tem…
sirreal Feb 6, 2026
45c01dd
HTML API: Populate $edits with text normalizations during compile
sirreal Feb 6, 2026
b316285
HTML API: Populate $edits with text placeholders during compile
sirreal Feb 6, 2026
9d15dfa
HTML API: Pre-compute attribute escapes at compile time
sirreal Feb 6, 2026
75f0d95
HTML API: Populate $edits with attribute placeholders during compile
sirreal Feb 6, 2026
18c925c
HTML API: Refactor render() to use unified $edits array
sirreal Feb 6, 2026
13323b3
HTML API: Refactor bind() to use $placeholder_names for validation
sirreal Feb 6, 2026
7747f6d
HTML API: Remove legacy $text_normalizations and $attr_escapes arrays
sirreal Feb 6, 2026
fc75174
HTML API: Add TODO for future $compiled removal
sirreal Feb 6, 2026
12b1f63
HTML API: Fix code style issues in WP_HTML_Template
sirreal Feb 6, 2026
8160d60
HTML API: Remove redundant $compiled variable from WP_HTML_Template
sirreal Feb 6, 2026
03e6104
lints
sirreal Feb 9, 2026
c275c86
Document $replacements
sirreal Feb 9, 2026
d6c1c24
Use WP_HTML_Text_Replacement class
sirreal Feb 9, 2026
14d5df9
Rely on HTML API text replacement
sirreal Feb 9, 2026
fa9d84e
Tests: Refactor static text escaping test to use data provider
sirreal Feb 9, 2026
26a4532
Remove dangling docblock
sirreal Feb 9, 2026
6ec99ea
Remove is_compiled (derive)
sirreal Feb 9, 2026
73ff19e
Add replacement warning test
sirreal Feb 9, 2026
51640e6
remove get_placeholders
sirreal Feb 9, 2026
ee24adc
Drop numeric placeholders / list replacements
sirreal Feb 10, 2026
7187586
Tests: Add coverage for invalid replacement type rejection
sirreal Feb 10, 2026
a8ca49d
Add status assessment
sirreal Feb 10, 2026
7cb1f74
Add multiple placeholder tests
sirreal Feb 10, 2026
623e938
Add more attribute tests, multiple attributes
sirreal Feb 10, 2026
9c3ee7d
Update status
sirreal Feb 10, 2026
0e81466
Add boolean attribute support to WP_HTML_Template
sirreal Feb 10, 2026
e8eb681
update status
sirreal Feb 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions STATUS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
## Critical Analysis: WP_HTML_Template vs. Ticket #60229 Requirements

I've examined ticket #60229, the current branch implementation, and the test suite. Here's my analysis:

---

### What's Working Well

**1. Core Safety Goal Achieved**
The implementation delivers on the primary promise: context-aware auto-escaping. The code correctly escapes `&`, `<`, `>`, `"`, `'` in both text and attribute contexts. Tests confirm placeholders can't be injected via replacement values (non-recursive replacement).

**2. Funky Comment Syntax**
The `</%placeholder>` syntax is a smart choice—it's valid HTML (parsed as a funky comment), can't be nested by construction, and is visually distinctive. The parsing handles whitespace around names correctly.

**3. Nested Templates**
Template composition (`WP_HTML_Template` as replacement value) works for text context with proper escaping propagation. The rejection of templates in attribute context is the right safety call.

**4. Compile-once Design**
Lazy compilation with cached edits (`$edits` array) is efficient for template reuse.

**5. Boolean Attribute Support**
Supply `true` to create a boolean attribute (`disabled="</%d>"` + `true` → `disabled`), or `false`/`null` to remove an attribute entirely. Only works for whole-attribute placeholders—partial placeholders reject boolean values.

---

### What's Missing from the Ticket Requirements

**1. No URL Escaping (Ticket TODO)**
The ticket explicitly says "does not escape URLs differently than other attributes." The XSS test shows `javascript:alert("xss")` only escapes quotes—no `esc_url()` equivalent. This is a security gap for `href`/`src` attributes.

**2. No Attribute Spread**
Ticket comment 9 discusses "spread" attributes for making tags placeholders. Not implemented.

**3. Missing Output Format Methods**
Ticket TODO lists `->final_output_to_browser()`, `->final_output_to_plaintext()`, `->final_output_to_markdown()`, etc. None exist.

**4. Embed Replacement in Tag Processor (Ticket TODO)**
The ticket wants replacement embedded in the Tag Processor. Current implementation uses a separate class with its own parsing pass.

---

### Overlooked Issues in the Ticket

**1. RAWTEXT/RCDATA Element Handling Is Half-Baked**
Tests show placeholders inside `<script>`, `<style>` are preserved literally, while `<title>`, `<textarea>` escape them as text. Neither allows actual replacement. The ticket doesn't acknowledge this limitation clearly. If I write a template for a script tag's content, I'd expect placeholders to work.

**2. Table Context Parsing**
Test explicitly skipped: "IN TABLE templates are not supported yet." HTML parsing rules for tables are complex—implicit elements, foster parenting. The ticket doesn't address this at all. Tables are everywhere in WordPress admin.

**3. No i18n Integration**
The ticket mentions "translation" as a sigil use case, and gziolo's comment asks about `createInterpolateElement` parity. Zero implementation of translation awareness. For WordPress core, this is a big miss.

**4. Performance Not Benchmarked**
Normalization runs twice: once in `compile()` for text detection, once in `render()` for final output. For high-frequency template rendering (e.g., list items), this could add up.

**5. Error Handling Philosophy Unclear**
`render()` returns `false` on errors, but `bind()` uses `_doing_it_wrong()` warnings and still returns a template. Mixed signals—should errors be fatal or recoverable? The test gaps document lists many untested failure cases.

---

### Gaps in Test Coverage (From Test-Gaps.md + My Observations)

**High Priority Missing:**

- ~~Non-string replacement values (integers, arrays, nulls) → should fail gracefully~~ ✅ Tests exist
- ~~Multiple placeholders in single attribute (e.g., `href="</%scheme>://</%host>"`)~~ ✅ Works, tests added
- Template immutability verification (`bind()` returns new instance)

**The PRE newline test is skipped** but the behavior matters—WordPress often outputs preformatted code.

**No tests for:**

- Very long templates/replacements (memory/performance)
- Deeply nested templates (stack depth)
- Unicode/multibyte characters in placeholder names
- SVG/MathML elements

---

### API Design Questions

**1. Why `from()` → `bind()` → `render()` instead of a single call?**
For reusable templates, the separation makes sense. But most WordPress use cases are one-shot renders. A convenience method like `::render($template, $replacements)` would be more ergonomic.

**2. `render()` Returns `string|false`—Is That Right?**
Returning `false` on error is classic WordPress, but it means every call site needs error checking. Exceptions or a Result type would be safer, though breaking with WP conventions.

**3. What About Streaming/Chunked Output?**
For large templates, building the entire string in memory before output is wasteful. No consideration of echo-as-you-go.

---

### What's the Competition Doing That WP Isn't?

- **Blade/Twig**: Rich conditionals, loops, inheritance. WP_HTML_Template is deliberately minimal.
- **htmx/alpine patterns**: Server-side templating with client-side enhancement. No thought given here.
- **Lit/React SSR**: Hydration markers. Not applicable, but WP could learn from structured output strategies.

The ticket's philosophy is "prefer trust and safety over features"—valid, but means this won't replace most template use cases. It's targeted at safe HTML generation, not a full templating language.

---

### Summary Table

| Requirement | Status | Notes |
| -------------------------- | ------ | ---------------------------------- |
| Context-aware escaping | ✅ | Works for text and attributes |
| Funky comment placeholders | ✅ | Clean implementation |
| Nested HTML via Templates | ✅ | Text context only |
| Boolean attributes | ✅ | true/false/null for whole-attr |
| URL escaping | ❌ | Only generic escaping |
| Attribute spread | ❌ | Not implemented |
| Output format methods | ❌ | Not implemented |
| Tag Processor integration | ❌ | Separate class |
| Table context support | ❌ | Explicitly unsupported |
| i18n integration | ❌ | Not addressed |
| RAWTEXT/RCDATA replacement | ❌ | Placeholders don't work inside |

2 changes: 1 addition & 1 deletion src/wp-includes/html-api/class-wp-html-tag-processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,7 @@ class WP_HTML_Tag_Processor {
* @since 6.2.0
* @var WP_HTML_Attribute_Token[]
*/
private $attributes = array();
protected $attributes = array();

/**
* Tracks spans of duplicate attributes on a given tag, used for removing
Expand Down
Loading
Loading