diff --git a/composer.json b/composer.json index 781e376722d..34a604eb0eb 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,7 @@ }, "suggest": { "ext-dom": "For handling output formats in XML", - "ext-mbstring": "For handling non-UTF8 characters in cache signature.", + "ext-mbstring": "For handling non-UTF8 characters.", "php-cs-fixer/phpunit-constraint-isidenticalstring": "For IsIdenticalString constraint.", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "For XmlMatchesXsd constraint.", "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." diff --git a/src/Fixer/ClassNotation/FinalInternalClassFixer.php b/src/Fixer/ClassNotation/FinalInternalClassFixer.php index b8503d43ac3..39d66aa8ddb 100644 --- a/src/Fixer/ClassNotation/FinalInternalClassFixer.php +++ b/src/Fixer/ClassNotation/FinalInternalClassFixer.php @@ -72,12 +72,12 @@ public function getDefinition() /** * {@inheritdoc} * - * Must run before FinalStaticAccessFixer, SelfStaticAccessorFixer. + * Must run before FinalStaticAccessFixer, ProtectedToPrivateFixer, SelfStaticAccessorFixer. * Must run after PhpUnitInternalClassFixer. */ public function getPriority() { - return 0; + return 67; } /** diff --git a/src/Fixer/ClassNotation/ProtectedToPrivateFixer.php b/src/Fixer/ClassNotation/ProtectedToPrivateFixer.php index 2b6760dd131..6c6c502b7ff 100644 --- a/src/Fixer/ClassNotation/ProtectedToPrivateFixer.php +++ b/src/Fixer/ClassNotation/ProtectedToPrivateFixer.php @@ -53,6 +53,7 @@ protected function test() * {@inheritdoc} * * Must run before OrderedClassElementsFixer. + * Must run after FinalInternalClassFixer. */ public function getPriority() { diff --git a/src/Fixer/Comment/NoEmptyCommentFixer.php b/src/Fixer/Comment/NoEmptyCommentFixer.php index 68c2950e731..652daab5be6 100644 --- a/src/Fixer/Comment/NoEmptyCommentFixer.php +++ b/src/Fixer/Comment/NoEmptyCommentFixer.php @@ -157,7 +157,7 @@ private function isEmptyComment($content) { static $mapper = [ self::TYPE_HASH => '|^#\s*$|', // single line comment starting with '#' - self::TYPE_SLASH_ASTERISK => '|^/\*\s*\*/$|', // comment starting with '/*' and ending with '*/' (but not a PHPDoc) + self::TYPE_SLASH_ASTERISK => '|^/\*[\s\*]*\*+/$|', // comment starting with '/*' and ending with '*/' (but not a PHPDoc) self::TYPE_DOUBLE_SLASH => '|^//\s*$|', // single line comment starting with '//' ]; diff --git a/src/Fixer/FunctionNotation/PhpdocToReturnTypeFixer.php b/src/Fixer/FunctionNotation/PhpdocToReturnTypeFixer.php index d49e290d63c..dda3fa27d69 100644 --- a/src/Fixer/FunctionNotation/PhpdocToReturnTypeFixer.php +++ b/src/Fixer/FunctionNotation/PhpdocToReturnTypeFixer.php @@ -75,6 +75,11 @@ final class PhpdocToReturnTypeFixer extends AbstractFixer implements Configurati */ private $classRegex = '/^\\\\?[a-zA-Z_\\x7f-\\xff](?:\\\\?[a-zA-Z0-9_\\x7f-\\xff]+)*(?\[\])*$/'; + /** + * @var array + */ + private $returnTypeCache = []; + /** * {@inheritdoc} */ @@ -251,6 +256,10 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens) continue; } + if (!$this->isValidType($returnType)) { + continue; + } + $this->fixFunctionDefinition($tokens, $startIndex, $isNullable, $returnType); } } @@ -337,4 +346,23 @@ private function findReturnAnnotations(Tokens $tokens, $index) return $doc->getAnnotationsOfType('return'); } + + /** + * @param string $returnType + * + * @return bool + */ + private function isValidType($returnType) + { + if (!\array_key_exists($returnType, $this->returnTypeCache)) { + try { + Tokens::fromCode(sprintf('returnTypeCache[$returnType] = true; + } catch (\ParseError $e) { + $this->returnTypeCache[$returnType] = false; + } + } + + return $this->returnTypeCache[$returnType]; + } } diff --git a/src/Fixer/PhpUnit/PhpUnitInternalClassFixer.php b/src/Fixer/PhpUnit/PhpUnitInternalClassFixer.php index 013cec41043..bb0bf276b0a 100644 --- a/src/Fixer/PhpUnit/PhpUnitInternalClassFixer.php +++ b/src/Fixer/PhpUnit/PhpUnitInternalClassFixer.php @@ -49,7 +49,7 @@ public function getDefinition() */ public function getPriority() { - return 1; + return 68; } /** diff --git a/src/Fixer/Phpdoc/PhpdocAnnotationWithoutDotFixer.php b/src/Fixer/Phpdoc/PhpdocAnnotationWithoutDotFixer.php index 77760a42362..82816f192f8 100644 --- a/src/Fixer/Phpdoc/PhpdocAnnotationWithoutDotFixer.php +++ b/src/Fixer/Phpdoc/PhpdocAnnotationWithoutDotFixer.php @@ -114,6 +114,10 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens) $content = Preg::replaceCallback( '/^(\s*\*\s*@\w+\s+'.$optionalTypeRegEx.')(\p{Lu}?(?=\p{Ll}|\p{Zs}))(.*)$/', static function (array $matches) { + if (\function_exists('mb_strtolower')) { + return $matches[1].mb_strtolower($matches[2]).$matches[3]; + } + return $matches[1].strtolower($matches[2]).$matches[3]; }, $startLine->getContent(), diff --git a/src/Fixer/Whitespace/BlankLineBeforeStatementFixer.php b/src/Fixer/Whitespace/BlankLineBeforeStatementFixer.php index d732f8b9a9a..6783a1e6d75 100644 --- a/src/Fixer/Whitespace/BlankLineBeforeStatementFixer.php +++ b/src/Fixer/Whitespace/BlankLineBeforeStatementFixer.php @@ -76,6 +76,8 @@ public function configure(array $configuration = null) foreach ($this->configuration['statements'] as $key) { $this->fixTokenMap[$key] = self::$tokenMap[$key]; } + + $this->fixTokenMap = array_values($this->fixTokenMap); } /** @@ -263,7 +265,7 @@ public function getPriority() */ public function isCandidate(Tokens $tokens) { - return $tokens->isAnyTokenKindsFound(array_values($this->fixTokenMap)); + return $tokens->isAnyTokenKindsFound($this->fixTokenMap); } /** @@ -271,13 +273,16 @@ public function isCandidate(Tokens $tokens) */ protected function applyFix(\SplFileInfo $file, Tokens $tokens) { - $tokenKinds = array_values($this->fixTokenMap); $analyzer = new TokensAnalyzer($tokens); for ($index = $tokens->count() - 1; $index > 0; --$index) { $token = $tokens[$index]; - if (!$token->isGivenKind($tokenKinds) || ($token->isGivenKind(T_WHILE) && $analyzer->isWhilePartOfDoWhile($index))) { + if (!$token->isGivenKind($this->fixTokenMap)) { + continue; + } + + if ($token->isGivenKind(T_WHILE) && $analyzer->isWhilePartOfDoWhile($index)) { continue; } @@ -331,7 +336,7 @@ private function shouldAddBlankLine(Tokens $tokens, $prevNonWhitespace) continue; } - return !$tokens[$j]->equals('{'); + return $tokens[$j]->equalsAny([';', '}']); } } diff --git a/tests/AutoReview/FixerFactoryTest.php b/tests/AutoReview/FixerFactoryTest.php index 82d4391dea7..4162b9d0d55 100644 --- a/tests/AutoReview/FixerFactoryTest.php +++ b/tests/AutoReview/FixerFactoryTest.php @@ -95,6 +95,7 @@ public function provideFixersPriorityCases() [$fixers['escape_implicit_backslashes'], $fixers['single_quote']], [$fixers['explicit_string_variable'], $fixers['simple_to_complex_string_variable']], [$fixers['final_internal_class'], $fixers['final_static_access']], + [$fixers['final_internal_class'], $fixers['protected_to_private']], [$fixers['final_internal_class'], $fixers['self_static_accessor']], [$fixers['fully_qualified_strict_types'], $fixers['no_superfluous_phpdoc_tags']], [$fixers['function_to_constant'], $fixers['native_function_casing']], diff --git a/tests/AutoReview/ProjectCodeTest.php b/tests/AutoReview/ProjectCodeTest.php index 13c6722555d..29ad10894e6 100644 --- a/tests/AutoReview/ProjectCodeTest.php +++ b/tests/AutoReview/ProjectCodeTest.php @@ -12,11 +12,8 @@ namespace PhpCsFixer\Tests\AutoReview; -if (!class_exists(\PHPUnit\Runner\Version::class)) { - class_alias('PHPUnit_Runner_Version', \PHPUnit\Runner\Version::class); -} - use PhpCsFixer\DocBlock\DocBlock; +use PhpCsFixer\Event\Event; use PhpCsFixer\Preg; use PhpCsFixer\Tests\TestCase; use PhpCsFixer\Tokenizer\Token; @@ -416,6 +413,67 @@ static function (\ReflectionMethod $reflectionMethod) use ($reflectionClass) { } } + /** + * @dataProvider provideSrcClassCases + * @dataProvider provideTestClassCases + * + * @param string $className + */ + public function testAllCodeContainSingleClassy($className) + { + $headerTypes = [ + T_ABSTRACT, + T_AS, + T_COMMENT, + T_DECLARE, + T_DOC_COMMENT, + T_FINAL, + T_LNUMBER, + T_NAMESPACE, + T_NS_SEPARATOR, + T_OPEN_TAG, + T_STRING, + T_USE, + T_WHITESPACE, + ]; + + $rc = new \ReflectionClass($className); + $file = $rc->getFileName(); + $tokens = Tokens::fromCode(file_get_contents($file)); + $isEvent = Event::class === $rc->getName(); // remove this exception when no longer needed + $classyIndex = null; + + static::assertTrue($tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds()), sprintf('File "%s" should contains a classy.', $file)); + + foreach ($tokens as $index => $token) { + if ($token->isClassy()) { + $classyIndex = $index; + + break; + } + + if (!$token->isGivenKind($headerTypes) && !$token->equalsAny([';', '=', '(', ')']) && !$isEvent) { + static::fail(sprintf('File "%s" should only contains single classy, found "%s" @ %d.', $file, $token->toJson(), $index)); + } + } + + static::assertNotNull($classyIndex, sprintf('File "%s" does not contain a classy.', $file)); + + $nextTokenOfKind = $tokens->getNextTokenOfKind($classyIndex, ['{']); + + if (!\is_int($nextTokenOfKind)) { + throw new \UnexpectedValueException('Classy without {} - braces.'); + } + + $classyEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $nextTokenOfKind); + + if ($isEvent) { + static::assertNotNull($tokens->getNextNonWhitespace($classyEndIndex), sprintf('File "%s" should not only contains a single classy.', $file)); + } else { + static::assertNull($tokens->getNextNonWhitespace($classyEndIndex), sprintf('File "%s" should only contains a single classy.', $file)); + } + } + public function provideSrcClassCases() { return array_map( diff --git a/tests/Fixer/Comment/NoEmptyCommentFixerTest.php b/tests/Fixer/Comment/NoEmptyCommentFixerTest.php index b12161b0baa..9cd2eb97510 100644 --- a/tests/Fixer/Comment/NoEmptyCommentFixerTest.php +++ b/tests/Fixer/Comment/NoEmptyCommentFixerTest.php @@ -212,6 +212,26 @@ public function provideFixCases() $bar = 2; ', ], + [ + ' [ ' [ + ' [ + ' [ + ' [ '