@@ -39,6 +39,9 @@ class HtmlSelfClosingTagsSniff implements Sniff
3939 'wbr ' ,
4040 ];
4141
42+ /** @var int */
43+ private int $ lastPointer = 0 ;
44+
4245 /**
4346 * @inheritDoc
4447 */
@@ -68,15 +71,32 @@ public function process(File $phpcsFile, $stackPtr)
6871 if (preg_match_all ('$<(\w{2,})\s?[^<]*\/>$ ' , $ html , $ matches , PREG_SET_ORDER )) {
6972 foreach ($ matches as $ match ) {
7073 if (!in_array ($ match [1 ], self ::HTML_VOID_ELEMENTS )) {
71- $ fix = $ phpcsFile ->addFixableError (
72- 'Avoid using self-closing tag with non-void html element '
73- . ' - " ' . $ match [0 ] . PHP_EOL ,
74- null ,
75- 'HtmlSelfClosingNonVoidTag '
76- );
74+ $ ptr = $ this ->findPointer ($ phpcsFile , $ match [0 ]);
75+ if ($ ptr ) {
76+ $ fix = $ phpcsFile ->addFixableError (
77+ 'Avoid using self-closing tag with non-void html element '
78+ . ' - " ' . $ match [0 ] . PHP_EOL ,
79+ $ ptr ,
80+ 'HtmlSelfClosingNonVoidTag '
81+ );
82+
83+ if ($ fix ) {
84+ $ token = $ phpcsFile ->getTokens ()[$ ptr ];
85+ $ original = $ match [0 ];
86+ $ replacement = str_replace ('/> ' , '></ ' . $ match [1 ] . '> ' , $ original );
87+ $ phpcsFile ->fixer ->replaceToken (
88+ $ ptr ,
89+ str_replace ($ original , $ replacement , $ token ['content ' ])
90+ );
7791
78- if ($ fix ) {
79- $ this ->fixClosingTag ($ phpcsFile , $ match );
92+ }
93+ } else {
94+ $ phpcsFile ->addError (
95+ 'Avoid using self-closing tag with non-void html element '
96+ . ' - " ' . $ match [0 ] . PHP_EOL ,
97+ null ,
98+ 'HtmlSelfClosingNonVoidTag '
99+ );
80100 }
81101 }
82102 }
@@ -86,24 +106,27 @@ public function process(File $phpcsFile, $stackPtr)
86106 /**
87107 * Apply a fix for the detected issue
88108 *
89- * @param File $phpcsFile
90- * @param array $match
109+ * @param File $phpcsFile
110+ * @param string $needle
111+ * @return int|null
91112 */
92- private function fixClosingTag (File $ phpcsFile , array $ match ): void
113+ private function findPointer (File $ phpcsFile , string $ needle ): ? int
93114 {
94115 foreach ($ phpcsFile ->getTokens () as $ ptr => $ token ) {
95- if ($ token [ ' code ' ] !== T_INLINE_HTML ) {
116+ if ($ ptr < $ this -> lastPointer ) {
96117 continue ;
97118 }
98119
99- if (str_contains ($ token ['content ' ], $ match [0 ])) {
100- $ original = $ match [0 ];
101- $ replacement = str_replace ('/> ' , '></ ' . $ match [1 ] . '> ' , $ original );
102-
103- $ phpcsFile ->fixer ->replaceToken ($ ptr , str_replace ($ original , $ replacement , $ token ['content ' ]));
120+ if ($ token ['code ' ] !== T_INLINE_HTML ) {
121+ continue ;
122+ }
104123
105- return ;
124+ if (str_contains ($ token ['content ' ], $ needle )) {
125+ $ this ->lastPointer = $ ptr ;
126+ return $ ptr ;
106127 }
107128 }
129+
130+ return null ;
108131 }
109132}
0 commit comments