Skip to content

Commit

Permalink
Support for use const and use function (fixes #43 and #47)
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Aug 8, 2016
1 parent d2989ad commit ef4c333
Show file tree
Hide file tree
Showing 16 changed files with 289 additions and 63 deletions.
38 changes: 37 additions & 1 deletion SlevomatCodingStandard/Helpers/ReferencedName.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,31 @@
class ReferencedName
{

const TYPE_DEFAULT = 'default';
const TYPE_FUNCTION = 'function';
const TYPE_CONSTANT = 'constant';

/** @var string */
private $nameAsReferencedInFile;

/** @var integer */
private $pointer;

/** @var string */
private $type;

/**
* @param string $nameAsReferencedInFile
* @param integer $pointer
* @param string $type
*/
public function __construct($nameAsReferencedInFile, $pointer)
public function __construct($nameAsReferencedInFile, $pointer, $type)
{
$this->nameAsReferencedInFile = $nameAsReferencedInFile;
$this->pointer = $pointer;
$this->type = $type;
}

/**
* @return string
*/
Expand All @@ -36,4 +46,30 @@ public function getPointer()
return $this->pointer;
}

/**
* @return boolean
*/
public function isConstant()
{
return $this->type === self::TYPE_CONSTANT;
}

/**
* @return boolean
*/
public function isFunction()
{
return $this->type === self::TYPE_FUNCTION;
}

/**
* @param \SlevomatCodingStandard\Helpers\UseStatement $useStatement
* @return boolean
*/
public function hasSameUseStatementType(UseStatement $useStatement)
{
return $this->isConstant() === $useStatement->isConstant()
&& $this->isFunction() === $useStatement->isFunction();
}

}
31 changes: 29 additions & 2 deletions SlevomatCodingStandard/Helpers/ReferencedNameHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ private static function createAllReferencedNames(PHP_CodeSniffer_File $phpcsFile
], true);
});
foreach ($referencedNames as $name) {
$types[] = new ReferencedName($name, $nameStartPointer);
$types[] = new ReferencedName($name, $nameStartPointer, ReferencedName::TYPE_DEFAULT);
}
}
};
Expand Down Expand Up @@ -124,7 +124,34 @@ private static function createAllReferencedNames(PHP_CodeSniffer_File $phpcsFile
);
continue;
}
$types[] = new ReferencedName(TokenHelper::getContent($phpcsFile, $nameStartPointer, $nameEndPointer), $nameStartPointer);

$nextTokenAfterEndPointer = TokenHelper::findNextNonWhitespace($phpcsFile, $nameEndPointer);
$previousTokenBeforeStartPointer = TokenHelper::findPreviousNonWhitespace($phpcsFile, $nameStartPointer - 1);
$type = ReferencedName::TYPE_DEFAULT;
if ($nextTokenAfterEndPointer !== null && $previousTokenBeforeStartPointer !== null) {
if ($tokens[$nextTokenAfterEndPointer]['code'] === T_OPEN_PARENTHESIS) {
if ($tokens[$previousTokenBeforeStartPointer]['code'] !== T_NEW) {
$type = ReferencedName::TYPE_FUNCTION;
}
} elseif ($tokens[$nextTokenAfterEndPointer]['code'] !== T_VARIABLE) {
if (
!in_array($tokens[$previousTokenBeforeStartPointer]['code'], [
T_EXTENDS,
T_IMPLEMENTS,
T_INSTANCEOF,
T_USE, // trait
T_NEW,
T_COLON, // return typehint
], true)
&& !in_array($tokens[$nextTokenAfterEndPointer]['code'], [
T_DOUBLE_COLON,
], true)
) {
$type = ReferencedName::TYPE_CONSTANT;
}
}
}
$types[] = new ReferencedName(TokenHelper::getContent($phpcsFile, $nameStartPointer, $nameEndPointer), $nameStartPointer, $type);
$beginSearchAtPointer = $nameEndPointer + 1;
}
return $types;
Expand Down
52 changes: 51 additions & 1 deletion SlevomatCodingStandard/Helpers/UseStatement.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
class UseStatement
{

const TYPE_DEFAULT = ReferencedName::TYPE_DEFAULT;
const TYPE_FUNCTION = ReferencedName::TYPE_FUNCTION;
const TYPE_CONSTANT = ReferencedName::TYPE_CONSTANT;

/** @var string */
private $nameAsReferencedInFile;

Expand All @@ -17,21 +21,27 @@ class UseStatement
/** @var integer */
private $usePointer;

/** @var string */
private $type;

/**
* @param string $nameAsReferencedInFile
* @param string $fullyQualifiedClassName
* @param integer $usePointer T_USE pointer
* @param string $type
*/
public function __construct(
$nameAsReferencedInFile,
$fullyQualifiedClassName,
$usePointer
$usePointer,
$type
)
{
$this->nameAsReferencedInFile = $nameAsReferencedInFile;
$this->normalizedNameAsReferencedInFile = self::normalizedNameAsReferencedInFile($nameAsReferencedInFile);
$this->fullyQualifiedTypeName = $fullyQualifiedClassName;
$this->usePointer = $usePointer;
$this->type = $type;
}

/**
Expand Down Expand Up @@ -75,4 +85,44 @@ public function getPointer()
return $this->usePointer;
}

/**
* @return boolean
*/
public function isConstant()
{
return $this->type === self::TYPE_CONSTANT;
}

/**
* @return boolean
*/
public function isFunction()
{
return $this->type === self::TYPE_FUNCTION;
}

/**
* @param self $that
* @return boolean
*/
public function hasSameType(self $that)
{
return $this->type === $that->type;
}

/**
* @param self $that
* @return integer
*/
public function compareByType(self $that)
{
$order = [
self::TYPE_DEFAULT => 1,
self::TYPE_CONSTANT => 2,
self::TYPE_FUNCTION => 3,
];

return $order[$this->type] - $order[$that->type];
}

}
16 changes: 15 additions & 1 deletion SlevomatCodingStandard/Helpers/UseStatementHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ public static function getFullyQualifiedTypeNameFromUse(PHP_CodeSniffer_File $ph
$nameEndPointer = TokenHelper::findPreviousExcluding($phpcsFile, [T_WHITESPACE], $nameEndPointer - 1) + 1;
}
$nameStartPointer = $phpcsFile->findNext(TokenHelper::$nameTokenCodes, $usePointer + 1, $nameEndPointer);
if (in_array($tokens[$nameStartPointer]['content'], ['const', 'function'], true)) {
$nameStartPointer = $phpcsFile->findNext(TokenHelper::$nameTokenCodes, $nameStartPointer + 1, $nameEndPointer);
}
$name = TokenHelper::getContent($phpcsFile, $nameStartPointer, $nameEndPointer);

