Skip to content

Commit

Permalink
Add ImplodeCallFixer
Browse files Browse the repository at this point in the history
  • Loading branch information
kubawerlos authored and keradus committed Aug 22, 2018
1 parent 791a415 commit f063eae
Show file tree
Hide file tree
Showing 18 changed files with 365 additions and 18 deletions.
7 changes: 7 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,13 @@ Choose from the list of available rules:

Convert ``heredoc`` to ``nowdoc`` where possible.

* **implode_call** [@Symfony:risky]

Function ``implode`` must be called with 2 arguments in the documented
order.

*Risky rule: risky when the function ``implode`` is overridden.*

* **include** [@Symfony]

Include/Require and file path should be divided with a single space.
Expand Down
2 changes: 1 addition & 1 deletion src/DocBlock/Annotation.php
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ public function remove()
*/
public function getContent()
{
return implode($this->lines);
return implode('', $this->lines);
}

public function supportTypes()
Expand Down
2 changes: 1 addition & 1 deletion src/DocBlock/DocBlock.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ public function getAnnotationsOfType($types)
*/
public function getContent()
{
return implode($this->lines);
return implode('', $this->lines);
}

private function findAnnotationLength($start)
Expand Down
150 changes: 150 additions & 0 deletions src/Fixer/FunctionNotation/ImplodeCallFixer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?php

/*
* This file is part of PHP CS Fixer.
*
* (c) Fabien Potencier <fabien@symfony.com>
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace PhpCsFixer\Fixer\FunctionNotation;

use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;

/**
* @author Kuba Werłos <werlos@gmail.com>
*/
final class ImplodeCallFixer extends AbstractFixer
{
/**
* {@inheritdoc}
*/
public function getDefinition()
{
return new FixerDefinition(
'Function `implode` must be called with 2 arguments in the documented order.',
[
new CodeSample("<?php\nimplode(\$pieces, '');\n"),
new CodeSample("<?php\nimplode(\$pieces);\n"),
],
null,
'Risky when the function `implode` is overridden.'
);
}

/**
* {@inheritdoc}
*/
public function isRisky()
{
return true;
}

/**
* {@inheritdoc}
*/
public function isCandidate(Tokens $tokens)
{
return $tokens->isTokenKindFound(T_STRING);
}

/**
* {@inheritdoc}
*/
public function getPriority()
{
// must be run after NoAliasFunctionsFixer
// must be run before MethodArgumentSpaceFixer
return -1;
}

/**
* {@inheritdoc}
*/
protected function applyFix(\SplFileInfo $file, Tokens $tokens)
{
$functionsAnalyzer = new FunctionsAnalyzer();

foreach ($tokens as $index => $token) {
if (!$tokens[$index]->equals([T_STRING, 'implode'], false)) {
continue;
}

if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
continue;
}

$argumentsIndices = $this->getArgumentIndices($tokens, $index);

if (1 === \count($argumentsIndices)) {
$firstArgumentIndex = \key($argumentsIndices);
$tokens->insertAt($firstArgumentIndex, [
new Token([T_CONSTANT_ENCAPSED_STRING, "''"]),
new Token(','),
new Token([T_WHITESPACE, ' ']),
]);

continue;
}

if (2 === \count($argumentsIndices)) {
list($firstArgumentIndex, $secondArgumentIndex) = \array_keys($argumentsIndices);

// If the first argument is string we have nothing to do
if ($tokens[$firstArgumentIndex]->isGivenKind(T_CONSTANT_ENCAPSED_STRING)) {
continue;
}
// If the second argument is not string we cannot make a swap
if (!$tokens[$secondArgumentIndex]->isGivenKind(T_CONSTANT_ENCAPSED_STRING)) {
continue;
}

// collect tokens from first argument
$firstArgumenteEndIndex = $argumentsIndices[\key($argumentsIndices)];
$newSecondArgumentTokens = [];
for ($i = \key($argumentsIndices); $i <= $firstArgumenteEndIndex; ++$i) {
$newSecondArgumentTokens[] = clone $tokens[$i];
$tokens->clearAt($i);
}

$tokens->insertAt($firstArgumentIndex, clone $tokens[$secondArgumentIndex]);

// insert above increased the second argument index
++$secondArgumentIndex;
$tokens->clearAt($secondArgumentIndex);
$tokens->insertAt($secondArgumentIndex, $newSecondArgumentTokens);
}
}
}

