fix: resolve 10 bugs in HTMLNode, HTMLTable, and TemplateCompiler#78
Merged
Conversation
added 11 commits
June 10, 2026 15:16
Replace str_replace('"', '\"', $val) with proper encoding of & and
" characters in quoted attribute values. This produces valid HTML output
where & is rendered as & and " as ".
Update existing test expectations to match the corrected output.
Closes #77
…ames setAttribute() now throws InvalidArgumentException when a structurally invalid attribute name is passed (e.g., names with spaces, quotes, =). Previously these were silently discarded. The TemplateCompiler uses a safeSetAttribute() helper to gracefully skip invalid names encountered during HTML parsing. Closes #76
Wrap the ob_start/require/ob_get_clean sequence in a try-catch block. If the template throws, ob_end_clean() is called before re-throwing the exception, preventing a permanently leaked output buffer. Closes #59
Use a static closure to execute the template require, preventing templates from accessing the TemplateCompiler instance via $this. Also eliminates variable name collisions by using __ prefixed names for internal variables. Closes #62
Replace manual $rows and $cols counters with methods that read from the actual DOM state. rows() returns childrenCount() and cols() returns the first row's child count. This eliminates desync when rows are added or removed via addRow() or inherited methods. Closes #71
Extend the existing <?php escaping to also handle <?= (short echo) and <? (short open tag) before splitting on <. This prevents InvalidNodeNameException when these sequences appear in parsed text. Closes #60
Copy attributes from parsed <html> and <body> elements to the HTMLDoc instance in parseHTMLDoc(). Previously these attributes (lang, dir, class, id, etc.) were silently discarded. Closes #56
Change the script content extraction regex from a fixed lookbehind (?<=<script>) to a capturing group <script[^>]*>(.*?)</script>. This correctly handles script tags with attributes like type or src, preventing their content from being corrupted by the HTML parser. Closes #55
Replace restricted ASCII character class [\t-!#-~] in attribute value regex with [^"]* (and [^']* for single quotes). This correctly matches UTF-8 multibyte characters and prevents > inside quoted attribute values from being interpreted as the tag closing delimiter. Closes #58
Extract comment bodies that contain < into placeholders before the
explode('<', ...) split, preserving the <!-- --> wrapper so the parser
still recognizes them as comment nodes. Only comments with < are
replaced to avoid disrupting normal comment parsing.
Closes #63
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## dev #78 +/- ##
============================================
- Coverage 98.44% 98.42% -0.03%
- Complexity 898 913 +15
============================================
Files 6 6
Lines 1799 1839 +40
============================================
+ Hits 1771 1810 +39
- Misses 28 29 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Fix array_indentation rule in php_cs.php.dist (was passed empty array instead of true). Run php-cs-fixer on all source files.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Fix 10 reported bugs across HTMLNode, HTMLTable, and TemplateCompiler classes.
Motivation
Multiple bugs were reported affecting attribute rendering, template parsing, and DOM state consistency. Fixes #55, #56, #57, #58, #59, #60, #62, #63, #71, #74, #75, #76, #77.
Changes
getStyle()breaking on CSS values with colons/semicolons inurl()(bug(HTMLNode): getStyle() breaks on CSS values containing colons or semicolons #75)fixBareLineFeed()trimming trailing whitespace (bug(HTMLNode): fixBareLineFeed() trims trailing whitespace from content #74)&and"in attribute values inopen()(bug(HTMLNode): Attribute values not HTML-encoded in open() output #77)InvalidArgumentExceptionfor invalid attribute names (bug(HTMLNode): setAttribute() accepts invalid attribute names #76)$thisusing static closure (bug(TemplateCompiler): $this exposed to PHP templates and variable name collisions #62)<?=and<?short tags in HTML text (bug(TemplateCompiler): <?= and <? in HTML text causes InvalidNodeNameException #60)<body>and<html>element attributes (bug(TemplateCompiler): Body and HTML element attributes lost when parsing full documents #56)>(bug(TemplateCompiler): UTF-8 attribute values containing > break the parser #58)<characters (bug(TemplateCompiler): HTML comments containing < are parsed incorrectly #63)How to Test / Verify
All fixes include dedicated PHPUnit tests. Run
composer testor:321 tests pass, 1016 assertions, zero failures.
Breaking Changes and Migration Steps
setAttribute()now throwsInvalidArgumentExceptionfor invalid attribute names (previously silently discarded)&are now rendered as&and"as"(correct per HTML spec)HTMLTable::rows()andcols()now derive from DOM state, not internal countersCode relying on silent attribute rejection should add try-catch. Code parsing
open()output expecting unescaped&in attributes needs updating.Checklist
composer fix-cs) — fixed config and appliedRelated issues
Closes #55, #56, #57, #58, #59, #60, #62, #63, #71, #74, #75, #76, #77