return NamespaceHelper::normalizeToCanonicalName($name);
Expand All @@ -85,12 +88,23 @@ public static function getFullyQualifiedTypeNameFromUse(PHP_CodeSniffer_File $ph
public static function getUseStatements(PHP_CodeSniffer_File $phpcsFile, $openTagPointer)
{
$names = [];
$tokens = $phpcsFile->getTokens();
foreach (self::getUseStatementPointers($phpcsFile, $openTagPointer) as $usePointer) {
$nextTokenFromUsePointer = TokenHelper::findNextNonWhitespace($phpcsFile, $usePointer + 1);
$type = UseStatement::TYPE_DEFAULT;
if ($tokens[$nextTokenFromUsePointer]['code'] === T_STRING) {
if ($tokens[$nextTokenFromUsePointer]['content'] === 'const') {
$type = UseStatement::TYPE_CONSTANT;
} elseif ($tokens[$nextTokenFromUsePointer]['content'] === 'function') {
$type = UseStatement::TYPE_FUNCTION;
}
}
$name = self::getNameAsReferencedInClassFromUse($phpcsFile, $usePointer);
$useStatement = new UseStatement(
$name,
self::getFullyQualifiedTypeNameFromUse($phpcsFile, $usePointer),
$usePointer
$usePointer,
$type
);
$names[$useStatement->getCanonicalNameAsReferencedInFile()] = $useStatement;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ class AlphabeticallySortedUsesSniff implements \PHP_CodeSniffer_Sniff

const CODE_INCORRECT_ORDER = 'IncorrectlyOrderedUses';

/** @var string */
private $lastUseTypeName;
/** @var \SlevomatCodingStandard\Helpers\UseStatement */
private $lastUse;

/**
* @return integer[]
Expand All @@ -31,20 +31,19 @@ public function register()
*/
public function process(PHP_CodeSniffer_File $phpcsFile, $openTagPointer)
{
$this->lastUseTypeName = null;
$this->lastUse = null;
$useStatements = UseStatementHelper::getUseStatements(
$phpcsFile,
$openTagPointer
);
foreach ($useStatements as $useStatement) {
$typeName = $useStatement->getFullyQualifiedTypeName();
if ($this->lastUseTypeName === null) {
$this->lastUseTypeName = $typeName;
if ($this->lastUse === null) {
$this->lastUse = $useStatement;
} else {
$order = $this->compareStrings($typeName, $this->lastUseTypeName);
$order = $this->compareUseStatements($useStatement, $this->lastUse);
if ($order < 0) {
$fix = $phpcsFile->addFixableError(
sprintf('Use statements are incorrectly ordered. The first wrong one is %s', $typeName),
sprintf('Use statements are incorrectly ordered. The first wrong one is %s', $useStatement->getFullyQualifiedTypeName()),
$useStatement->getPointer(),
self::CODE_INCORRECT_ORDER
);
Expand All @@ -54,7 +53,7 @@ public function process(PHP_CodeSniffer_File $phpcsFile, $openTagPointer)

return;
} else {
$this->lastUseTypeName = $typeName;
$this->lastUse = $useStatement;
}
}
}
Expand All @@ -78,7 +77,7 @@ private function fixAlphabeticalOrder(
}

uasort($useStatements, function (UseStatement $a, UseStatement $b) {
return $this->compareStrings($a->getFullyQualifiedTypeName(), $b->getFullyQualifiedTypeName());
return $this->compareUseStatements($a, $b);
});

$phpcsFile->fixer->addContent($firstUseStatement->getPointer(), implode(PHP_EOL, array_map(function (UseStatement $useStatement) {
Expand All @@ -93,34 +92,40 @@ private function fixAlphabeticalOrder(
}

/**
* @param string $a
* @param string $b
* @param \SlevomatCodingStandard\Helpers\UseStatement $a
* @param \SlevomatCodingStandard\Helpers\UseStatement $b
* @return integer
*/
private function compareStrings($a, $b)
private function compareUseStatements(UseStatement $a, UseStatement $b)
{
if (!$a->hasSameType($b)) {
return $a->compareByType($b);
}
$aName = $a->getFullyQualifiedTypeName();
$bName = $b->getFullyQualifiedTypeName();

$i = 0;
for (; $i < min(strlen($a), strlen($b)); $i++) {
if ($this->isSpecialCharacter($a[$i]) && !$this->isSpecialCharacter($b[$i])) {
for (; $i < min(strlen($aName), strlen($bName)); $i++) {
if ($this->isSpecialCharacter($aName[$i]) && !$this->isSpecialCharacter($bName[$i])) {
return -1;
} elseif (!$this->isSpecialCharacter($a[$i]) && $this->isSpecialCharacter($b[$i])) {
} elseif (!$this->isSpecialCharacter($aName[$i]) && $this->isSpecialCharacter($bName[$i])) {
return 1;
}

if (is_numeric($a[$i]) && is_numeric($b[$i])) {
if (is_numeric($aName[$i]) && is_numeric($bName[$i])) {
break;
}

$cmp = strcasecmp($a[$i], $b[$i]);
$cmp = strcasecmp($aName[$i], $bName[$i]);
if (
$cmp !== 0
|| ($a[$i] !== $b[$i] && strtolower($a[$i]) === strtolower($b[$i]))
|| ($aName[$i] !== $bName[$i] && strtolower($aName[$i]) === strtolower($bName[$i]))
) {
return $cmp;
}
}

return strnatcasecmp(substr($a, $i), substr($b, $i));
return strnatcasecmp(substr($aName, $i), substr($bName, $i));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public function process(PHP_CodeSniffer_File $phpcsFile, $openTagPointer)
$pointer = $referencedName->getPointer();
$name = $referencedName->getNameAsReferencedInFile();
$normalizedName = UseStatement::normalizedNameAsReferencedInFile($name);
if (isset($useStatements[$normalizedName])) {
if (isset($useStatements[$normalizedName]) && $referencedName->hasSameUseStatementType($useStatements[$normalizedName])) {
$useStatement = $useStatements[$normalizedName];
if (
!StringHelper::endsWith($useStatement->getFullyQualifiedTypeName(), 'Exception')
Expand Down
3 changes: 3 additions & 0 deletions SlevomatCodingStandard/Sniffs/Namespaces/UnusedUsesSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ public function process(PHP_CodeSniffer_File $phpcsFile, $openTagPointer)
!NamespaceHelper::isFullyQualifiedName($name)
&& isset($unusedNames[$normalizedNameAsReferencedInFile])
) {
if (!$referencedName->hasSameUseStatementType($unusedNames[$normalizedNameAsReferencedInFile])) {
continue;
}
if ($unusedNames[$normalizedNameAsReferencedInFile]->getNameAsReferencedInFile() !== $nameAsReferencedInFile) {
$phpcsFile->addError(sprintf(
'Case of reference name %s and use statement %s do not match',
Expand Down
2 changes: 2 additions & 0 deletions build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
>
<arg value="--exclude"/>
<arg path="tests/Helpers/data/php7"/>
<arg value="--exclude"/>
<arg path="tests/Sniffs/Namespaces/data/php7"/>
<arg path="SlevomatCodingStandard" />
<arg path="tests" />
</exec>
Expand Down
Loading

0 comments on commit ef4c333

Please sign in to comment.