/**
* @param Tokens $tokens
* @param int $functionNameIndex
*
* @return array<int, int> In the format: startIndex => endIndex
*/
private function getArgumentIndices(Tokens $tokens, $functionNameIndex)
{
$argumentsAnalyzer = new ArgumentsAnalyzer();

$openParenthesis = $tokens->getNextTokenOfKind($functionNameIndex, ['(']);
$closeParenthesis = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesis);

$indices = [];

foreach ($argumentsAnalyzer->getArguments($tokens, $openParenthesis, $closeParenthesis) as $startIndexCandidate => $endIndex) {
$indices[$tokens->getNextMeaningfulToken($startIndexCandidate - 1)] = $tokens->getPrevMeaningfulToken($endIndex + 1);
}

return $indices;
}
}
9 changes: 9 additions & 0 deletions src/Fixer/FunctionNotation/MethodArgumentSpaceFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,15 @@ public function configure(array $configuration = null)
}
}

/**
* {@inheritdoc}
*/
public function getPriority()
{
// must be run after ImplodeCallFixer
return -2;
}

/**
* {@inheritdoc}
*/
Expand Down
6 changes: 3 additions & 3 deletions src/Fixer/PhpUnit/PhpUnitInternalClassFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ private function updateDocBlockIfNeeded(Tokens $tokens, $docBlockIndex)
}
$doc = $this->makeDocBlockMultiLineIfNeeded($doc, $tokens, $docBlockIndex);
$lines = $this->addInternalAnnotation($doc, $tokens, $docBlockIndex);
$lines = implode($lines);
$lines = implode('', $lines);

$tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $lines]);
}
Expand Down Expand Up @@ -227,7 +227,7 @@ private function makeDocBlockMultiLineIfNeeded(DocBlock $doc, Tokens $tokens, $d
if (1 === \count($lines) && empty($doc->getAnnotationsOfType('internal'))) {
$lines = $this->splitUpDocBlock($lines, $tokens, $docBlockIndex);

return new DocBlock(implode($lines));
return new DocBlock(implode('', $lines));
}

return $doc;
Expand Down Expand Up @@ -275,6 +275,6 @@ private function getSingleLineDocBlockEntry($line)
}
$line = \array_slice($line, $i);

return implode($line);
return implode('', $line);
}
}
2 changes: 1 addition & 1 deletion src/Fixer/PhpUnit/PhpUnitMethodCasingFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ private function updateDocBlock(Tokens $tokens, $docBlockIndex)
}

if ($docBlockNeesUpdate) {
$lines = implode($lines);
$lines = implode('', $lines);
$tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $lines]);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Fixer/PhpUnit/PhpUnitNoExpectationAnnotationFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ private function fixPhpUnitClass(Tokens $tokens, $startIndex, $endIndex)
$newMethodsCode = '<?php $this->'
.(isset($annotations['expectedExceptionMessageRegExp']) ? 'setExpectedExceptionRegExp' : 'setExpectedException')
.'('
.implode($paramList, ', ')
.implode(', ', $paramList)
.');';
$newMethods = Tokens::fromCode($newMethodsCode);
$newMethods[0] = new Token([
Expand Down
2 changes: 1 addition & 1 deletion src/Fixer/PhpUnit/PhpUnitOrderedCoversFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens)
);
}

$tokens[$index] = new Token([T_DOC_COMMENT, implode($lines)]);
$tokens[$index] = new Token([T_DOC_COMMENT, implode('', $lines)]);
}
}
}
14 changes: 7 additions & 7 deletions src/Fixer/PhpUnit/PhpUnitTestAnnotationFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ private function applyTestAnnotation(Tokens $tokens, $startIndex, $endIndex)

$lines = $this->addTestAnnotation($lines, $tokens, $docBlockIndex);

$lines = implode($lines);
$lines = implode('', $lines);
$tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $lines]);
}
}
Expand All @@ -167,7 +167,7 @@ private function applyTestPrefix(Tokens $tokens, $startIndex, $endIndex)

$lines = $this->updateDocBlock($tokens, $docBlockIndex);

