@@ -23,7 +23,7 @@ class StaticThisUsageSniff extends AbstractScopeSniff
2323 */
2424 public function __construct ()
2525 {
26- parent ::__construct ([T_CLASS , T_TRAIT , T_ENUM , T_ANON_CLASS ], [T_FUNCTION ] );
26+ parent ::__construct ([T_CLASS , T_TRAIT , T_ENUM , T_ANON_CLASS ], [T_FUNCTION , T_CLOSURE ], true );
2727 }
2828
2929
@@ -42,22 +42,28 @@ public function processTokenWithinScope(File $phpcsFile, int $stackPtr, int $cur
4242 $ tokens = $ phpcsFile ->getTokens ();
4343
4444 // Determine if this is a function which needs to be examined.
45- $ conditions = $ tokens [$ stackPtr ]['conditions ' ];
46- end ($ conditions );
47- $ deepestScope = key ($ conditions );
48- if ($ deepestScope !== $ currScope ) {
49- return ;
50- }
45+ if ($ tokens [$ stackPtr ]['code ' ] === T_FUNCTION ) {
46+ $ conditions = $ tokens [$ stackPtr ]['conditions ' ];
47+ end ($ conditions );
48+ $ deepestScope = key ($ conditions );
49+ if ($ deepestScope !== $ currScope ) {
50+ return ;
51+ }
5152
52- // Ignore abstract functions.
53- if (isset ($ tokens [$ stackPtr ]['scope_closer ' ]) === false ) {
54- return ;
55- }
53+ // Ignore abstract functions.
54+ if (isset ($ tokens [$ stackPtr ]['scope_closer ' ]) === false ) {
55+ return ;
56+ }
5657
57- $ next = $ phpcsFile ->findNext (Tokens::EMPTY_TOKENS , ($ stackPtr + 1 ), null , true );
58- if ($ next === false || $ tokens [$ next ]['code ' ] !== T_STRING ) {
59- // Not a function declaration, or incomplete.
60- return ;
58+ $ next = $ phpcsFile ->findNext (Tokens::EMPTY_TOKENS , ($ stackPtr + 1 ), null , true );
59+ if ($ next === false || $ tokens [$ next ]['code ' ] !== T_STRING ) {
60+ // Not a function declaration, or incomplete.
61+ return ;
62+ }
63+
64+ $ type = 'method ' ;
65+ } else {
66+ $ type = 'closure ' ;
6167 }
6268
6369 $ methodProps = $ phpcsFile ->getMethodProperties ($ stackPtr );
@@ -68,7 +74,7 @@ public function processTokenWithinScope(File $phpcsFile, int $stackPtr, int $cur
6874 $ next = $ stackPtr ;
6975 $ end = $ tokens [$ stackPtr ]['scope_closer ' ];
7076
71- $ this ->checkThisUsage ($ phpcsFile , $ next , $ end );
77+ $ this ->checkThisUsage ($ phpcsFile , $ next , $ end, $ type );
7278 }
7379
7480
@@ -78,21 +84,25 @@ public function processTokenWithinScope(File $phpcsFile, int $stackPtr, int $cur
7884 * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being scanned.
7985 * @param int $next The position of the next token to check.
8086 * @param int $end The position of the last token to check.
87+ * @param string $type Type of context being checked. Either 'method' or 'closure'.
8188 *
8289 * @return void
8390 */
84- private function checkThisUsage (File $ phpcsFile , int $ next , int $ end )
91+ private function checkThisUsage (File $ phpcsFile , int $ next , int $ end, string $ type )
8592 {
8693 $ tokens = $ phpcsFile ->getTokens ();
8794
8895 do {
89- $ next = $ phpcsFile ->findNext ([T_VARIABLE , T_ANON_CLASS ], ($ next + 1 ), $ end );
96+ $ next = $ phpcsFile ->findNext ([T_VARIABLE , T_CLOSURE , T_ANON_CLASS ], ($ next + 1 ), $ end );
9097 if ($ next === false ) {
9198 continue ;
9299 }
93100
94- if ($ tokens [$ next ]['code ' ] === T_ANON_CLASS ) {
95- $ this ->checkThisUsage ($ phpcsFile , $ next , $ tokens [$ next ]['scope_opener ' ]);
101+ if (($ tokens [$ next ]['code ' ] === T_ANON_CLASS
102+ || $ tokens [$ next ]['code ' ] === T_CLOSURE )
103+ && isset ($ tokens [$ next ]['scope_opener ' ]) === true
104+ ) {
105+ $ this ->checkThisUsage ($ phpcsFile , $ next , $ tokens [$ next ]['scope_opener ' ], $ type );
96106 $ next = $ tokens [$ next ]['scope_closer ' ];
97107 continue ;
98108 }
@@ -101,8 +111,10 @@ private function checkThisUsage(File $phpcsFile, int $next, int $end)
101111 continue ;
102112 }
103113
104- $ error = 'Usage of "$this" in static methods will cause runtime errors ' ;
105- $ phpcsFile ->addError ($ error , $ next , 'Found ' );
114+ $ error = 'Usage of "$this" in a static %s will cause runtime errors ' ;
115+ $ data = [$ type ];
116+
117+ $ phpcsFile ->addError ($ error , $ next , 'Found ' , $ data );
106118 } while ($ next !== false );
107119 }
108120
@@ -119,5 +131,18 @@ private function checkThisUsage(File $phpcsFile, int $next, int $end)
119131 */
120132 protected function processTokenOutsideScope (File $ phpcsFile , int $ stackPtr )
121133 {
134+ $ tokens = $ phpcsFile ->getTokens ();
135+
136+ if ($ tokens [$ stackPtr ]['code ' ] !== T_CLOSURE ) {
137+ // We're only interested in closures when looking outside of OO.
138+ return ;
139+ }
140+
141+ $ methodProps = $ phpcsFile ->getMethodProperties ($ stackPtr );
142+ if ($ methodProps ['is_static ' ] === false ) {
143+ return ;
144+ }
145+
146+ $ this ->checkThisUsage ($ phpcsFile , $ stackPtr , $ tokens [$ stackPtr ]['scope_closer ' ], 'closure ' );
122147 }
123148}
0 commit comments