Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NativeConstantInvocationFixer - add "strict" flag #4679

Merged
merged 1 commit into from Jan 7, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.rst
Expand Up @@ -970,6 +970,8 @@ Choose from the list of available rules:
- ``include`` (``array``): list of additional constants to fix; defaults to ``[]``
- ``scope`` (``'all'``, ``'namespaced'``): only fix constant invocations that are made
within a namespace or fix all; defaults to ``'all'``
- ``strict`` (``bool``): whether leading ``\`` of constant invocation not meant to
have it should be removed; defaults to ``false``

* **native_function_casing** [@Symfony, @PhpCsFixer]

Expand Down
41 changes: 26 additions & 15 deletions src/Fixer/ConstantNotation/NativeConstantInvocationFixer.php
Expand Up @@ -225,14 +225,18 @@ protected function createConfigurationDefinition()
->setAllowedValues(['all', 'namespaced'])
->setDefault('all')
->getOption(),
(new FixerOptionBuilder('strict', 'Whether leading `\` of constant invocation not meant to have it should be removed.'))
->setAllowedTypes(['bool'])
->setDefault(false) // @TODO: 3.0 change to true as default
->getOption(),
]);
}

/**
* @param int $start
* @param int $end
* @param int $startIndex
* @param int $endIndex
*/
private function fixConstantInvocations(Tokens $tokens, $start, $end)
private function fixConstantInvocations(Tokens $tokens, $startIndex, $endIndex)
{
$useDeclarations = (new NamespaceUsesAnalyzer())->getDeclarationsFromTokens($tokens);
$useConstantDeclarations = [];
Expand All @@ -244,39 +248,46 @@ private function fixConstantInvocations(Tokens $tokens, $start, $end)

$tokenAnalyzer = new TokensAnalyzer($tokens);

$indexes = [];
for ($index = $start; $index < $end; ++$index) {
for ($index = $endIndex; $index > $startIndex; --$index) {
$token = $tokens[$index];

// test if we are at a constant call
if (!$token->isGivenKind(T_STRING)) {
continue;
}

if (!$tokenAnalyzer->isConstantInvocation($index)) {
continue;
}

$tokenContent = $token->getContent();

$prevIndex = $tokens->getPrevMeaningfulToken($index);

if (!isset($this->constantsToEscape[$tokenContent]) && !isset($this->caseInsensitiveConstantsToEscape[strtolower($tokenContent)])) {
if (!$this->configuration['strict']) {
continue;
}
if (!$tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
continue;
}
$prevPrevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
if ($tokens[$prevPrevIndex]->isGivenKind(T_STRING)) {
continue;
}
$tokens->clearTokenAndMergeSurroundingWhitespace($prevIndex);

continue;
}

if (isset($useConstantDeclarations[$tokenContent])) {
continue;
}

$prevIndex = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
continue;
}

if (!$tokenAnalyzer->isConstantInvocation($index)) {
continue;
}

$indexes[] = $index;
}

$indexes = array_reverse($indexes);
foreach ($indexes as $index) {
$tokens->insertAt($index, new Token([T_NS_SEPARATOR, '\\']));
}
}
Expand Down
22 changes: 11 additions & 11 deletions src/Fixer/PhpTag/EchoTagSyntaxFixer.php
Expand Up @@ -93,10 +93,10 @@ public function getDefinition()
public function isCandidate(Tokens $tokens)
{
if (self::FORMAT_SHORT === $this->configuration[self::OPTION_FORMAT]) {
return $tokens->isAnyTokenKindsFound([\T_ECHO, \T_PRINT]);
return $tokens->isAnyTokenKindsFound([T_ECHO, T_PRINT]);
}

return $tokens->isTokenKindFound(\T_OPEN_TAG_WITH_ECHO);
return $tokens->isTokenKindFound(T_OPEN_TAG_WITH_ECHO);
}

