View source
<?php
namespace Drupal\Sniffs\Classes;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
class FullyQualifiedNamespaceSniff implements Sniff {
public function register() {
return [
T_NS_SEPARATOR,
];
}
public function process(File $phpcsFile, $stackPtr) {
$tokens = $phpcsFile
->getTokens();
if (substr($phpcsFile
->getFilename(), -8) === '.api.php') {
return $phpcsFile->numTokens + 1;
}
if ($tokens[$stackPtr - 1]['code'] !== T_STRING || $tokens[$stackPtr + 1]['code'] !== T_STRING) {
return;
}
$before = $phpcsFile
->findPrevious([
T_STRING,
T_NS_SEPARATOR,
T_WHITESPACE,
T_COMMA,
T_AS,
], $stackPtr, null, true);
if ($tokens[$before]['code'] === T_USE || $tokens[$before]['code'] === T_NAMESPACE) {
return $phpcsFile
->findNext([
T_STRING,
T_NS_SEPARATOR,
T_WHITESPACE,
T_COMMA,
T_AS,
], $stackPtr + 1, null, true);
}
else {
$before = $phpcsFile
->findPrevious([
T_STRING,
T_NS_SEPARATOR,
T_WHITESPACE,
], $stackPtr, null, true);
}
$after = $phpcsFile
->findNext([
T_STRING,
T_NS_SEPARATOR,
T_WHITESPACE,
], $stackPtr, null, true);
if ($tokens[$after]['code'] === T_OPEN_PARENTHESIS && $tokens[$before]['code'] !== T_NEW) {
return $after + 1;
}
$fullName = $phpcsFile
->getTokensAsString($before + 1, $after - 1 - $before);
$fullName = trim($fullName, "\\ \n");
$parts = explode('\\', $fullName);
$className = end($parts);
$conflict = false;
$alreadyUsed = false;
$aliasName = false;
$useStatement = $phpcsFile
->findNext(T_USE, 0);
while ($useStatement !== false && empty($tokens[$useStatement]['conditions']) === true) {
$endPtr = $phpcsFile
->findEndOfStatement($useStatement);
$useEnd = $phpcsFile
->findNext([
T_STRING,
T_NS_SEPARATOR,
T_WHITESPACE,
], $useStatement + 1, null, true) - 1;
$useFullName = trim($phpcsFile
->getTokensAsString($useStatement + 1, $useEnd - $useStatement));
$asPtr = $phpcsFile
->findNext(T_AS, $useEnd + 1, $endPtr);
if ($asPtr !== false) {
$aliasName = trim($phpcsFile
->getTokensAsString($asPtr + 1, $endPtr - 1 - $asPtr));
}
if (strcasecmp($useFullName, $fullName) === 0) {
$alreadyUsed = true;
break;
}
$parts = explode('\\', $useFullName);
$useClassName = end($parts);
if ($aliasName === $className || $useClassName === $className) {
$conflict = true;
break;
}
$aliasName = false;
if ($tokens[$endPtr]['code'] === T_COMMA) {
$useStatement = $endPtr;
continue;
}
$useStatement = $phpcsFile
->findNext(T_USE, $endPtr + 1);
}
if ($conflict === false) {
$classStatement = $phpcsFile
->findNext(T_CLASS, 0);
while ($classStatement !== false) {
$afterClassStatement = $phpcsFile
->findNext(T_WHITESPACE, $classStatement + 1, null, true);
if ($tokens[$afterClassStatement]['code'] === T_STRING) {
$declaredName = $tokens[$afterClassStatement]['content'];
if ($declaredName === $className) {
$conflict = true;
break;
}
}
$classStatement = $phpcsFile
->findNext(T_CLASS, $classStatement + 1);
}
}
$error = 'Namespaced classes/interfaces/traits should be referenced with use statements';
if ($conflict === true) {
$fix = false;
$phpcsFile
->addError($error, $stackPtr, 'UseStatementMissing');
}
else {
$fix = $phpcsFile
->addFixableError($error, $stackPtr, 'UseStatementMissing');
}
if ($fix === true) {
$phpcsFile->fixer
->beginChangeset();
for ($i = $before + 1; $i < $after; $i++) {
if ($tokens[$i]['code'] !== T_WHITESPACE) {
$phpcsFile->fixer
->replaceToken($i, '');
}
}
if ($aliasName !== false) {
$phpcsFile->fixer
->addContentBefore($after - 1, $aliasName);
}
else {
$phpcsFile->fixer
->addContentBefore($after - 1, $className);
}
if ($alreadyUsed === false || $phpcsFile->fixer
->getTokenContent($useStatement) !== $tokens[$useStatement]['content']) {
if ($aliasName !== false) {
$use = "use {$fullName} as {$aliasName};";
}
else {
$use = "use {$fullName};";
}
$useStatement = $phpcsFile
->findNext(T_USE, 0);
if ($useStatement !== false && empty($tokens[$useStatement]['conditions']) === true) {
$phpcsFile->fixer
->addContentBefore($useStatement, "{$use}\n");
}
else {
$beginning = 0;
$fileComment = $phpcsFile
->findNext(T_WHITESPACE, $beginning + 1, null, true);
if ($tokens[$fileComment]['code'] === T_DOC_COMMENT_OPEN_TAG) {
$beginning = $tokens[$fileComment]['comment_closer'];
$phpcsFile->fixer
->addContent($beginning, "\n\n{$use}\n");
}
else {
$phpcsFile->fixer
->addContent($beginning, "{$use}\n");
}
}
}
$phpcsFile->fixer
->endChangeset();
}
return $phpcsFile
->findNext([
T_STRING,
T_NS_SEPARATOR,
], $stackPtr + 1, null, true);
}
}