55use PHP_CodeSniffer \Files \File ;
66use PHP_CodeSniffer \Sniffs \Sniff ;
77use UnexpectedValueException ;
8- use function preg_match ;
8+ use function count ;
9+ use function in_array ;
10+ use function is_array ;
911use function sprintf ;
12+ use function strpos ;
13+ use function token_get_all ;
1014use const T_DOUBLE_QUOTED_STRING ;
1115use const T_HEREDOC ;
16+ use const T_VARIABLE ;
1217
1318class DisallowVariableParsingSniff implements Sniff
1419{
@@ -19,10 +24,6 @@ class DisallowVariableParsingSniff implements Sniff
1924
2025 public const CODE_DISALLOWED_SIMPLE_SYNTAX = 'DisallowedSimpleSyntax ' ;
2126
22- private const DOLLAR_CURLY_SYNTAX_PATTERN = '~\${[\w\[\]]+}~ ' ;
23- private const CURLY_DOLLAR_SYNTAX_PATTERN = '~{\$[\w\[\]\->]+}~ ' ;
24- private const SIMPLE_SYNTAX_PATTERN = '~(?<!{|\[)\$[\w\[\]\->]+(?!})~ ' ;
25-
2627 /** @var bool */
2728 public $ disallowDollarCurlySyntax = true ;
2829
@@ -56,48 +57,98 @@ public function process(File $phpcsFile, $stringPointer): void
5657 $ tokens = $ phpcsFile ->getTokens ();
5758 $ tokenContent = $ tokens [$ stringPointer ]['content ' ];
5859
59- // Cover strings where ${...} syntax is used
60- if ($ this ->disallowDollarCurlySyntax && preg_match (self ::DOLLAR_CURLY_SYNTAX_PATTERN , $ tokenContent , $ invalidFragments ) === 1 ) {
61- foreach ($ invalidFragments as $ fragment ) {
62- $ phpcsFile ->addError (
63- sprintf (
64- 'Using variable syntax "${...}" inside string is disallowed as syntax "${...}" is deprecated as of PHP 8.2, found "%s". ' ,
65- $ fragment
66- ),
67- $ stringPointer ,
68- self ::CODE_DISALLOWED_DOLLAR_CURLY_SYNTAX
69- );
70- }
60+ if (strpos ($ tokenContent , '$ ' ) === false ) {
61+ return ;
7162 }
7263
73- // Cover strings where {$...} syntax is used
74- if ($ this ->disallowCurlyDollarSyntax && preg_match (self ::CURLY_DOLLAR_SYNTAX_PATTERN , $ tokenContent , $ invalidFragments ) === 1 ) {
75- foreach ($ invalidFragments as $ fragment ) {
76- $ phpcsFile ->addError (
77- sprintf (
78- 'Using variable syntax "{$...}" inside string is disallowed, found "%s". ' ,
79- $ fragment
80- ),
81- $ stringPointer ,
82- self ::CODE_DISALLOWED_CURLY_DOLLAR_SYNTAX
83- );
64+ $ stringTokens = $ tokens [$ stringPointer ]['code ' ] === T_HEREDOC
65+ ? token_get_all ('<?php " ' . $ tokenContent . '" ' )
66+ : token_get_all ('<?php ' . $ tokenContent );
67+
68+ for ($ i = 0 ; $ i < count ($ stringTokens ); $ i ++) {
69+ $ stringToken = $ stringTokens [$ i ];
70+
71+ if (!is_array ($ stringToken )) {
72+ continue ;
8473 }
85- }
8674
87- // Cover strings where $... syntax is used
88- // phpcs:disable SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed
89- if ($ this ->disallowSimpleSyntax && preg_match (self ::SIMPLE_SYNTAX_PATTERN , $ tokenContent , $ invalidFragments ) === 1 ) {
90- foreach ($ invalidFragments as $ fragment ) {
91- $ phpcsFile ->addError (
92- sprintf (
93- 'Using variable syntax "$..." inside string is disallowed, found "%s". ' ,
94- $ fragment
95- ),
96- $ stringPointer ,
97- self ::CODE_DISALLOWED_SIMPLE_SYNTAX
98- );
75+ if ($ this ->disallowDollarCurlySyntax && $ this ->getTokenContent ($ stringToken ) === '${ ' ) {
76+ $ usedVariable = $ stringToken [1 ];
77+
78+ for ($ j = $ i + 1 ; $ j < count ($ stringTokens ); $ j ++) {
79+ $ usedVariable .= $ this ->getTokenContent ($ stringTokens [$ j ]);
80+
81+ if ($ this ->getTokenContent ($ stringTokens [$ j ]) === '} ' ) {
82+ $ phpcsFile ->addError (
83+ sprintf (
84+ 'Using variable syntax "${...}" inside string is disallowed as syntax "${...}" is deprecated as of PHP 8.2, found "%s". ' ,
85+ $ usedVariable
86+ ),
87+ $ stringPointer ,
88+ self ::CODE_DISALLOWED_DOLLAR_CURLY_SYNTAX
89+ );
90+
91+ break ;
92+ }
93+ }
94+ } elseif ($ stringToken [0 ] === T_VARIABLE ) {
95+ if ($ this ->disallowCurlyDollarSyntax && $ this ->getTokenContent ($ stringTokens [$ i - 1 ]) === '{ ' ) {
96+ $ usedVariable = $ stringToken [1 ];
97+
98+ for ($ j = $ i + 1 ; $ j < count ($ stringTokens ); $ j ++) {
99+ $ stringTokenContent = $ this ->getTokenContent ($ stringTokens [$ j ]);
100+ if ($ stringTokenContent === '} ' ) {
101+ break ;
102+ }
103+
104+ $ usedVariable .= $ stringTokenContent ;
105+ }
106+
107+ $ phpcsFile ->addError (
108+ sprintf (
109+ 'Using variable syntax "{$...}" inside string is disallowed, found "{%s}". ' ,
110+ $ usedVariable
111+ ),
112+ $ stringPointer ,
113+ self ::CODE_DISALLOWED_CURLY_DOLLAR_SYNTAX
114+ );
115+ } elseif ($ this ->disallowSimpleSyntax ) {
116+ $ error = true ;
117+
118+ for ($ j = $ i - 1 ; $ j >= 0 ; $ j --) {
119+ $ stringTokenContent = $ this ->getTokenContent ($ stringTokens [$ j ]);
120+
121+ if (in_array ($ stringTokenContent , ['{ ' , '${ ' ], true )) {
122+ $ error = false ;
123+ break ;
124+ }
125+
126+ if ($ stringTokenContent === '} ' ) {
127+ break ;
128+ }
129+ }
130+
131+ if ($ error ) {
132+ $ phpcsFile ->addError (
133+ sprintf (
134+ 'Using variable syntax "$..." inside string is disallowed, found "%s". ' ,
135+ $ this ->getTokenContent ($ stringToken )
136+ ),
137+ $ stringPointer ,
138+ self ::CODE_DISALLOWED_SIMPLE_SYNTAX
139+ );
140+ }
141+ }
99142 }
100143 }
101144 }
102145
146+ /**
147+ * @param array{0: int, 1: string}|string $token
148+ */
149+ private function getTokenContent ($ token ): string
150+ {
151+ return is_array ($ token ) ? $ token [1 ] : $ token ;
152+ }
153+
103154}
0 commit comments