/**
Expand Down Expand Up @@ -137,14 +137,14 @@ private function longToShort(Tokens $tokens)
$skipWhenComplexCode = $this->configuration[self::OPTION_SHORTEN_SIMPLE_STATEMENTS_ONLY];
$count = $tokens->count();
for ($index = 0; $index < $count; ++$index) {
if (!$tokens[$index]->isGivenKind(\T_OPEN_TAG)) {
if (!$tokens[$index]->isGivenKind(T_OPEN_TAG)) {
continue;
}
$nextMeaningful = $tokens->getNextMeaningfulToken($index);
if (null === $nextMeaningful) {
return;
}
if (!$tokens[$nextMeaningful]->isGivenKind([\T_ECHO, \T_PRINT])) {
if (!$tokens[$nextMeaningful]->isGivenKind([T_ECHO, T_PRINT])) {
$index = $nextMeaningful;

continue;
Expand All @@ -163,19 +163,19 @@ private function longToShort(Tokens $tokens)
private function shortToLong(Tokens $tokens)
{
if (self::LONG_FUNCTION_PRINT === $this->configuration[self::OPTION_LONG_FUNCTION]) {
$echoToken = [\T_PRINT, 'print'];
$echoToken = [T_PRINT, 'print'];
} else {
$echoToken = [\T_ECHO, 'echo'];
$echoToken = [T_ECHO, 'echo'];
}
$index = -1;
for (;;) {
$index = $tokens->getNextTokenOfKind($index, [[\T_OPEN_TAG_WITH_ECHO]]);
$index = $tokens->getNextTokenOfKind($index, [[T_OPEN_TAG_WITH_ECHO]]);
if (null === $index) {
return;
}
$replace = [new Token([\T_OPEN_TAG, '<?php ']), new Token($echoToken)];
$replace = [new Token([T_OPEN_TAG, '<?php ']), new Token($echoToken)];
if (!$tokens[$index + 1]->isWhitespace()) {
$replace[] = new Token([\T_WHITESPACE, ' ']);
$replace[] = new Token([T_WHITESPACE, ' ']);
}
$tokens->overrideRange($index, $index, $replace);
++$index;
Expand All @@ -202,7 +202,7 @@ private function isComplexCode(Tokens $tokens, $index)
$semicolonFound = false;
for ($count = $tokens->count(); $index < $count; ++$index) {
$token = $tokens[$index];
if ($token->isGivenKind(\T_CLOSE_TAG)) {
if ($token->isGivenKind(T_CLOSE_TAG)) {
return false;
}
if (';' === $token->getContent()) {
Expand All @@ -225,7 +225,7 @@ private function isComplexCode(Tokens $tokens, $index)
*/
private function buildLongToShortTokens(Tokens $tokens, $openTagIndex, $echoTagIndex)
{
$result = [new Token([\T_OPEN_TAG_WITH_ECHO, '<?='])];
$result = [new Token([T_OPEN_TAG_WITH_ECHO, '<?='])];

$start = $tokens->getNextNonWhitespace($openTagIndex);

Expand Down
2 changes: 1 addition & 1 deletion src/Fixer/StringNotation/ExplicitStringVariableFixer.php
Expand Up @@ -112,7 +112,7 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens)

++$nextIndex;
}
krsort($variableTokens, \SORT_NUMERIC);
krsort($variableTokens, SORT_NUMERIC);

foreach ($variableTokens as $distinctVariableSet) {
if (1 === \count($distinctVariableSet['tokens'])) {
Expand Down
1 change: 1 addition & 0 deletions src/RuleSet.php
Expand Up @@ -190,6 +190,7 @@ final class RuleSet implements RuleSetInterface
'PHP_VERSION_ID',
],
'scope' => 'namespaced',
'strict' => true,
],
'native_function_invocation' => [
'include' => [NativeFunctionInvocationFixer::SET_COMPILER_OPTIMIZED],
Expand Down
18 changes: 18 additions & 0 deletions tests/Fixer/ConstantNotation/NativeConstantInvocationFixerTest.php
Expand Up @@ -535,4 +535,22 @@ public function testFixScopedOnlyNoNamespace()

$this->doTest($expected);
}

public function testFixStrictOption()
{
$this->fixer->configure(['strict' => true]);

$this->doTest(
'<?php
echo \PHP_VERSION . \PHP_EOL; // built-in constants to have backslash
echo MY_FRAMEWORK_MAJOR_VERSION . MY_FRAMEWORK_MINOR_VERSION; // non-built-in constants not to have backslash
echo \Dont\Touch\Namespaced\CONSTANT;
',
'<?php
echo \PHP_VERSION . PHP_EOL; // built-in constants to have backslash
echo \MY_FRAMEWORK_MAJOR_VERSION . MY_FRAMEWORK_MINOR_VERSION; // non-built-in constants not to have backslash
echo \Dont\Touch\Namespaced\CONSTANT;
'
);
}
}