$lines = implode($lines);
$lines = implode('', $lines);
$tokens[$docBlockIndex] = new Token([T_DOC_COMMENT, $lines]);

$functionNameIndex = $tokens->getNextMeaningfulToken($i);
Expand Down Expand Up @@ -451,7 +451,7 @@ private function getSingleLineDocBlockEntry($line)
}
$line = \array_slice($line, $i);

return implode($line);
return implode('', $line);
}

/**
Expand Down Expand Up @@ -480,14 +480,14 @@ private function removeTestPrefixFromDependsAnnotation(Line $line)
$line = \str_split($line->getContent());

$dependsIndex = $this->findWhereDependsFunctionNameStarts($line);
$dependsFunctionName = implode(\array_slice($line, $dependsIndex));
$dependsFunctionName = implode('', \array_slice($line, $dependsIndex));

if ($this->startsWith('test', $dependsFunctionName)) {
$dependsFunctionName = $this->removeTestPrefix($dependsFunctionName);
}
array_splice($line, $dependsIndex);

return new Line(implode($line).$dependsFunctionName);
return new Line(implode('', $line).$dependsFunctionName);
}

/**
Expand All @@ -499,15 +499,15 @@ private function addTestPrefixToDependsAnnotation(Line $line)
{
$line = \str_split($line->getContent());
$dependsIndex = $this->findWhereDependsFunctionNameStarts($line);
$dependsFunctionName = implode(\array_slice($line, $dependsIndex));
$dependsFunctionName = implode('', \array_slice($line, $dependsIndex));

if (!$this->startsWith('test', $dependsFunctionName)) {
$dependsFunctionName = $this->addTestPrefix($dependsFunctionName);
}

array_splice($line, $dependsIndex);

return new Line(implode($line).$dependsFunctionName);
return new Line(implode('', $line).$dependsFunctionName);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Fixer/Phpdoc/PhpdocAlignFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ private function fixDocBlock($content)
}
}

return implode($lines);
return implode('', $lines);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Fixer/Whitespace/NoExtraBlankLinesFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ private function removeMultipleBlankLines($index)
$count = \count($parts);

if ($count > $expected) {
$this->tokens[$index] = new Token([T_WHITESPACE, implode(\array_slice($parts, 0, $expected)).rtrim($parts[$count - 1], "\r\n")]);
$this->tokens[$index] = new Token([T_WHITESPACE, implode('', \array_slice($parts, 0, $expected)).rtrim($parts[$count - 1], "\r\n")]);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Fixer/Whitespace/NoTrailingWhitespaceFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens)
}
}

$content = implode($lines);
$content = implode('', $lines);
if ('' !== $content) {
$tokens[$index] = new Token([$token->getId(), $content]);
} else {
Expand Down
1 change: 1 addition & 0 deletions src/RuleSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ final class RuleSet implements RuleSetInterface
'fopen_flag_order' => true,
'fopen_flags' => true,
'function_to_constant' => true,
'implode_call' => true,
'is_null' => true,
'modernize_types_casting' => true,
'native_constant_invocation' => [
Expand Down
2 changes: 2 additions & 0 deletions tests/AutoReview/FixerFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,13 @@ public function provideFixersPriorityCases()
[$fixers['general_phpdoc_annotation_remove'], $fixers['phpdoc_trim']],
[$fixers['general_phpdoc_annotation_remove'], $fixers['no_empty_phpdoc']],
[$fixers['indentation_type'], $fixers['phpdoc_indent']],
[$fixers['implode_call'], $fixers['method_argument_space']],
[$fixers['is_null'], $fixers['yoda_style']],
[$fixers['list_syntax'], $fixers['binary_operator_spaces']],
[$fixers['list_syntax'], $fixers['ternary_operator_spaces']],
[$fixers['method_separation'], $fixers['braces']],
[$fixers['method_separation'], $fixers['indentation_type']],
[$fixers['no_alias_functions'], $fixers['implode_call']],
[$fixers['no_alias_functions'], $fixers['php_unit_dedicate_assert']],
[$fixers['no_blank_lines_after_phpdoc'], $fixers['single_blank_line_before_namespace']],
[$fixers['no_empty_comment'], $fixers['no_extra_blank_lines']],
Expand Down
Loading

0 comments on commit f063eae

Please sign in to comment.