Missing dependancies
This commit is contained in:
+239
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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\Comment;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
|
||||
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
|
||||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
|
||||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
|
||||
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Preg;
|
||||
use PhpCsFixer\Tokenizer\Analyzer\CommentsAnalyzer;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
use PhpCsFixer\Utils;
|
||||
|
||||
/**
|
||||
* @author Kuba Werłos <werlos@gmail.com>
|
||||
*/
|
||||
final class CommentToPhpdocFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $ignoredTags = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isTokenKindFound(T_COMMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRisky(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run before GeneralPhpdocAnnotationRemoveFixer, GeneralPhpdocTagRenameFixer, NoBlankLinesAfterPhpdocFixer, NoEmptyPhpdocFixer, NoSuperfluousPhpdocTagsFixer, PhpdocAddMissingParamAnnotationFixer, PhpdocAlignFixer, PhpdocAnnotationWithoutDotFixer, PhpdocInlineTagNormalizerFixer, PhpdocLineSpanFixer, PhpdocNoAccessFixer, PhpdocNoAliasTagFixer, PhpdocNoEmptyReturnFixer, PhpdocNoPackageFixer, PhpdocNoUselessInheritdocFixer, PhpdocOrderByValueFixer, PhpdocOrderFixer, PhpdocReturnSelfReferenceFixer, PhpdocSeparationFixer, PhpdocSingleLineVarSpacingFixer, PhpdocSummaryFixer, PhpdocTagCasingFixer, PhpdocTagTypeFixer, PhpdocToCommentFixer, PhpdocToParamTypeFixer, PhpdocToPropertyTypeFixer, PhpdocToReturnTypeFixer, PhpdocTrimConsecutiveBlankLineSeparationFixer, PhpdocTrimFixer, PhpdocTypesOrderFixer, PhpdocVarAnnotationCorrectOrderFixer, PhpdocVarWithoutNameFixer.
|
||||
* Must run after AlignMultilineCommentFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
// Should be run before all other PHPDoc fixers
|
||||
return 26;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Comments with annotation should be docblock when used on structural elements.',
|
||||
[
|
||||
new CodeSample("<?php /* header */ \$x = true; /* @var bool \$isFoo */ \$isFoo = true;\n"),
|
||||
new CodeSample("<?php\n// @todo do something later\n\$foo = 1;\n\n// @var int \$a\n\$a = foo();\n", ['ignored_tags' => ['todo']]),
|
||||
],
|
||||
null,
|
||||
'Risky as new docblocks might mean more, e.g. a Doctrine entity might have a new column in database.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
parent::configure($configuration);
|
||||
|
||||
$this->ignoredTags = array_map(
|
||||
static function (string $tag): string {
|
||||
return strtolower($tag);
|
||||
},
|
||||
$this->configuration['ignored_tags']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
|
||||
{
|
||||
return new FixerConfigurationResolver([
|
||||
(new FixerOptionBuilder('ignored_tags', 'List of ignored tags'))
|
||||
->setAllowedTypes(['array'])
|
||||
->setDefault([])
|
||||
->getOption(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
$commentsAnalyzer = new CommentsAnalyzer();
|
||||
|
||||
for ($index = 0, $limit = \count($tokens); $index < $limit; ++$index) {
|
||||
$token = $tokens[$index];
|
||||
|
||||
if (!$token->isGivenKind(T_COMMENT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($commentsAnalyzer->isHeaderComment($tokens, $index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$commentsAnalyzer->isBeforeStructuralElement($tokens, $index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$commentIndices = $commentsAnalyzer->getCommentBlockIndices($tokens, $index);
|
||||
|
||||
if ($this->isCommentCandidate($tokens, $commentIndices)) {
|
||||
$this->fixComment($tokens, $commentIndices);
|
||||
}
|
||||
|
||||
$index = max($commentIndices);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $indices
|
||||
*/
|
||||
private function isCommentCandidate(Tokens $tokens, array $indices): bool
|
||||
{
|
||||
return array_reduce(
|
||||
$indices,
|
||||
function (bool $carry, int $index) use ($tokens): bool {
|
||||
if ($carry) {
|
||||
return true;
|
||||
}
|
||||
if (1 !== Preg::match('~(?:#|//|/\*+|\R(?:\s*\*)?)\s*\@([a-zA-Z0-9_\\\\-]+)(?=\s|\(|$)~', $tokens[$index]->getContent(), $matches)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !\in_array(strtolower($matches[1]), $this->ignoredTags, true);
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $indices
|
||||
*/
|
||||
private function fixComment(Tokens $tokens, array $indices): void
|
||||
{
|
||||
if (1 === \count($indices)) {
|
||||
$this->fixCommentSingleLine($tokens, reset($indices));
|
||||
} else {
|
||||
$this->fixCommentMultiLine($tokens, $indices);
|
||||
}
|
||||
}
|
||||
|
||||
private function fixCommentSingleLine(Tokens $tokens, int $index): void
|
||||
{
|
||||
$message = $this->getMessage($tokens[$index]->getContent());
|
||||
|
||||
if ('' !== trim(substr($message, 0, 1))) {
|
||||
$message = ' '.$message;
|
||||
}
|
||||
|
||||
if ('' !== trim(substr($message, -1))) {
|
||||
$message .= ' ';
|
||||
}
|
||||
|
||||
$tokens[$index] = new Token([T_DOC_COMMENT, '/**'.$message.'*/']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $indices
|
||||
*/
|
||||
private function fixCommentMultiLine(Tokens $tokens, array $indices): void
|
||||
{
|
||||
$startIndex = reset($indices);
|
||||
$indent = Utils::calculateTrailingWhitespaceIndent($tokens[$startIndex - 1]);
|
||||
|
||||
$newContent = '/**'.$this->whitespacesConfig->getLineEnding();
|
||||
$count = max($indices);
|
||||
|
||||
for ($index = $startIndex; $index <= $count; ++$index) {
|
||||
if (!$tokens[$index]->isComment()) {
|
||||
continue;
|
||||
}
|
||||
if (str_contains($tokens[$index]->getContent(), '*/')) {
|
||||
return;
|
||||
}
|
||||
$message = $this->getMessage($tokens[$index]->getContent());
|
||||
if ('' !== trim(substr($message, 0, 1))) {
|
||||
$message = ' '.$message;
|
||||
}
|
||||
$newContent .= $indent.' *'.$message.$this->whitespacesConfig->getLineEnding();
|
||||
}
|
||||
|
||||
for ($index = $startIndex; $index <= $count; ++$index) {
|
||||
$tokens->clearAt($index);
|
||||
}
|
||||
|
||||
$newContent .= $indent.' */';
|
||||
|
||||
$tokens->insertAt($startIndex, new Token([T_DOC_COMMENT, $newContent]));
|
||||
}
|
||||
|
||||
private function getMessage(string $content): string
|
||||
{
|
||||
if (str_starts_with($content, '#')) {
|
||||
return substr($content, 1);
|
||||
}
|
||||
if (str_starts_with($content, '//')) {
|
||||
return substr($content, 2);
|
||||
}
|
||||
|
||||
return rtrim(ltrim($content, '/*'), '*/');
|
||||
}
|
||||
}
|
||||
+454
@@ -0,0 +1,454 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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\Comment;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
|
||||
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
|
||||
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
|
||||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
|
||||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
|
||||
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Preg;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
|
||||
/**
|
||||
* @author Antonio J. García Lagar <aj@garcialagar.es>
|
||||
*/
|
||||
final class HeaderCommentFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
|
||||
{
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public const HEADER_PHPDOC = 'PHPDoc';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public const HEADER_COMMENT = 'comment';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Add, replace or remove header comment.',
|
||||
[
|
||||
new CodeSample(
|
||||
'<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace A\B;
|
||||
|
||||
echo 1;
|
||||
',
|
||||
[
|
||||
'header' => 'Made with love.',
|
||||
]
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace A\B;
|
||||
|
||||
echo 1;
|
||||
',
|
||||
[
|
||||
'header' => 'Made with love.',
|
||||
'comment_type' => 'PHPDoc',
|
||||
'location' => 'after_open',
|
||||
'separate' => 'bottom',
|
||||
]
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace A\B;
|
||||
|
||||
echo 1;
|
||||
',
|
||||
[
|
||||
'header' => 'Made with love.',
|
||||
'comment_type' => 'comment',
|
||||
'location' => 'after_declare_strict',
|
||||
]
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Comment is not wanted here.
|
||||
*/
|
||||
|
||||
namespace A\B;
|
||||
|
||||
echo 1;
|
||||
',
|
||||
[
|
||||
'header' => '',
|
||||
]
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isMonolithicPhp();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run before SingleLineCommentStyleFixer.
|
||||
* Must run after DeclareStrictTypesFixer, NoBlankLinesAfterPhpdocFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
// When this fixer is configured with ["separate" => "bottom", "comment_type" => "PHPDoc"]
|
||||
// and the target file has no namespace or declare() construct,
|
||||
// the fixed header comment gets trimmed by NoBlankLinesAfterPhpdocFixer if we run before it.
|
||||
return -30;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
$location = $this->configuration['location'];
|
||||
$locationIndices = [];
|
||||
|
||||
foreach (['after_open', 'after_declare_strict'] as $possibleLocation) {
|
||||
$locationIndex = $this->findHeaderCommentInsertionIndex($tokens, $possibleLocation);
|
||||
|
||||
if (!isset($locationIndices[$locationIndex]) || $possibleLocation === $location) {
|
||||
$locationIndices[$locationIndex] = $possibleLocation;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($locationIndices as $possibleLocation) {
|
||||
// figure out where the comment should be placed
|
||||
$headerNewIndex = $this->findHeaderCommentInsertionIndex($tokens, $possibleLocation);
|
||||
|
||||
// check if there is already a comment
|
||||
$headerCurrentIndex = $this->findHeaderCommentCurrentIndex($tokens, $headerNewIndex - 1);
|
||||
|
||||
if (null === $headerCurrentIndex) {
|
||||
if ('' === $this->configuration['header'] || $possibleLocation !== $location) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->insertHeader($tokens, $headerNewIndex);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$sameComment = $this->getHeaderAsComment() === $tokens[$headerCurrentIndex]->getContent();
|
||||
$expectedLocation = $possibleLocation === $location;
|
||||
|
||||
if (!$sameComment || !$expectedLocation) {
|
||||
if ($expectedLocation ^ $sameComment) {
|
||||
$this->removeHeader($tokens, $headerCurrentIndex);
|
||||
}
|
||||
|
||||
if ('' === $this->configuration['header']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($possibleLocation === $location) {
|
||||
$this->insertHeader($tokens, $headerNewIndex);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->fixWhiteSpaceAroundHeader($tokens, $headerCurrentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
|
||||
{
|
||||
$fixerName = $this->getName();
|
||||
|
||||
return new FixerConfigurationResolver([
|
||||
(new FixerOptionBuilder('header', 'Proper header content.'))
|
||||
->setAllowedTypes(['string'])
|
||||
->setNormalizer(static function (Options $options, string $value) use ($fixerName): string {
|
||||
if ('' === trim($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (str_contains($value, '*/')) {
|
||||
throw new InvalidFixerConfigurationException($fixerName, 'Cannot use \'*/\' in header.');
|
||||
}
|
||||
|
||||
return $value;
|
||||
})
|
||||
->getOption(),
|
||||
(new FixerOptionBuilder('comment_type', 'Comment syntax type.'))
|
||||
->setAllowedValues([self::HEADER_PHPDOC, self::HEADER_COMMENT])
|
||||
->setDefault(self::HEADER_COMMENT)
|
||||
->getOption(),
|
||||
(new FixerOptionBuilder('location', 'The location of the inserted header.'))
|
||||
->setAllowedValues(['after_open', 'after_declare_strict'])
|
||||
->setDefault('after_declare_strict')
|
||||
->getOption(),
|
||||
(new FixerOptionBuilder('separate', 'Whether the header should be separated from the file content with a new line.'))
|
||||
->setAllowedValues(['both', 'top', 'bottom', 'none'])
|
||||
->setDefault('both')
|
||||
->getOption(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enclose the given text in a comment block.
|
||||
*/
|
||||
private function getHeaderAsComment(): string
|
||||
{
|
||||
$lineEnding = $this->whitespacesConfig->getLineEnding();
|
||||
$comment = (self::HEADER_COMMENT === $this->configuration['comment_type'] ? '/*' : '/**').$lineEnding;
|
||||
$lines = explode("\n", str_replace("\r", '', $this->configuration['header']));
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$comment .= rtrim(' * '.$line).$lineEnding;
|
||||
}
|
||||
|
||||
return $comment.' */';
|
||||
}
|
||||
|
||||
private function findHeaderCommentCurrentIndex(Tokens $tokens, int $headerNewIndex): ?int
|
||||
{
|
||||
$index = $tokens->getNextNonWhitespace($headerNewIndex);
|
||||
|
||||
if (null === $index || !$tokens[$index]->isComment()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$next = $index + 1;
|
||||
|
||||
if (!isset($tokens[$next]) || \in_array($this->configuration['separate'], ['top', 'none'], true) || !$tokens[$index]->isGivenKind(T_DOC_COMMENT)) {
|
||||
return $index;
|
||||
}
|
||||
|
||||
if ($tokens[$next]->isWhitespace()) {
|
||||
if (!Preg::match('/^\h*\R\h*$/D', $tokens[$next]->getContent())) {
|
||||
return $index;
|
||||
}
|
||||
|
||||
++$next;
|
||||
}
|
||||
|
||||
if (!isset($tokens[$next]) || !$tokens[$next]->isClassy() && !$tokens[$next]->isGivenKind(T_FUNCTION)) {
|
||||
return $index;
|
||||
}
|
||||
|
||||
return $this->getHeaderAsComment() === $tokens[$index]->getContent() ? $index : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index where the header comment must be inserted.
|
||||
*/
|
||||
private function findHeaderCommentInsertionIndex(Tokens $tokens, string $location): int
|
||||
{
|
||||
$openTagIndex = $tokens[0]->isGivenKind(T_OPEN_TAG) ? 0 : $tokens->getNextTokenOfKind(0, [[T_OPEN_TAG]]);
|
||||
|
||||
if (null === $openTagIndex) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ('after_open' === $location) {
|
||||
return $openTagIndex + 1;
|
||||
}
|
||||
|
||||
$index = $tokens->getNextMeaningfulToken($openTagIndex);
|
||||
|
||||
if (null === $index) {
|
||||
return $openTagIndex + 1; // file without meaningful tokens but an open tag, comment should always be placed directly after the open tag
|
||||
}
|
||||
|
||||
if (!$tokens[$index]->isGivenKind(T_DECLARE)) {
|
||||
return $openTagIndex + 1;
|
||||
}
|
||||
|
||||
$next = $tokens->getNextMeaningfulToken($index);
|
||||
|
||||
if (null === $next || !$tokens[$next]->equals('(')) {
|
||||
return $openTagIndex + 1;
|
||||
}
|
||||
|
||||
$next = $tokens->getNextMeaningfulToken($next);
|
||||
|
||||
if (null === $next || !$tokens[$next]->equals([T_STRING, 'strict_types'], false)) {
|
||||
return $openTagIndex + 1;
|
||||
}
|
||||
|
||||
$next = $tokens->getNextMeaningfulToken($next);
|
||||
|
||||
if (null === $next || !$tokens[$next]->equals('=')) {
|
||||
return $openTagIndex + 1;
|
||||
}
|
||||
|
||||
$next = $tokens->getNextMeaningfulToken($next);
|
||||
|
||||
if (null === $next || !$tokens[$next]->isGivenKind(T_LNUMBER)) {
|
||||
return $openTagIndex + 1;
|
||||
}
|
||||
|
||||
$next = $tokens->getNextMeaningfulToken($next);
|
||||
|
||||
if (null === $next || !$tokens[$next]->equals(')')) {
|
||||
return $openTagIndex + 1;
|
||||
}
|
||||
|
||||
$next = $tokens->getNextMeaningfulToken($next);
|
||||
|
||||
if (null === $next || !$tokens[$next]->equals(';')) { // don't insert after close tag
|
||||
return $openTagIndex + 1;
|
||||
}
|
||||
|
||||
return $next + 1;
|
||||
}
|
||||
|
||||
private function fixWhiteSpaceAroundHeader(Tokens $tokens, int $headerIndex): void
|
||||
{
|
||||
$lineEnding = $this->whitespacesConfig->getLineEnding();
|
||||
|
||||
// fix lines after header comment
|
||||
if (
|
||||
('both' === $this->configuration['separate'] || 'bottom' === $this->configuration['separate'])
|
||||
&& null !== $tokens->getNextMeaningfulToken($headerIndex)
|
||||
) {
|
||||
$expectedLineCount = 2;
|
||||
} else {
|
||||
$expectedLineCount = 1;
|
||||
}
|
||||
|
||||
if ($headerIndex === \count($tokens) - 1) {
|
||||
$tokens->insertAt($headerIndex + 1, new Token([T_WHITESPACE, str_repeat($lineEnding, $expectedLineCount)]));
|
||||
} else {
|
||||
$lineBreakCount = $this->getLineBreakCount($tokens, $headerIndex, 1);
|
||||
|
||||
if ($lineBreakCount < $expectedLineCount) {
|
||||
$missing = str_repeat($lineEnding, $expectedLineCount - $lineBreakCount);
|
||||
|
||||
if ($tokens[$headerIndex + 1]->isWhitespace()) {
|
||||
$tokens[$headerIndex + 1] = new Token([T_WHITESPACE, $missing.$tokens[$headerIndex + 1]->getContent()]);
|
||||
} else {
|
||||
$tokens->insertAt($headerIndex + 1, new Token([T_WHITESPACE, $missing]));
|
||||
}
|
||||
} elseif ($lineBreakCount > $expectedLineCount && $tokens[$headerIndex + 1]->isWhitespace()) {
|
||||
$newLinesToRemove = $lineBreakCount - $expectedLineCount;
|
||||
$tokens[$headerIndex + 1] = new Token([
|
||||
T_WHITESPACE,
|
||||
Preg::replace("/^\\R{{$newLinesToRemove}}/", '', $tokens[$headerIndex + 1]->getContent()),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// fix lines before header comment
|
||||
$expectedLineCount = 'both' === $this->configuration['separate'] || 'top' === $this->configuration['separate'] ? 2 : 1;
|
||||
$prev = $tokens->getPrevNonWhitespace($headerIndex);
|
||||
|
||||
$regex = '/\h$/';
|
||||
|
||||
if ($tokens[$prev]->isGivenKind(T_OPEN_TAG) && Preg::match($regex, $tokens[$prev]->getContent())) {
|
||||
$tokens[$prev] = new Token([T_OPEN_TAG, Preg::replace($regex, $lineEnding, $tokens[$prev]->getContent())]);
|
||||
}
|
||||
|
||||
$lineBreakCount = $this->getLineBreakCount($tokens, $headerIndex, -1);
|
||||
|
||||
if ($lineBreakCount < $expectedLineCount) {
|
||||
// because of the way the insert index was determined for header comment there cannot be an empty token here
|
||||
$tokens->insertAt($headerIndex, new Token([T_WHITESPACE, str_repeat($lineEnding, $expectedLineCount - $lineBreakCount)]));
|
||||
}
|
||||
}
|
||||
|
||||
private function getLineBreakCount(Tokens $tokens, int $index, int $direction): int
|
||||
{
|
||||
$whitespace = '';
|
||||
|
||||
for ($index += $direction; isset($tokens[$index]); $index += $direction) {
|
||||
$token = $tokens[$index];
|
||||
|
||||
if ($token->isWhitespace()) {
|
||||
$whitespace .= $token->getContent();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (-1 === $direction && $token->isGivenKind(T_OPEN_TAG)) {
|
||||
$whitespace .= $token->getContent();
|
||||
}
|
||||
|
||||
if ('' !== $token->getContent()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return substr_count($whitespace, "\n");
|
||||
}
|
||||
|
||||
private function removeHeader(Tokens $tokens, int $index): void
|
||||
{
|
||||
$prevIndex = $index - 1;
|
||||
$prevToken = $tokens[$prevIndex];
|
||||
$newlineRemoved = false;
|
||||
|
||||
if ($prevToken->isWhitespace()) {
|
||||
$content = $prevToken->getContent();
|
||||
|
||||
if (Preg::match('/\R/', $content)) {
|
||||
$newlineRemoved = true;
|
||||
}
|
||||
|
||||
$content = Preg::replace('/\R?\h*$/', '', $content);
|
||||
|
||||
$tokens->ensureWhitespaceAtIndex($prevIndex, 0, $content);
|
||||
}
|
||||
|
||||
$nextIndex = $index + 1;
|
||||
$nextToken = $tokens[$nextIndex] ?? null;
|
||||
|
||||
if (!$newlineRemoved && null !== $nextToken && $nextToken->isWhitespace()) {
|
||||
$content = Preg::replace('/^\R/', '', $nextToken->getContent());
|
||||
|
||||
$tokens->ensureWhitespaceAtIndex($nextIndex, 0, $content);
|
||||
}
|
||||
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($index);
|
||||
}
|
||||
|
||||
private function insertHeader(Tokens $tokens, int $index): void
|
||||
{
|
||||
$tokens->insertAt($index, new Token([self::HEADER_COMMENT === $this->configuration['comment_type'] ? T_COMMENT : T_DOC_COMMENT, $this->getHeaderAsComment()]));
|
||||
$this->fixWhiteSpaceAroundHeader($tokens, $index);
|
||||
}
|
||||
}
|
||||
Vendored
+98
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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\Comment;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Preg;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
/**
|
||||
* @author Filippo Tessarotto <zoeslam@gmail.com>
|
||||
*/
|
||||
final class MultilineCommentOpeningClosingFixer extends AbstractFixer
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'DocBlocks must start with two asterisks, multiline comments must start with a single asterisk, after the opening slash. Both must end with a single asterisk before the closing slash.',
|
||||
[
|
||||
new CodeSample(
|
||||
<<<'EOT'
|
||||
<?php
|
||||
|
||||
/******
|
||||
* Multiline comment with arbitrary asterisks count
|
||||
******/
|
||||
|
||||
/**\
|
||||
* Multiline comment that seems a DocBlock
|
||||
*/
|
||||
|
||||
/**
|
||||
* DocBlock with arbitrary asterisk count at the end
|
||||
**/
|
||||
|
||||
EOT
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isAnyTokenKindsFound([T_COMMENT, T_DOC_COMMENT]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
foreach ($tokens as $index => $token) {
|
||||
$originalContent = $token->getContent();
|
||||
|
||||
if (
|
||||
!$token->isGivenKind(T_DOC_COMMENT)
|
||||
&& !($token->isGivenKind(T_COMMENT) && str_starts_with($originalContent, '/*'))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$newContent = $originalContent;
|
||||
|
||||
// Fix opening
|
||||
if ($token->isGivenKind(T_COMMENT)) {
|
||||
$newContent = Preg::replace('/^\\/\\*{2,}(?!\\/)/', '/*', $newContent);
|
||||
}
|
||||
|
||||
// Fix closing
|
||||
$newContent = Preg::replace('/(?<!\\/)\\*{2,}\\/$/', '*/', $newContent);
|
||||
|
||||
if ($newContent !== $originalContent) {
|
||||
$tokens[$index] = new Token([$token->getId(), $newContent]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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\Comment;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Preg;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
final class NoEmptyCommentFixer extends AbstractFixer
|
||||
{
|
||||
private const TYPE_HASH = 1;
|
||||
|
||||
private const TYPE_DOUBLE_SLASH = 2;
|
||||
|
||||
private const TYPE_SLASH_ASTERISK = 3;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run before NoExtraBlankLinesFixer, NoTrailingWhitespaceFixer, NoWhitespaceInBlankLineFixer.
|
||||
* Must run after PhpdocToCommentFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'There should not be any empty comments.',
|
||||
[new CodeSample("<?php\n//\n#\n/* */\n")]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isTokenKindFound(T_COMMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
for ($index = 1, $count = \count($tokens); $index < $count; ++$index) {
|
||||
if (!$tokens[$index]->isGivenKind(T_COMMENT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[$blockStart, $index, $isEmpty] = $this->getCommentBlock($tokens, $index);
|
||||
if (false === $isEmpty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for ($i = $blockStart; $i <= $index; ++$i) {
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the start index, end index and a flag stating if the comment block is empty.
|
||||
*
|
||||
* @param int $index T_COMMENT index
|
||||
*/
|
||||
private function getCommentBlock(Tokens $tokens, int $index): array
|
||||
{
|
||||
$commentType = $this->getCommentType($tokens[$index]->getContent());
|
||||
$empty = $this->isEmptyComment($tokens[$index]->getContent());
|
||||
|
||||
if (self::TYPE_SLASH_ASTERISK === $commentType) {
|
||||
return [$index, $index, $empty];
|
||||
}
|
||||
|
||||
$start = $index;
|
||||
$count = \count($tokens);
|
||||
++$index;
|
||||
|
||||
for (; $index < $count; ++$index) {
|
||||
if ($tokens[$index]->isComment()) {
|
||||
if ($commentType !== $this->getCommentType($tokens[$index]->getContent())) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($empty) { // don't retest if already known the block not being empty
|
||||
$empty = $this->isEmptyComment($tokens[$index]->getContent());
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$tokens[$index]->isWhitespace() || $this->getLineBreakCount($tokens, $index, $index + 1) > 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return [$start, $index - 1, $empty];
|
||||
}
|
||||
|
||||
private function getCommentType(string $content): int
|
||||
{
|
||||
if (str_starts_with($content, '#')) {
|
||||
return self::TYPE_HASH;
|
||||
}
|
||||
|
||||
if ('*' === $content[1]) {
|
||||
return self::TYPE_SLASH_ASTERISK;
|
||||
}
|
||||
|
||||
return self::TYPE_DOUBLE_SLASH;
|
||||
}
|
||||
|
||||
private function getLineBreakCount(Tokens $tokens, int $whiteStart, int $whiteEnd): int
|
||||
{
|
||||
$lineCount = 0;
|
||||
for ($i = $whiteStart; $i < $whiteEnd; ++$i) {
|
||||
$lineCount += Preg::matchAll('/\R/u', $tokens[$i]->getContent(), $matches);
|
||||
}
|
||||
|
||||
return $lineCount;
|
||||
}
|
||||
|
||||
private function isEmptyComment(string $content): bool
|
||||
{
|
||||
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_DOUBLE_SLASH => '|^//\s*$|', // single line comment starting with '//'
|
||||
];
|
||||
|
||||
$type = $this->getCommentType($content);
|
||||
|
||||
return 1 === Preg::match($mapper[$type], $content);
|
||||
}
|
||||
}
|
||||
Vendored
+84
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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\Comment;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Preg;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
/**
|
||||
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*/
|
||||
final class NoTrailingWhitespaceInCommentFixer extends AbstractFixer
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'There MUST be no trailing spaces inside comment or PHPDoc.',
|
||||
[new CodeSample('<?php
|
||||
// This is '.'
|
||||
// a comment. '.'
|
||||
')]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run after PhpdocNoUselessInheritdocFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isAnyTokenKindsFound([T_COMMENT, T_DOC_COMMENT]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
foreach ($tokens as $index => $token) {
|
||||
if ($token->isGivenKind(T_DOC_COMMENT)) {
|
||||
$tokens[$index] = new Token([T_DOC_COMMENT, Preg::replace('/(*ANY)[\h]+$/m', '', $token->getContent())]);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token->isGivenKind(T_COMMENT)) {
|
||||
if (str_starts_with($token->getContent(), '/*')) {
|
||||
$tokens[$index] = new Token([T_COMMENT, Preg::replace('/(*ANY)[\h]+$/m', '', $token->getContent())]);
|
||||
} elseif (isset($tokens[$index + 1]) && $tokens[$index + 1]->isWhitespace()) {
|
||||
$trimmedContent = ltrim($tokens[$index + 1]->getContent(), " \t");
|
||||
$tokens->ensureWhitespaceAtIndex($index + 1, 0, $trimmedContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+119
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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\Comment;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
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 SingleLineCommentSpacingFixer extends AbstractFixer
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Single-line comments must have proper spacing.',
|
||||
[
|
||||
new CodeSample(
|
||||
'<?php
|
||||
//comment 1
|
||||
#comment 2
|
||||
/*comment 3*/
|
||||
'
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run after PhpdocToCommentFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isTokenKindFound(T_COMMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
for ($index = \count($tokens) - 1; 0 <= $index; --$index) {
|
||||
$token = $tokens[$index];
|
||||
|
||||
if (!$token->isGivenKind(T_COMMENT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$content = $token->getContent();
|
||||
$contentLength = \strlen($content);
|
||||
|
||||
if ('/' === $content[0]) {
|
||||
if ($contentLength < 3) {
|
||||
continue; // cheap check for "//"
|
||||
}
|
||||
|
||||
if ('*' === $content[1]) { // slash asterisk comment
|
||||
if ($contentLength < 5 || '*' === $content[2] || str_contains($content, "\n")) {
|
||||
continue; // cheap check for "/**/", comment that looks like a PHPDoc, or multi line comment
|
||||
}
|
||||
|
||||
$newContent = rtrim(substr($content, 0, -2)).' '.substr($content, -2);
|
||||
$newContent = $this->fixCommentLeadingSpace($newContent, '/*');
|
||||
} else { // double slash comment
|
||||
$newContent = $this->fixCommentLeadingSpace($content, '//');
|
||||
}
|
||||
} else { // hash comment
|
||||
if ($contentLength < 2 || '[' === $content[1]) { // cheap check for "#" or annotation (like) comment
|
||||
continue;
|
||||
}
|
||||
|
||||
$newContent = $this->fixCommentLeadingSpace($content, '#');
|
||||
}
|
||||
|
||||
if ($newContent !== $content) {
|
||||
$tokens[$index] = new Token([T_COMMENT, $newContent]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fix space between comment open and leading text
|
||||
private function fixCommentLeadingSpace(string $content, string $prefix): string
|
||||
{
|
||||
if (0 !== Preg::match(sprintf('@^%s\h+.*$@', preg_quote($prefix, '@')), $content)) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$position = \strlen($prefix);
|
||||
|
||||
return substr($content, 0, $position).' '.substr($content, $position);
|
||||
}
|
||||
}
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* 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\Comment;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
|
||||
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
|
||||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
|
||||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
|
||||
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Preg;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
/**
|
||||
* @author Filippo Tessarotto <zoeslam@gmail.com>
|
||||
*/
|
||||
final class SingleLineCommentStyleFixer extends AbstractFixer implements ConfigurableFixerInterface
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $asteriskEnabled;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $hashEnabled;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
parent::configure($configuration);
|
||||
|
||||
$this->asteriskEnabled = \in_array('asterisk', $this->configuration['comment_types'], true);
|
||||
$this->hashEnabled = \in_array('hash', $this->configuration['comment_types'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Single-line comments and multi-line comments with only one line of actual content should use the `//` syntax.',
|
||||
[
|
||||
new CodeSample(
|
||||
'<?php
|
||||
/* asterisk comment */
|
||||
$a = 1;
|
||||
|
||||
# hash comment
|
||||
$b = 2;
|
||||
|
||||
/*
|
||||
* multi-line
|
||||
* comment
|
||||
*/
|
||||
$c = 3;
|
||||
'
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php
|
||||
/* first comment */
|
||||
$a = 1;
|
||||
|
||||
/*
|
||||
* second comment
|
||||
*/
|
||||
$b = 2;
|
||||
|
||||
/*
|
||||
* third
|
||||
* comment
|
||||
*/
|
||||
$c = 3;
|
||||
',
|
||||
['comment_types' => ['asterisk']]
|
||||
),
|
||||
new CodeSample(
|
||||
"<?php # comment\n",
|
||||
['comment_types' => ['hash']]
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run after HeaderCommentFixer, NoUselessReturnFixer, PhpdocToCommentFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return -31;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isTokenKindFound(T_COMMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
foreach ($tokens as $index => $token) {
|
||||
if (!$token->isGivenKind(T_COMMENT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$content = $token->getContent();
|
||||
$commentContent = substr($content, 2, -2) ?: '';
|
||||
|
||||
if ($this->hashEnabled && str_starts_with($content, '#')) {
|
||||
if (isset($content[1]) && '[' === $content[1]) {
|
||||
continue; // This might be an attribute on PHP8, do not change
|
||||
}
|
||||
|
||||
$tokens[$index] = new Token([$token->getId(), '//'.substr($content, 1)]);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
!$this->asteriskEnabled
|
||||
|| str_contains($commentContent, '?>')
|
||||
|| !str_starts_with($content, '/*')
|
||||
|| 1 === Preg::match('/[^\s\*].*\R.*[^\s\*]/s', $commentContent)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$nextTokenIndex = $index + 1;
|
||||
if (isset($tokens[$nextTokenIndex])) {
|
||||
$nextToken = $tokens[$nextTokenIndex];
|
||||
if (!$nextToken->isWhitespace() || 1 !== Preg::match('/\R/', $nextToken->getContent())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tokens[$nextTokenIndex] = new Token([$nextToken->getId(), ltrim($nextToken->getContent(), " \t")]);
|
||||
}
|
||||
|
||||
$content = '//';
|
||||
if (1 === Preg::match('/[^\s\*]/', $commentContent)) {
|
||||
$content = '// '.Preg::replace('/[\s\*]*([^\s\*](?:.+[^\s\*])?)[\s\*]*/', '\1', $commentContent);
|
||||
}
|
||||
$tokens[$index] = new Token([$token->getId(), $content]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
|
||||
{
|
||||
return new FixerConfigurationResolver([
|
||||
(new FixerOptionBuilder('comment_types', 'List of comment types to fix'))
|
||||
->setAllowedTypes(['array'])
|
||||
->setAllowedValues([new AllowedValueSubset(['asterisk', 'hash'])])
|
||||
->setDefault(['asterisk', 'hash'])
|
||||
->getOption(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user