@@ -89,6 +89,14 @@ class ProperEscapingFunctionSniff extends Sniff {
8989 '= \\" ' ,
9090 ];
9191
92+ /**
93+ * Keep track of whether or not we're currently in the first statement of a short open echo tag.
94+ *
95+ * @var int|false Integer stack pointer to the end of the first statement in the current
96+ * short open echo tag or false when not in a short open echo tag.
97+ */
98+ private $ in_short_echo = false ;
99+
92100 /**
93101 * Returns an array of tokens this test wants to listen for.
94102 *
@@ -97,7 +105,10 @@ class ProperEscapingFunctionSniff extends Sniff {
97105 public function register () {
98106 $ this ->echo_or_concat_tokens += Tokens::$ emptyTokens ;
99107
100- return [ T_STRING ];
108+ return [
109+ T_STRING ,
110+ T_OPEN_TAG_WITH_ECHO ,
111+ ];
101112 }
102113
103114 /**
@@ -108,6 +119,35 @@ public function register() {
108119 * @return void
109120 */
110121 public function process_token ( $ stackPtr ) {
122+ /*
123+ * Short open echo tags will act as an echo for the first expression and
124+ * allow for passing multiple comma-separated parameters.
125+ * However, short open echo tags also allow for additional statements after, but
126+ * those have to be full PHP statements, not expressions.
127+ *
128+ * This snippet of code will keep track of whether or not we're in the first
129+ * expression in a short open echo tag.
130+ * $phpcsFile->findStartOfStatement() unfortunately is useless, as it will return
131+ * the first token in the statement, which can be anything - variable, text string -
132+ * without any indication of whether this is the start of a normal statement or
133+ * a short open echo expression.
134+ * So, if we used that, we'd need to walk back from every start of statement to
135+ * the previous non-empty to see if it is the short open echo tag.
136+ */
137+ if ( $ this ->tokens [ $ stackPtr ]['code ' ] === T_OPEN_TAG_WITH_ECHO ) {
138+ $ end_of_echo = $ this ->phpcsFile ->findNext ( [ T_SEMICOLON , T_CLOSE_TAG ], ( $ stackPtr + 1 ) );
139+ if ( $ end_of_echo === false ) {
140+ $ this ->in_short_echo = $ this ->phpcsFile ->numTokens ;
141+ } else {
142+ $ this ->in_short_echo = $ end_of_echo ;
143+ }
144+
145+ return ;
146+ }
147+
148+ if ( $ this ->in_short_echo !== false && $ this ->in_short_echo < $ stackPtr ) {
149+ $ this ->in_short_echo = false ;
150+ }
111151
112152 $ function_name = strtolower ( $ this ->tokens [ $ stackPtr ]['content ' ] );
113153
@@ -121,10 +161,14 @@ public function process_token( $stackPtr ) {
121161 return ;
122162 }
123163
124- $ ignore = $ this ->echo_or_concat_tokens ;
125- $ start_of_statement = $ this ->phpcsFile ->findStartOfStatement ( $ stackPtr , T_COMMA );
126- if ( $ this ->tokens [ $ start_of_statement ]['code ' ] === T_ECHO ) {
164+ $ ignore = $ this ->echo_or_concat_tokens ;
165+ if ( $ this ->in_short_echo !== false ) {
127166 $ ignore [ T_COMMA ] = T_COMMA ;
167+ } else {
168+ $ start_of_statement = $ this ->phpcsFile ->findStartOfStatement ( $ stackPtr , T_COMMA );
169+ if ( $ this ->tokens [ $ start_of_statement ]['code ' ] === T_ECHO ) {
170+ $ ignore [ T_COMMA ] = T_COMMA ;
171+ }
128172 }
129173
130174 $ html = $ this ->phpcsFile ->findPrevious ( $ ignore , $ stackPtr - 1 , null , true );
0 commit comments