Skip to content

Commit

Permalink
Merge branch '2.15' into 2.16
Browse files Browse the repository at this point in the history
* 2.15:
  BlankLineBeforeStatementFixer - better comment handling
  NoEmptyCommentFixer - handle multiline comments
  Assert all project source and test files contain a single classy.
  PhpdocAnnotationWithoutDotFixer - handle unicode characters using mb_*
  Update naming
  Cache reserved keyword list (PHP-CS-Fixer#4)
  Add one more test
  PhpdocToReturnTypeFixer - tests against all tokens
  FinalInternalClassFixer - must run before ProtectedToPrivateFixer
  • Loading branch information
SpacePossum committed Jun 14, 2020
2 parents 0d06f47 + c0d6a34 commit a4e94a9
Show file tree
Hide file tree
Showing 15 changed files with 189 additions and 13 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down
4 changes: 2 additions & 2 deletions src/Fixer/ClassNotation/FinalInternalClassFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/Fixer/ClassNotation/ProtectedToPrivateFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ protected function test()
* {@inheritdoc}
*
* Must run before OrderedClassElementsFixer.
* Must run after FinalInternalClassFixer.
*/
public function getPriority()
{
Expand Down
2 changes: 1 addition & 1 deletion src/Fixer/Comment/NoEmptyCommentFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 '//'
];

Expand Down
28 changes: 28 additions & 0 deletions src/Fixer/FunctionNotation/PhpdocToReturnTypeFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ final class PhpdocToReturnTypeFixer extends AbstractFixer implements Configurati
*/
private $classRegex = '/^\\\\?[a-zA-Z_\\x7f-\\xff](?:\\\\?[a-zA-Z0-9_\\x7f-\\xff]+)*(?<array>\[\])*$/';

/**
* @var array<string, bool>
*/
private $returnTypeCache = [];

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -251,6 +256,10 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens)
continue;
}

if (!$this->isValidType($returnType)) {
continue;
}

$this->fixFunctionDefinition($tokens, $startIndex, $isNullable, $returnType);
}
}
Expand Down Expand Up @@ -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('<?php function f():%s {}', $returnType));
$this->returnTypeCache[$returnType] = true;
} catch (\ParseError $e) {
$this->returnTypeCache[$returnType] = false;
}
}

return $this->returnTypeCache[$returnType];
}
}
2 changes: 1 addition & 1 deletion src/Fixer/PhpUnit/PhpUnitInternalClassFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public function getDefinition()
*/
public function getPriority()
{
return 1;
return 68;
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/Fixer/Phpdoc/PhpdocAnnotationWithoutDotFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
13 changes: 9 additions & 4 deletions src/Fixer/Whitespace/BlankLineBeforeStatementFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -263,21 +265,24 @@ public function getPriority()
*/
public function isCandidate(Tokens $tokens)
{
return $tokens->isAnyTokenKindsFound(array_values($this->fixTokenMap));
return $tokens->isAnyTokenKindsFound($this->fixTokenMap);
}

/**
* {@inheritdoc}
*/
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;
}

Expand Down Expand Up @@ -331,7 +336,7 @@ private function shouldAddBlankLine(Tokens $tokens, $prevNonWhitespace)
continue;
}

return !$tokens[$j]->equals('{');
return $tokens[$j]->equalsAny([';', '}']);
}
}

Expand Down
1 change: 1 addition & 0 deletions tests/AutoReview/FixerFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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']],
Expand Down
66 changes: 62 additions & 4 deletions tests/AutoReview/ProjectCodeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down
20 changes: 20 additions & 0 deletions tests/Fixer/Comment/NoEmptyCommentFixerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,26 @@ public function provideFixCases()
$bar = 2;
',
],
[
'<?php
'.'
',
'<?php
/*
*
*/
',
],
[
'<?php
'.'
',
'<?php
/********
*
********/
',
],
];
}

Expand Down
9 changes: 9 additions & 0 deletions tests/Fixer/FunctionNotation/PhpdocToReturnTypeFixerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ public function provideFixCases()
'invalid class 2' => [
'<?php /** @return \\Foo\\\\Bar */ function my_foo() {}',
],
'invalid class 3' => [
'<?php /** @return Break */ function my_foo() {}',
],
'invalid class 4' => [
'<?php /** @return __CLASS__ */ function my_foo() {}',
],
'invalid class 5' => [
'<?php /** @return I\Want\To\Break\Free */ function queen() {}',
],
'blacklisted class methods' => [
'<?php
Expand Down
14 changes: 14 additions & 0 deletions tests/Fixer/Phpdoc/PhpdocAnnotationWithoutDotFixerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,20 @@ function foo($str) {}',
*/
function nothingToDo() {}',
],
[
'<?php
/**
* @param string $bar τάχιστη
*/
function foo ($bar) {}
',
'<?php
/**
* @param string $bar Τάχιστη.
*/
function foo ($bar) {}
',
],
];
}
}
11 changes: 11 additions & 0 deletions tests/Fixer/Whitespace/BlankLineBeforeStatementFixerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,17 @@ function foo()
// comment
return "bar";
}',
],
[
'<?php
function foo()
{
switch ($foo) {
case 2: // comment
return 1;
}
}',
],
];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
Integration of fixers: final_internal_class,protected_to_private.
--RULESET--
{"final_internal_class": true, "protected_to_private": true}
--EXPECT--
<?php
/**
* @internal
*/
final class Foo
{
private $bar;
private function baz() {}
}

--INPUT--
<?php
/**
* @internal
*/
class Foo
{
protected $bar;
protected function baz() {}
}

0 comments on commit a4e94a9

Please sign in to comment.