Skip to content

Commit

Permalink
feature #4679 NativeConstantInvocationFixer - add "strict" flag (kuba…
Browse files Browse the repository at this point in the history
…werlos)

This PR was merged into the 2.17-dev branch.

Discussion
----------

NativeConstantInvocationFixer - add "strict" flag

@nicolas-grekas is it OK to add it to `@Symfony` rule set (I did it as it is very similar "strict" as for `NativeFunctionInvocationFixer`)?

Commits
-------

74aae91 NativeConstantInvocationFixer - add "strict" flag
  • Loading branch information
SpacePossum committed Jan 7, 2020
2 parents 6f73629 + 74aae91 commit e5a3dd6
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 27 deletions.
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;
'
);
}
}

0 comments on commit e5a3dd6

Please sign in to comment.