Skip to content

Commit 2ccfae2

Browse files
committed
Squiz/SelfMemberReference: fix sniff to work with the PHP 8 identifier tokens
The existing unit tests already contain tests covering this change. The sniff has slightly different behaviour PHPCS 4.x vs the previous behaviour in PHPCS 3.x. Notable differences: * The tolerance for inline names interlaced with whitespace and comments (parse error in PHP 8) has been removed. Previously, the sniff would still fix such a name if there was a match. As of PHPCS 4.x it no longer will and won't throw an error for this anymore either. * The `getDeclarationNameWithNamespace()` method has been removed and the `getNamespaceOfScope()` method has been renamed to `getNamespaceName()`. Sniffs which extend this sniff and overload(ed) either method, will need to be aware of this. Includes: * Fixing an incorrect unit test.
1 parent 2017437 commit 2ccfae2

File tree

4 files changed

+42
-87
lines changed

4 files changed

+42
-87
lines changed

src/Standards/Squiz/Sniffs/Classes/SelfMemberReferenceSniff.php

Lines changed: 37 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -75,48 +75,40 @@ protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScop
7575

7676
return;
7777
}
78-
} else if ($tokens[$calledClassName]['code'] === T_STRING) {
79-
// If the class is called with a namespace prefix, build fully qualified
80-
// namespace calls for both current scope class and requested class.
81-
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($calledClassName - 1), null, true);
82-
if ($prevNonEmpty !== false && $tokens[$prevNonEmpty]['code'] === T_NS_SEPARATOR) {
83-
$declarationName = $this->getDeclarationNameWithNamespace($tokens, $calledClassName);
84-
$declarationName = ltrim($declarationName, '\\');
85-
$fullQualifiedClassName = $this->getNamespaceOfScope($phpcsFile, $currScope);
86-
if ($fullQualifiedClassName === '\\') {
87-
$fullQualifiedClassName = '';
88-
} else {
89-
$fullQualifiedClassName .= '\\';
90-
}
78+
} else if (isset(Tokens::$nameTokens[$tokens[$calledClassName]['code']]) === true) {
79+
// Work out the fully qualified name for both the class declaration
80+
// as well as the class usage to see if they match.
81+
$namespaceName = $this->getNamespaceName($phpcsFile, $currScope);
82+
if ($namespaceName === '\\') {
83+
$namespaceName = '';
84+
}
85+
86+
$declarationName = $namespaceName.'\\'.$phpcsFile->getDeclarationName($currScope);
87+
$inlineName = '';
88+
89+
switch ($tokens[$calledClassName]['code']) {
90+
case T_NAME_FULLY_QUALIFIED:
91+
$inlineName = $tokens[$calledClassName]['content'];
92+
break;
93+
94+
case T_NAME_QUALIFIED:
95+
case T_STRING:
96+
$inlineName = $namespaceName.'\\'.$tokens[$calledClassName]['content'];
97+
break;
9198

92-
$fullQualifiedClassName .= $phpcsFile->getDeclarationName($currScope);
93-
} else {
94-
$declarationName = $phpcsFile->getDeclarationName($currScope);
95-
$fullQualifiedClassName = $tokens[$calledClassName]['content'];
99+
case T_NAME_RELATIVE:
100+
$inlineName = $namespaceName.substr($tokens[$calledClassName]['content'], 9);
101+
break;
96102
}
97103

98-
if ($declarationName === $fullQualifiedClassName) {
104+
if ($declarationName === $inlineName) {
99105
// Class name is the same as the current class, which is not allowed.
100106
$error = 'Must use "self::" for local static member reference';
101107
$fix = $phpcsFile->addFixableError($error, $calledClassName, 'NotUsed');
102108

103109
if ($fix === true) {
104110
$phpcsFile->fixer->beginChangeset();
105-
106-
$currentPointer = ($stackPtr - 1);
107-
while ($tokens[$currentPointer]['code'] === T_NS_SEPARATOR
108-
|| $tokens[$currentPointer]['code'] === T_STRING
109-
|| isset(Tokens::$emptyTokens[$tokens[$currentPointer]['code']]) === true
110-
) {
111-
if (isset(Tokens::$emptyTokens[$tokens[$currentPointer]['code']]) === true) {
112-
--$currentPointer;
113-
continue;
114-
}
115-
116-
$phpcsFile->fixer->replaceToken($currentPointer, '');
117-
--$currentPointer;
118-
}
119-
111+
$phpcsFile->fixer->replaceToken($calledClassName, '');
120112
$phpcsFile->fixer->replaceToken($stackPtr, 'self::');
121113
$phpcsFile->fixer->endChangeset();
122114

@@ -180,69 +172,33 @@ protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
180172

181173

182174
/**
183-
* Returns the declaration names for classes/interfaces/functions with a namespace.
184-
*
185-
* @param array $tokens Token stack for this file.
186-
* @param int $stackPtr The position where the namespace building will start.
187-
*
188-
* @return string
189-
*/
190-
protected function getDeclarationNameWithNamespace(array $tokens, $stackPtr)
191-
{
192-
$nameParts = [];
193-
$currentPointer = $stackPtr;
194-
while ($tokens[$currentPointer]['code'] === T_NS_SEPARATOR
195-
|| $tokens[$currentPointer]['code'] === T_STRING
196-
|| isset(Tokens::$emptyTokens[$tokens[$currentPointer]['code']]) === true
197-
) {
198-
if (isset(Tokens::$emptyTokens[$tokens[$currentPointer]['code']]) === true) {
199-
--$currentPointer;
200-
continue;
201-
}
202-
203-
$nameParts[] = $tokens[$currentPointer]['content'];
204-
--$currentPointer;
205-
}
206-
207-
$nameParts = array_reverse($nameParts);
208-
return implode('', $nameParts);
209-
210-
}//end getDeclarationNameWithNamespace()
211-
212-
213-
/**
214-
* Returns the namespace declaration of a file.
175+
* Returns the namespace name of the current scope.
215176
*
216177
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
217178
* @param int $stackPtr The position where the search for the
218179
* namespace declaration will start.
219180
*
220181
* @return string
221182
*/
222-
protected function getNamespaceOfScope(File $phpcsFile, $stackPtr)
183+
protected function getNamespaceName(File $phpcsFile, $stackPtr)
223184
{
224-
$namespace = '\\';
225-
$tokens = $phpcsFile->getTokens();
185+
$namespace = '\\';
186+
$namespaceDeclaration = $phpcsFile->findPrevious(T_NAMESPACE, $stackPtr);
226187

227-
while (($namespaceDeclaration = $phpcsFile->findPrevious(T_NAMESPACE, $stackPtr)) !== false) {
188+
if ($namespaceDeclaration !== false) {
189+
$tokens = $phpcsFile->getTokens();
228190
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($namespaceDeclaration + 1), null, true);
229-
if ($tokens[$nextNonEmpty]['code'] === T_NS_SEPARATOR) {
230-
// Namespace operator. Ignore.
231-
$stackPtr = ($namespaceDeclaration - 1);
232-
continue;
191+
if ($nextNonEmpty !== false
192+
&& ($tokens[$nextNonEmpty]['code'] === T_NAME_QUALIFIED
193+
|| $tokens[$nextNonEmpty]['code'] === T_STRING)
194+
) {
195+
$namespace .= $tokens[$nextNonEmpty]['content'];
233196
}
234-
235-
$endOfNamespaceDeclaration = $phpcsFile->findNext([T_SEMICOLON, T_OPEN_CURLY_BRACKET, T_CLOSE_TAG], $namespaceDeclaration);
236-
$namespace = $this->getDeclarationNameWithNamespace(
237-
$phpcsFile->getTokens(),
238-
($endOfNamespaceDeclaration - 1)
239-
);
240-
break;
241197
}
242198

243199
return $namespace;
244200

245-
}//end getNamespaceOfScope()
201+
}//end getNamespaceName()
246202

247203

248204
}//end class

src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.inc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,16 +159,16 @@ class Nested_Anon_Class {
159159
namespace Foo\Baz {
160160
class BarFoo {
161161
public function foo() {
162-
echo Foo\Baz\BarFoo::$prop;
162+
echo \Foo\Baz\BarFoo::$prop;
163163
}
164164
}
165165
}
166166

167-
// Prevent false negative when namespace has whitespace/comments.
167+
// Prevent false negative when namespace has whitespace/comments - no longer supported as of PHPCS 4.x.
168168
namespace Foo /*comment*/ \ Bah {
169169
class BarFoo {
170170
public function foo() {
171-
echo Foo \ /*comment*/ Bah\BarFoo::$prop;
171+
echo \Foo \ /*comment*/ Bah\BarFoo::$prop;
172172
}
173173
}
174174
}

src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.inc.fixed

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,11 @@ namespace Foo\Baz {
152152
}
153153
}
154154

155-
// Prevent false negative when namespace has whitespace/comments.
155+
// Prevent false negative when namespace has whitespace/comments - no longer supported as of PHPCS 4.x.
156156
namespace Foo /*comment*/ \ Bah {
157157
class BarFoo {
158158
public function foo() {
159-
echo /*comment*/ self::$prop;
159+
echo \Foo \ /*comment*/ Bah\BarFoo::$prop;
160160
}
161161
}
162162
}

src/Standards/Squiz/Tests/Classes/SelfMemberReferenceUnitTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ public function getErrorList()
4545
140 => 1,
4646
143 => 2,
4747
162 => 1,
48-
171 => 1,
4948
183 => 1,
5049
197 => 1,
5150
];

0 commit comments

Comments
 (0)