Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 52 additions & 44 deletions src/Standards/Generic/Sniffs/Classes/DuplicateClassNameSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;

class DuplicateClassNameSniff implements Sniff
{
Expand Down Expand Up @@ -55,63 +56,70 @@ public function process(File $phpcsFile, $stackPtr)
T_TRAIT,
T_ENUM,
T_NAMESPACE,
T_CLOSE_TAG,
];

$stackPtr = $phpcsFile->findNext($findTokens, ($stackPtr + 1));
while ($stackPtr !== false) {
if ($tokens[$stackPtr]['code'] === T_CLOSE_TAG) {
// We can stop here. The sniff will continue from the next open
// tag when PHPCS reaches that token, if there is one.
return;
}

// Keep track of what namespace we are in.
if ($tokens[$stackPtr]['code'] === T_NAMESPACE) {
$nsEnd = $phpcsFile->findNext(
[
T_NS_SEPARATOR,
T_STRING,
T_WHITESPACE,
],
($stackPtr + 1),
null,
true
);

$namespace = trim($phpcsFile->getTokensAsString(($stackPtr + 1), ($nsEnd - $stackPtr - 1)));
$stackPtr = $nsEnd;
} else {
$nameToken = $phpcsFile->findNext(T_STRING, $stackPtr);
$name = $tokens[$nameToken]['content'];
if ($namespace !== '') {
$name = $namespace.'\\'.$name;
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($nextNonEmpty !== false
// Ignore namespace keyword used as operator.
&& $tokens[$nextNonEmpty]['code'] !== T_NS_SEPARATOR
) {
$namespace = '';
for ($i = $nextNonEmpty; $i < $phpcsFile->numTokens; $i++) {
if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
continue;
}

if ($tokens[$i]['code'] !== T_STRING && $tokens[$i]['code'] !== T_NS_SEPARATOR) {
break;
}

$namespace .= $tokens[$i]['content'];
}

$stackPtr = $i;
}

$compareName = strtolower($name);
if (isset($this->foundClasses[$compareName]) === true) {
$type = strtolower($tokens[$stackPtr]['content']);
$file = $this->foundClasses[$compareName]['file'];
$line = $this->foundClasses[$compareName]['line'];
$error = 'Duplicate %s name "%s" found; first defined in %s on line %s';
$data = [
$type,
$name,
$file,
$line,
];
$phpcsFile->addWarning($error, $stackPtr, 'Found', $data);
} else {
$this->foundClasses[$compareName] = [
'file' => $phpcsFile->getFilename(),
'line' => $tokens[$stackPtr]['line'],
];
} else {
$name = $phpcsFile->getDeclarationName($stackPtr);
if (empty($name) === false) {
if ($namespace !== '') {
$name = $namespace.'\\'.$name;
}

$compareName = strtolower($name);
if (isset($this->foundClasses[$compareName]) === true) {
$type = strtolower($tokens[$stackPtr]['content']);
$file = $this->foundClasses[$compareName]['file'];
$line = $this->foundClasses[$compareName]['line'];
$error = 'Duplicate %s name "%s" found; first defined in %s on line %s';
$data = [
$type,
$name,
$file,
$line,
];
$phpcsFile->addWarning($error, $stackPtr, 'Found', $data);
} else {
$this->foundClasses[$compareName] = [
'file' => $phpcsFile->getFilename(),
'line' => $tokens[$stackPtr]['line'],
];
}
}//end if

if (isset($tokens[$stackPtr]['scope_closer']) === true) {
$stackPtr = $tokens[$stackPtr]['scope_closer'];
}
}//end if

$stackPtr = $phpcsFile->findNext($findTokens, ($stackPtr + 1));
}//end while

return $phpcsFile->numTokens;

}//end process()


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php
namespace Code\IsNamespaced ?>

<?php
class MyClass {}
interface MyInterface {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
namespace Code\IsNamespaced;
class FooBar {}

namespace Code\AnotherNamespace;

namespace Code\IsNamespaced ?>

<?php
echo '123';
?>
<?php
class FooBar {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
namespace E;
namespace\functionCall();
class MyClass {}
interface YourInterface {}

namespace F;
namespace\functionCall();
class MyClass {}

namespace /*comment*/ G;
namespace\functionCall();
class MyClass {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php
namespace Foo\Bar;
class MyClass {}
interface YourInterface {}

namespace Foo /*comment*/ \ /*comment*/ Bar;
class MyClass {}
interface YourInterface {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php
namespace {
class MyClass {}
trait YourTrait {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

// Intentional parse error/live coding.
// This should be the only test in this file.
// This is a two-file test: test case file 97 and 98 belong together.
// Testing against false positives during live coding and invalid class names being stored in the cache.

class
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

// Intentional parse error/live coding.
// This should be the only test in this file.
// This is a two-file test: test case file 97 and 98 belong together.
// Testing against false positives during live coding and invalid class names being stored in the cache.

class
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

// Intentional parse error/live coding.
// This should be the only test in this file.
// Testing that the sniff is *not* triggered.

namespace
15 changes: 15 additions & 0 deletions src/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,21 @@ public function getWarningList($testFile='')
case 'DuplicateClassNameUnitTest.6.inc':
return [10 => 1];

case 'DuplicateClassNameUnitTest.8.inc':
return [
7 => 1,
8 => 1,
];

case 'DuplicateClassNameUnitTest.9.inc':
return [
3 => 1,
4 => 1,
];

case 'DuplicateClassNameUnitTest.11.inc':
return [13 => 1];

default:
return [];
}//end switch
Expand Down