Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 104 additions & 67 deletions .github/phpcs/SectionComments.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,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;

Expand Down Expand Up @@ -99,10 +100,10 @@ public function __construct()
];

foreach ($this->comments as $type => $string) {
$regexes[$type] = preg_replace('/\s+/', '\s+', preg_quote($string, '/'));
$words[] = trim($string, "/* \n\t");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$words[] = trim($string, "/* \n\t");
$words[] = preg_replace('/\s+/', '\s+', preg_quote(trim($string, "/* \n\t"), '/'));

This needs to keep the part where we replace literal whitespace with \s+. Otherwise this supposedly more forgiving regex will fail if, for example, the comment's indentation changed.

}

$this->comment_regex = implode('|', $regexes);
$this->comment_regex = '/^\/[*\s]+(?:' . implode('|', $words) . ')[*\s]+\/$/';
}

public function getName(): string
Expand Down Expand Up @@ -207,32 +208,16 @@ public function isCandidate(Tokens $tokens): bool

protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
{
// First remove any existing section comments.
$existing = [];

foreach ($tokens as $key => $token) {
if ($token->getName() === 'T_COMMENT') {
if (preg_match('/^' . $this->comment_regex . '$/', $token->getContent())) {
$tokens->clearAt($key);

if ($tokens[$key + 1]->isWhitespace()) {
$tokens[$key + 1] = new Token([
T_WHITESPACE,
"\n\n\t",
]);
} else {
$tokens->insertAt(
$key + 1,
new Token([
T_WHITESPACE,
"\n\n\t",
]),
);
}
if ($token->getId() === T_COMMENT) {
if (preg_match($this->comment_regex, $token->getContent())) {
$existing[$key] = true;
}
}
}

$tokens->clearEmptyTokens();

// Does this file contain an enumeration?
$is_enum = $tokens->isAnyTokenKindsFound([T_ENUM]);

Expand All @@ -254,57 +239,58 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
$in = [];

foreach ($tokens as $key => $token) {
$name = $token->getName();
$id = $token->getId();

// Build up the list of token types so that we can figure out
// which comment type we will want.
if (\in_array(
$name,
empty($in) ? [
'T_PUBLIC',
'T_PROTECTED',
'T_PRIVATE',
$is_enum && !$exists['case'] ? 'T_CASE' : NAN,
$id,
$in === [] ? [
T_PUBLIC,
T_PROTECTED,
T_PRIVATE,
$is_enum && !$exists['case'] ? T_CASE : -1,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$is_enum && !$exists['case'] ? T_CASE : -1,
$is_enum && !$exists['case'] ? T_CASE : NAN,

NAN was an intentional choice here, because it is never equal to anything (not even itself).

] : [
'T_CONST',
'T_STATIC',
'T_VARIABLE',
'T_FUNCTION',
T_CONST,
T_STATIC,
T_VARIABLE,
T_FUNCTION,
],
true,
)) {
$in[$name] = $key;
$in[$id] = $key;
}

// Which comment type do we want to insert?
if (\array_key_exists('T_CONST', $in)) {
if (isset($in[T_CONST])) {
$insert_type = 'const';
} elseif ($is_enum && !$exists['case'] && \array_key_exists('T_CASE', $in)) {
} elseif ($is_enum && !$exists['case'] && isset($in[T_CASE])) {
$insert_type = 'case';
} elseif (\array_key_exists('T_VARIABLE', $in)) {
if (\array_key_exists('T_STATIC', $in)) {
if (\array_key_exists('T_PUBLIC', $in)) {
} elseif (isset($in[T_VARIABLE])) {
if (isset($in[T_STATIC])) {
if (isset($in[T_PUBLIC])) {
$insert_type = 'public_static_property';
} elseif (\array_key_exists('T_PROTECTED', $in) || \array_key_exists('T_PRIVATE', $in)) {
} elseif (isset($in[T_PROTECTED]) || isset($in[T_PRIVATE])) {
$insert_type = 'internal_static_property';
}
} else {
if (\array_key_exists('T_PUBLIC', $in)) {
if (isset($in[T_PUBLIC])) {
$insert_type = 'public_property';
} elseif (\array_key_exists('T_PROTECTED', $in) || \array_key_exists('T_PRIVATE', $in)) {
} elseif (isset($in[T_PROTECTED]) || isset($in[T_PRIVATE])) {
$insert_type = 'internal_property';
}
}
} elseif (\array_key_exists('T_FUNCTION', $in)) {
if (\array_key_exists('T_STATIC', $in)) {
if (\array_key_exists('T_PUBLIC', $in)) {
} elseif (isset($in[T_FUNCTION])) {
if (isset($in[T_STATIC])) {
if (isset($in[T_PUBLIC])) {
$insert_type = 'public_static_method';
} elseif (\array_key_exists('T_PROTECTED', $in) || \array_key_exists('T_PRIVATE', $in)) {
} elseif (isset($in[T_PROTECTED]) || isset($in[T_PRIVATE])) {
$insert_type = 'internal_static_method';
}
} else {
if (\array_key_exists('T_PUBLIC', $in)) {
if (isset($in[T_PUBLIC])) {
$insert_type = 'public_method';
} elseif (\array_key_exists('T_PROTECTED', $in) || \array_key_exists('T_PRIVATE', $in)) {
} elseif (isset($in[T_PROTECTED]) || isset($in[T_PRIVATE])) {
$insert_type = 'internal_method';
}
}
Expand Down Expand Up @@ -332,34 +318,85 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
// Now we need to take one step forward again.
$insert_at++;

// Create the comment to insert.
$to_insert = [
new Token([
T_COMMENT,
$this->comments[$insert_type],
]),
];

// If necessary, also insert some whitespace.
if (!$tokens[$insert_at]->isWhitespace()) {
$to_insert[] = new Token([
T_WHITESPACE,
"\n\n\t",
]);
// Rewind to the first attribute in an attribute group.
while ($tokens[$prev_index = $tokens->getPrevMeaningfulToken($insert_at)]->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) {
$insert_at = $tokens->findBlockStart(Tokens::BLOCK_TYPE_ATTRIBUTE, $prev_index);

while (
isset($tokens[$insert_at - 1])
&& ($tokens[$insert_at - 1]->isWhitespace() || $tokens[$insert_at - 1]->isComment())
) {
$insert_at--;
}

// Now we need to take one step forward again.
$insert_at++;
}

// Insert our comment.
$slices[$insert_at] = $to_insert;
if ($tokens[$insert_at]->getContent() !== $this->comments[$insert_type]) {
// Create the comment to insert.
$to_insert = [
new Token([
T_COMMENT,
$this->comments[$insert_type],
]),
];

// If necessary, also insert some whitespace.
if (!$tokens[$insert_at]->isWhitespace()) {
$to_insert[] = new Token([
T_WHITESPACE,
"\n\n\t",
]);
}

// This comment type has now been done.
$exists[$insert_type] = true;
// Insert our comment.
$slices[$insert_at] = $to_insert;

// This comment type has now been done.
$exists[$insert_type] = true;
} else {
// Normalize whitespace.
if ($tokens[$insert_at - 1]->isWhitespace()) {
$prev = $tokens->getPrevMeaningfulToken($insert_at);

$tokens[$insert_at - 1] = new Token([
T_WHITESPACE,
$tokens[$prev]->equals('{') ? "\n\t" : "\n\n\t",
]);
}

if ($tokens[$insert_at + 1]->isWhitespace()) {
$tokens[$insert_at + 1] = new Token([
T_WHITESPACE,
"\n\n\t",
]);
}

$exists[$insert_type] = true;

// Do not remove this token.
if (isset($existing[$insert_at])) {
unset($existing[$insert_at]);
}
}
}

$in = [];
unset($insert_type);
}
}

// Any existing tokens to empty out?
foreach ($existing as $key => $_) {
$tokens->clearAt($key);

// If necessary, also delete whitespace.
if ($tokens[$key + 1]->isWhitespace()) {
$tokens->clearAt($key + 1);
}
}

// Insert comments.
if ($slices !== []) {
$tokens->insertSlices($slices);
Expand Down
Loading