Skip to content

Commit

Permalink
Deprecate PhpdocParamOrderFixer (#892)
Browse files Browse the repository at this point in the history
  • Loading branch information
kubawerlos committed May 24, 2023
1 parent 69ee4b4 commit ac74539
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 121 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,9 @@
# CHANGELOG for PHP CS Fixer: custom fixers

## v3.15.0
- Deprecate PhpdocParamOrderFixer
- Update minimum PHP CS Fixer version to 3.17.0

## v3.14.0
- Add EmptyFunctionBodyFixer
- Deprecate DataProviderStaticFixer - use "php_unit_data_provider_static"
Expand Down
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -3,7 +3,7 @@
[![Latest stable version](https://img.shields.io/packagist/v/kubawerlos/php-cs-fixer-custom-fixers.svg?label=current%20version)](https://packagist.org/packages/kubawerlos/php-cs-fixer-custom-fixers)
[![PHP version](https://img.shields.io/packagist/php-v/kubawerlos/php-cs-fixer-custom-fixers.svg)](https://php.net)
[![License](https://img.shields.io/github/license/kubawerlos/php-cs-fixer-custom-fixers.svg)](LICENSE)
![Tests](https://img.shields.io/badge/tests-3440-brightgreen.svg)
![Tests](https://img.shields.io/badge/tests-3441-brightgreen.svg)
[![Downloads](https://img.shields.io/packagist/dt/kubawerlos/php-cs-fixer-custom-fixers.svg)](https://packagist.org/packages/kubawerlos/php-cs-fixer-custom-fixers)

[![CI Status](https://github.com/kubawerlos/php-cs-fixer-custom-fixers/workflows/CI/badge.svg?branch=main)](https://github.com/kubawerlos/php-cs-fixer-custom-fixers/actions)
Expand Down Expand Up @@ -491,6 +491,7 @@ Configuration options:

#### PhpdocParamOrderFixer
The `@param` annotations must be in the same order as the function parameters.
DEPRECATED: use `phpdoc_param_order` instead.
```diff
<?php
/**
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Expand Up @@ -13,7 +13,7 @@
"php": "^7.4 || ^8.0",
"ext-filter": "*",
"ext-tokenizer": "*",
"friendsofphp/php-cs-fixer": "^3.16.0"
"friendsofphp/php-cs-fixer": "^3.17"
},
"require-dev": {
"phpunit/phpunit": "^9.6.4 || ^10.0.14"
Expand Down
138 changes: 19 additions & 119 deletions src/Fixer/PhpdocParamOrderFixer.php
Expand Up @@ -11,18 +11,25 @@

namespace PhpCsFixerCustomFixers\Fixer;

use PhpCsFixer\DocBlock\Annotation;
use PhpCsFixer\DocBlock\DocBlock;
use PhpCsFixer\DocBlock\Line;
use PhpCsFixer\Fixer\DeprecatedFixerInterface;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;

final class PhpdocParamOrderFixer extends AbstractFixer
/**
* @deprecated
*/
final class PhpdocParamOrderFixer extends AbstractFixer implements DeprecatedFixerInterface
{
/** @var \PhpCsFixer\Fixer\Phpdoc\PhpdocParamOrderFixer */
private $phpdocParamOrderFixer;

public function __construct()
{
$this->phpdocParamOrderFixer = new \PhpCsFixer\Fixer\Phpdoc\PhpdocParamOrderFixer();
}

public function getDefinition(): FixerDefinitionInterface
{
return new FixerDefinition(
Expand All @@ -44,136 +51,29 @@ function foo($a, $b, $c) {}
*/
public function getPriority(): int
{
return 0;
return $this->phpdocParamOrderFixer->getPriority();
}

public function isCandidate(Tokens $tokens): bool
{
return $tokens->isAllTokenKindsFound([\T_DOC_COMMENT, \T_FUNCTION]);
return $this->phpdocParamOrderFixer->isCandidate($tokens);
}

public function isRisky(): bool
{
return false;
return $this->phpdocParamOrderFixer->isRisky();
}

public function fix(\SplFileInfo $file, Tokens $tokens): void
{
for ($index = 0; $index < $tokens->count(); $index++) {
if (!$tokens[$index]->isGivenKind(\T_DOC_COMMENT)) {
continue;
}

$functionIndex = $tokens->getTokenNotOfKindSibling($index, 1, [[\T_ABSTRACT], [\T_COMMENT], [\T_FINAL], [\T_PRIVATE], [\T_PROTECTED], [\T_PUBLIC], [\T_STATIC], [\T_WHITESPACE]]);

if ($functionIndex === null) {
return;
}

if (!$tokens[$functionIndex]->isGivenKind(\T_FUNCTION)) {
continue;
}

$paramNames = $this->getParamNames($tokens, $functionIndex);

$docBlock = new DocBlock($tokens[$index]->getContent());
$sorted = $this->getSortedAnnotations($docBlock->getAnnotations(), $paramNames);

foreach ($sorted as $annotationIndex => $annotationContent) {
$annotation = $docBlock->getAnnotation($annotationIndex);
\assert($annotation instanceof Annotation);
$annotation->remove();

$line = $docBlock->getLine($annotation->getStart());
\assert($line instanceof Line);
$line->setContent($annotationContent);
}

if ($docBlock->getContent() === $tokens[$index]->getContent()) {
continue;
}

$tokens[$index] = new Token([\T_DOC_COMMENT, $docBlock->getContent()]);
}
$this->phpdocParamOrderFixer->fix($file, $tokens);
}

/**
* @return array<string>
*/
private function getParamNames(Tokens $tokens, int $functionIndex): array
public function getSuccessorsNames(): array
{
$paramBlockStartIndex = $tokens->getNextTokenOfKind($functionIndex, ['(']);
\assert(\is_int($paramBlockStartIndex));

$paramBlockEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $paramBlockStartIndex);

$paramNames = [];
for ($index = $paramBlockStartIndex; $index < $paramBlockEndIndex; $index++) {
if ($tokens[$index]->isGivenKind(\T_VARIABLE)) {
$paramNames[] = $tokens[$index]->getContent();
}
}

return $paramNames;
}

/**
* @param array<Annotation> $annotations
* @param array<string> $paramNames
*
* @return array<int, string>
*/
private function getSortedAnnotations(array $annotations, array $paramNames): array
{
$paramFound = false;
$annotationsBeforeParams = [];
$paramsByName = \array_combine($paramNames, \array_fill(0, \count($paramNames), null));
$superfluousParams = [];
$annotationsAfterParams = [];

foreach ($annotations as $annotation) {
if ($annotation->getTag()->getName() === 'param') {
$paramFound = true;
$paramName = $this->getParamName($paramNames, $paramsByName, $annotation->getContent());

if ($paramName === null) {
$superfluousParams[] = $annotation->getContent();
} else {
$paramsByName[$paramName] = $annotation->getContent();
}

continue;
}

if ($paramFound) {
$annotationsAfterParams[] = $annotation->getContent();
continue;
}

$annotationsBeforeParams[] = $annotation->getContent();
}

return \array_merge($annotationsBeforeParams, \array_values(\array_filter($paramsByName)), $superfluousParams, $annotationsAfterParams);
}

/**
* @param array<string> $paramNames
* @param array<string, null|string> $paramsByName
*/
private function getParamName(array $paramNames, array $paramsByName, string $annotation): ?string
{
foreach ($paramNames as $paramName) {
if (Preg::match(\sprintf('/@param\s+(?:[^\$](?:[^<\s]|<[^>]*>)*\s+)?(?:&|\.\.\.)?\s*(\Q%s\E)\b/', $paramName), $annotation, $matches) !== 1) {
continue;
}

if (\array_key_exists($matches[1], $paramsByName) && $paramsByName[$matches[1]] !== null) {
return null;
}

return $matches[1];
}

return null;
return [$this->phpdocParamOrderFixer->getName()];
}
}
10 changes: 10 additions & 0 deletions tests/Fixer/PhpdocParamOrderFixerTest.php
Expand Up @@ -11,9 +11,14 @@

namespace Tests\Fixer;

use PhpCsFixer\Fixer\ConfigurableFixerInterface;
use PhpCsFixer\Fixer\DeprecatedFixerInterface;

/**
* @internal
*
* @property ConfigurableFixerInterface&DeprecatedFixerInterface $fixer
*
* @covers \PhpCsFixerCustomFixers\Fixer\PhpdocParamOrderFixer
*/
final class PhpdocParamOrderFixerTest extends AbstractFixerTestCase
Expand All @@ -23,6 +28,11 @@ public function testIsRisky(): void
self::assertFalse($this->fixer->isRisky());
}

public function testSuccessorName(): void
{
self::assertContains('phpdoc_param_order', $this->fixer->getSuccessorsNames());
}

/**
* @dataProvider provideFixCases
*/
Expand Down

0 comments on commit ac74539

Please sign in to comment.