Skip to content

Commit

Permalink
fix: ClassDefinitionFixer for anonymous class with phpdoc/attribute o…
Browse files Browse the repository at this point in the history
…n separate line (PHP-CS-Fixer#7546)
  • Loading branch information
mvorisek authored and danog committed Feb 2, 2024
1 parent 8b19bfa commit d35efcb
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 7 deletions.
35 changes: 29 additions & 6 deletions src/Fixer/ClassNotation/ClassDefinitionFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
Expand Down Expand Up @@ -362,15 +363,37 @@ private function makeClassyDefinitionSingleLine(Tokens $tokens, int $startIndex,
{
for ($i = $endIndex; $i >= $startIndex; --$i) {
if ($tokens[$i]->isWhitespace()) {
if ($tokens[$i - 1]->isComment() || $tokens[$i + 1]->isComment()) {
if (str_contains($tokens[$i]->getContent(), "\n")) {
if (\defined('T_ATTRIBUTE')) { // @TODO: drop condition and else when PHP 8.0+ is required
if ($tokens[$i - 1]->isGivenKind(CT::T_ATTRIBUTE_CLOSE) || $tokens[$i + 1]->isGivenKind(T_ATTRIBUTE)) {
continue;
}
} else {
if (($tokens[$i - 1]->isComment() && str_ends_with($tokens[$i - 1]->getContent(), ']'))
|| ($tokens[$i + 1]->isComment() && str_starts_with($tokens[$i + 1]->getContent(), '#['))
) {
continue;
}
}

if ($tokens[$i - 1]->isGivenKind(T_DOC_COMMENT) || $tokens[$i + 1]->isGivenKind(T_DOC_COMMENT)) {
continue;
}
}

if ($tokens[$i - 1]->isComment()) {
$content = $tokens[$i - 1]->getContent();
if (!str_starts_with($content, '//') && !str_starts_with($content, '#')) {
$tokens[$i] = new Token([T_WHITESPACE, ' ']);
}

if (!('#' === $content || str_starts_with($content, '//'))) {
$content = $tokens[$i + 1]->getContent();
continue;
}

if (!('#' === $content || str_starts_with($content, '//'))) {
$tokens[$i] = new Token([T_WHITESPACE, ' ']);
}
if ($tokens[$i + 1]->isComment()) {
$content = $tokens[$i + 1]->getContent();
if (!str_starts_with($content, '//')) {
$tokens[$i] = new Token([T_WHITESPACE, ' ']);
}

continue;
Expand Down
17 changes: 17 additions & 0 deletions src/Fixer/LanguageConstruct/SingleSpaceAroundConstructFixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,23 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
}
}

if ($tokens[$whitespaceTokenIndex]->isWhitespace() && str_contains($tokens[$whitespaceTokenIndex]->getContent(), "\n")) {
$nextNextToken = $tokens[$whitespaceTokenIndex + 1];
if (\defined('T_ATTRIBUTE')) { // @TODO: drop condition and else when PHP 8.0+ is required
if ($nextNextToken->isGivenKind(T_ATTRIBUTE)) {
continue;
}
} else {
if ($nextNextToken->isComment() && str_starts_with($nextNextToken->getContent(), '#[')) {
continue;
}
}

if ($nextNextToken->isGivenKind(T_DOC_COMMENT)) {
continue;
}
}

$tokens->ensureWhitespaceAtIndex($whitespaceTokenIndex, 0, ' ');
}
}
Expand Down
63 changes: 62 additions & 1 deletion tests/Fixer/ClassNotation/ClassDefinitionFixerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public static function provideInvalidConfigurationCases(): iterable
*
* @dataProvider provideFixCases
*/
public function testFix(string $expected, string $input, array $configuration = []): void
public function testFix(string $expected, string $input = null, array $configuration = []): void
{
$this->fixer->configure($configuration);
$this->doTest($expected, $input);
Expand Down Expand Up @@ -305,6 +305,67 @@ class#
['space_before_parenthesis' => true, 'inline_constructor_arguments' => false],
];

yield 'single attribute on separate line' => [
<<<'EOF'
<?php
$a = new
#[FOO]
class() {};
EOF,
];

yield 'multiple attributes on separate line' => [
<<<'EOF'
<?php
$a = new
#[FOO]
#[\Ns\Bar]
class() {};
EOF,
];

yield 'single line phpdoc on separate line' => [
<<<'EOF'
<?php
$a = new
/** @property string $x */
class() {};
EOF,
];

yield 'multi line phpdoc on separate line' => [
<<<'EOF'
<?php
$a = new
/**
@property string $x
*/
class() {};
EOF,
];

yield 'phpdoc and single attribute on separate line' => [
<<<'EOF'
<?php
$a = new
/**
@property string $x
*/
#[FOO]
class() {};
EOF,
];

yield 'phpdoc and multiple attributes on separate line' => [
<<<'EOF'
<?php
$a = new
/** @property string $x */
#[FOO] #[\Ns\Bar]
class() {};
EOF,
];

yield from self::provideClassyCases('class');

yield from self::provideClassyExtendingCases('class');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1878,6 +1878,26 @@ public static function provideFixWithNewCases(): iterable
'<?php new /* foo */Bar();',
'<?php new/* foo */Bar();',
];

yield 'attribute on separate line' => [
<<<'EOF'
<?php
$a = new
#[FOO]
class() {};
EOF,
];

yield 'phpdoc on separate line' => [
<<<'EOF'
<?php
$a = new
/**
@property string $x
*/
class() {};
EOF,
];
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2122,6 +2122,26 @@ public static function provideFixWithNewCases(): iterable
'<?php new /* foo */Bar();',
'<?php new/* foo */Bar();',
];

yield 'attribute on separate line' => [
<<<'EOF'
<?php
$a = new
#[FOO]
class() {};
EOF,
];

yield 'phpdoc on separate line' => [
<<<'EOF'
<?php
$a = new
/**
@property string $x
*/
class() {};
EOF,
];
}

/**
Expand Down
50 changes: 50 additions & 0 deletions tests/Fixtures/Integration/misc/anonymous_class_with_phpdoc.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
--TEST--
Integration of fixers: Anonymous class /w PHPDoc and attributes on separate line.
--RULESET--
{"@PhpCsFixer": true}
--EXPECT--
<?php

$a = new
/** @property string $x */
class() {};

$b = new
#[X]
class() {};

$c = new
/** @property string $x */
#[X] #[Y\Z]
class() {};

class Z {}

$d = new
#[X] // comment
class() {};

--INPUT--
<?php

$a = new
/** @property string $x */

class() {};


$b = new
#[X]
class() {};


$c = new
/** @property string $x */
#[X] #[Y\Z]
class() {};

class Z {}

$d = new
#[X] // comment
class() {};

0 comments on commit d35efcb

Please sign in to comment.