Skip to content

Commit 6ef95cf

Browse files
authored
Merge pull request #3 from imagewize/feature/unbalanced-html-tags-check
Feature/unbalanced Html Tags Check
2 parents a52a30b + dd7b269 commit 6ef95cf

6 files changed

Lines changed: 380 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ create-pr.sh
4343
# Testing
4444
/coverage/
4545
.phpunit.result.cache
46+
.phpunit.cache/
4647
phpunit.xml
4748

4849
# General

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [2.1.0] - 2026-05-08
8+
9+
### Added
10+
11+
- **Unbalanced HTML tags check** in `BaseRules` (`src/Compliance/Rules/BaseRules.php`) — Detects unbalanced HTML tags (`<div>`, `<ul>`, `<ol>`, `<li>`, `<figure>`, `<figcaption>`, `<section>`, `<article>`, `<header>`, `<footer>`, `<nav>`, `<aside>`, `<main>`, `<p>`) in pattern files. Reports violations with opening/closing tag counts to help authors identify mismatches. Addresses issue #2 where missing `</div>` tags caused DOM nesting issues but went undetected by both pt-cli and `wp pattern validate`.
12+
- **PHPUnit test suite** (`tests/`) — First test suite for the project with comprehensive tests for the unbalanced HTML tags rule:
13+
- `tests/bootstrap.php` — Test bootstrap file
14+
- `tests/Compliance/Rules/BaseRulesTest.php` — Tests for BaseRules including unbalanced HTML tags detection
15+
16+
### Changed
17+
18+
- `.gitignore` — Added `.phpunit.cache/` directory
19+
- `README.md` — Added "Balanced HTML tags" rule to the Base rules table
20+
721
## [2.0.0] - 2026-05-08
822

923
### Added

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ Config lookup order:
204204
| No hardcoded font sizes (CSS px/rem/em) | No | Error |
205205
| No spacer blocks | No | Error |
206206
| Margin reset on alignfull patterns | No | Error |
207+
| Balanced HTML tags (`<div>`, `<ul>`, `<ol>`, `<li>`, `<figure>`, etc.) | No | Error |
207208
| Responsive grid for 3+ columns (warn on `wp:columns`) | No | Warning |
208209
| No hardcoded media IDs | No | Error |
209210
| Translated strings (HTML tags + alt attributes) | No | Error |

src/Compliance/Rules/BaseRules.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public function check(string $filePath): array
6969
$violations = array_merge($violations, $this->checkFontPresetClasses($content));
7070
$violations = array_merge($violations, $this->checkBlockGapInlineStyles($content));
7171
$violations = array_merge($violations, $this->checkUntranslatedStrings($content));
72+
$violations = array_merge($violations, $this->checkUnbalancedHtmlTags($content));
7273

7374
if ($rules['requirePatternName'] ?? false) {
7475
$isTemplate = str_starts_with($patternName, 'template-');
@@ -898,4 +899,45 @@ function ($m) use ($property) {
898899
1
899900
);
900901
}
902+
903+
/**
904+
* Check for unbalanced HTML tags in pattern files.
905+
* This catches missing closing tags like </div> that can cause DOM nesting issues.
906+
* Only checks structural HTML outside of block comments and PHP code.
907+
*
908+
* Note: <p> tags are included but WordPress block patterns use explicit <p class="wp-block-...">
909+
* with matching </p>, so false positives are unlikely. The regex [^>]* correctly avoids
910+
* matching <?php since ? != p after <\s*.
911+
*/
912+
private function checkUnbalancedHtmlTags(string $content): array
913+
{
914+
$violations = [];
915+
916+
// Structural tags that should be balanced in pattern HTML
917+
// Note: <li> is included even though HTML5 allows implicit closing, because
918+
// WP block patterns use wp:list-item which explicitly closes them
919+
$tags = ['div', 'ul', 'ol', 'li', 'figure', 'figcaption', 'section', 'article', 'header', 'footer', 'nav', 'aside', 'main', 'p'];
920+
921+
foreach ($tags as $tag) {
922+
// Count opening tags (match <div, <div>, <div class="foo">, etc.)
923+
$openingCount = preg_match_all('/<\s*' . $tag . '\b[^>]*>/i', $content);
924+
925+
// Count closing tags (match </div>, </div >, etc.)
926+
$closingCount = preg_match_all('/<\/\s*' . $tag . '\s*>/i', $content);
927+
928+
if ($openingCount !== $closingCount) {
929+
$violations[] = $this->violation(
930+
'unbalanced-html-tags',
931+
sprintf(
932+
'Unbalanced <%s> tags: %d opening, %d closing',
933+
$tag,
934+
$openingCount,
935+
$closingCount
936+
)
937+
);
938+
}
939+
}
940+
941+
return $violations;
942+
}
901943
}

0 commit comments

Comments
 (0)