mirror of
https://github.com/elyby/php-code-style.git
synced 2025-05-31 14:12:05 +05:30
Resolves #16. Split repository and move all custom fixers to separate repository
This commit is contained in:
@@ -3,17 +3,17 @@ declare(strict_types=1);
|
||||
|
||||
namespace Ely\CS;
|
||||
|
||||
use Ely\CS\Fixers as ElyFixers;
|
||||
use ErickSkrauch\PhpCsFixer\Fixers as ErickSkrauchFixers;
|
||||
use PhpCsFixer\Config as PhpCsFixerConfig;
|
||||
use PhpCsFixer\ConfigInterface as PhpCsFixerConfigInterface;
|
||||
use PhpCsFixerCustomFixers\Fixers as KubawerlosFixers;
|
||||
|
||||
class Config {
|
||||
final class Config {
|
||||
|
||||
public static function create(array $overwrittenRules = []): PhpCsFixerConfigInterface {
|
||||
return (new PhpCsFixerConfig())
|
||||
->setRiskyAllowed(true)
|
||||
->registerCustomFixers(new ElyFixers())
|
||||
->registerCustomFixers(new ErickSkrauchFixers())
|
||||
->registerCustomFixers(new KubawerlosFixers())
|
||||
->setRules(Rules::create($overwrittenRules));
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\CS\Fixer;
|
||||
|
||||
abstract class AbstractFixer extends \PhpCsFixer\AbstractFixer {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName(): string {
|
||||
return sprintf('Ely/%s', parent::getName());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\CS\Fixer\FunctionNotation;
|
||||
|
||||
use Ely\CS\Fixer\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\Tokenizer\Analyzer\FunctionsAnalyzer;
|
||||
use PhpCsFixer\Tokenizer\Analyzer\WhitespacesAnalyzer;
|
||||
use PhpCsFixer\Tokenizer\CT;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
use PhpCsFixer\Tokenizer\TokensAnalyzer;
|
||||
use SplFileInfo;
|
||||
|
||||
final class AlignMultilineParametersFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface {
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public const C_VARIABLES = 'variables';
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public const C_DEFAULTS = 'defaults';
|
||||
|
||||
private array $parameterModifiers;
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
$this->parameterModifiers = [
|
||||
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC,
|
||||
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED,
|
||||
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE,
|
||||
];
|
||||
if (defined('T_READONLY')) {
|
||||
$this->parameterModifiers[] = T_READONLY;
|
||||
}
|
||||
}
|
||||
|
||||
public function getDefinition(): FixerDefinitionInterface {
|
||||
return new FixerDefinition(
|
||||
'Aligns parameters in multiline function declaration.',
|
||||
[
|
||||
new CodeSample(
|
||||
'<?php
|
||||
function test(
|
||||
string $a,
|
||||
int $b = 0
|
||||
): void {};
|
||||
',
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php
|
||||
function test(
|
||||
string $string,
|
||||
int $int = 0
|
||||
): void {};
|
||||
',
|
||||
[self::C_VARIABLES => false, self::C_DEFAULTS => false],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public function isCandidate(Tokens $tokens): bool {
|
||||
return $tokens->isAnyTokenKindsFound([T_FUNCTION, T_FN]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Must run after StatementIndentationFixer, MethodArgumentSpaceFixer
|
||||
*/
|
||||
public function getPriority(): int {
|
||||
return -10;
|
||||
}
|
||||
|
||||
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface {
|
||||
return new FixerConfigurationResolver([
|
||||
(new FixerOptionBuilder(self::C_VARIABLES, 'on null no value alignment, on bool forces alignment'))
|
||||
->setAllowedTypes(['bool', 'null'])
|
||||
->setDefault(true)
|
||||
->getOption(),
|
||||
(new FixerOptionBuilder(self::C_DEFAULTS, 'on null no value alignment, on bool forces alignment'))
|
||||
->setAllowedTypes(['bool', 'null'])
|
||||
->setDefault(null)
|
||||
->getOption(),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function applyFix(SplFileInfo $file, Tokens $tokens): void {
|
||||
// There is nothing to do
|
||||
if ($this->configuration[self::C_VARIABLES] === null && $this->configuration[self::C_DEFAULTS] === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tokensAnalyzer = new TokensAnalyzer($tokens);
|
||||
$functionsAnalyzer = new FunctionsAnalyzer();
|
||||
/** @var \PhpCsFixer\Tokenizer\Token $functionToken */
|
||||
foreach ($tokens as $i => $functionToken) {
|
||||
if (!$functionToken->isGivenKind([T_FUNCTION, T_FN])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$openBraceIndex = $tokens->getNextTokenOfKind($i, ['(']);
|
||||
$isMultiline = $tokensAnalyzer->isBlockMultiline($tokens, $openBraceIndex);
|
||||
if (!$isMultiline) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var \PhpCsFixer\Tokenizer\Analyzer\Analysis\ArgumentAnalysis[] $arguments */
|
||||
$arguments = $functionsAnalyzer->getFunctionArguments($tokens, $i);
|
||||
if (empty($arguments)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$longestType = 0;
|
||||
$longestVariableName = 0;
|
||||
$hasAtLeastOneTypedArgument = false;
|
||||
foreach ($arguments as $argument) {
|
||||
$typeAnalysis = $argument->getTypeAnalysis();
|
||||
if ($typeAnalysis) {
|
||||
$hasAtLeastOneTypedArgument = true;
|
||||
$typeLength = $this->getFullTypeLength($tokens, $typeAnalysis->getStartIndex());
|
||||
if ($typeLength > $longestType) {
|
||||
$longestType = $typeLength;
|
||||
}
|
||||
}
|
||||
|
||||
$variableNameLength = strlen($argument->getName());
|
||||
if ($variableNameLength > $longestVariableName) {
|
||||
$longestVariableName = $variableNameLength;
|
||||
}
|
||||
}
|
||||
|
||||
$argsIndent = WhitespacesAnalyzer::detectIndent($tokens, $i) . $this->whitespacesConfig->getIndent();
|
||||
foreach ($arguments as $argument) {
|
||||
if ($this->configuration[self::C_VARIABLES] !== null) {
|
||||
$whitespaceIndex = $argument->getNameIndex() - 1;
|
||||
if ($this->configuration[self::C_VARIABLES] === true) {
|
||||
$typeLen = 0;
|
||||
if ($argument->getTypeAnalysis() !== null) {
|
||||
$typeLen = $this->getFullTypeLength($tokens, $argument->getTypeAnalysis()->getStartIndex());
|
||||
}
|
||||
|
||||
$appendix = str_repeat(' ', $longestType - $typeLen + (int)$hasAtLeastOneTypedArgument);
|
||||
if ($argument->hasTypeAnalysis()) {
|
||||
$whitespaceToken = $appendix;
|
||||
} else {
|
||||
$whitespaceToken = $this->whitespacesConfig->getLineEnding() . $argsIndent . $appendix;
|
||||
}
|
||||
} else {
|
||||
if ($argument->hasTypeAnalysis()) {
|
||||
$whitespaceToken = ' ';
|
||||
} else {
|
||||
$whitespaceToken = $this->whitespacesConfig->getLineEnding() . $argsIndent;
|
||||
}
|
||||
}
|
||||
|
||||
$tokens->ensureWhitespaceAtIndex($whitespaceIndex, 0, $whitespaceToken);
|
||||
}
|
||||
|
||||
if ($this->configuration[self::C_DEFAULTS] !== null) {
|
||||
// Can't use $argument->hasDefault() because it's null when it's default for a type (e.g. 0 for int)
|
||||
/** @var \PhpCsFixer\Tokenizer\Token $equalToken */
|
||||
$equalToken = $tokens[$tokens->getNextMeaningfulToken($argument->getNameIndex())];
|
||||
if ($equalToken->getContent() === '=') {
|
||||
$nameLen = strlen($argument->getName());
|
||||
$whitespaceIndex = $argument->getNameIndex() + 1;
|
||||
if ($this->configuration[self::C_DEFAULTS] === true) {
|
||||
$tokens->ensureWhitespaceAtIndex($whitespaceIndex, 0, str_repeat(' ', $longestVariableName - $nameLen + 1));
|
||||
} else {
|
||||
$tokens->ensureWhitespaceAtIndex($whitespaceIndex, 0, ' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getFullTypeLength(Tokens $tokens, int $typeIndex): int {
|
||||
/** @var \PhpCsFixer\Tokenizer\Token $typeToken */
|
||||
$typeToken = $tokens[$typeIndex];
|
||||
$typeLength = strlen($typeToken->getContent());
|
||||
|
||||
/** @var \PhpCsFixer\Tokenizer\Token $possiblyReadonlyToken */
|
||||
$possiblyReadonlyToken = $tokens[$typeIndex - 2];
|
||||
if ($possiblyReadonlyToken->isGivenKind($this->parameterModifiers)) {
|
||||
/** @var \PhpCsFixer\Tokenizer\Token $whitespaceToken */
|
||||
$whitespaceToken = $tokens[$typeIndex - 1];
|
||||
$typeLength += strlen($possiblyReadonlyToken->getContent() . $whitespaceToken->getContent());
|
||||
}
|
||||
|
||||
/** @var \PhpCsFixer\Tokenizer\Token $possiblyPromotionToken */
|
||||
$possiblyPromotionToken = $tokens[$typeIndex - 4];
|
||||
if ($possiblyPromotionToken->isGivenKind($this->parameterModifiers)) {
|
||||
/** @var \PhpCsFixer\Tokenizer\Token $whitespaceToken */
|
||||
$whitespaceToken = $tokens[$typeIndex - 3];
|
||||
$typeLength += strlen($possiblyPromotionToken->getContent() . $whitespaceToken->getContent());
|
||||
}
|
||||
|
||||
return $typeLength;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\CS\Fixer\LanguageConstruct;
|
||||
|
||||
use Ely\CS\Fixer\AbstractFixer;
|
||||
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 SplFileInfo;
|
||||
|
||||
/**
|
||||
* Replaces Yii2 BaseObject::className() usages with native ::class keyword, introduced in PHP 5.5.
|
||||
*
|
||||
* @author ErickSkrauch <erickskrauch@ely.by>
|
||||
*/
|
||||
final class RemoveClassNameMethodUsagesFixer extends AbstractFixer {
|
||||
|
||||
public function getDefinition(): FixerDefinitionInterface {
|
||||
return new FixerDefinition(
|
||||
'Converts Yii2 `BaseObject::className()` method usage into `::class` keyword.',
|
||||
[
|
||||
new CodeSample(
|
||||
'<?php
|
||||
|
||||
use Foo\Bar\Baz;
|
||||
|
||||
$className = Baz::className();
|
||||
',
|
||||
),
|
||||
],
|
||||
null,
|
||||
'Risky when the method `className()` is overridden.',
|
||||
);
|
||||
}
|
||||
|
||||
public function isCandidate(Tokens $tokens): bool {
|
||||
return $tokens->isTokenKindFound(T_STRING);
|
||||
}
|
||||
|
||||
public function isRisky(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function applyFix(SplFileInfo $file, Tokens $tokens): void {
|
||||
for ($index = $tokens->count() - 4; $index > 0; --$index) {
|
||||
$candidate = $this->getReplaceCandidate($tokens, $index);
|
||||
if ($candidate === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->fixClassNameMethodUsage(
|
||||
$tokens,
|
||||
$index,
|
||||
$candidate[0], // brace open
|
||||
$candidate[1], // brace close
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function getReplaceCandidate(Tokens $tokens, int $index): ?array {
|
||||
if (!$tokens[$index]->isGivenKind(T_STRING)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$braceOpenIndex = $tokens->getNextMeaningfulToken($index);
|
||||
if (!$tokens[$braceOpenIndex]->equals('(')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$braceCloseIndex = $tokens->getNextMeaningfulToken($braceOpenIndex);
|
||||
if (!$tokens[$braceCloseIndex]->equals(')')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$doubleColon = $tokens->getPrevMeaningfulToken($index);
|
||||
if (!$tokens[$doubleColon]->isGivenKind([T_DOUBLE_COLON])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$methodName = $tokens[$index]->getContent();
|
||||
if ($methodName !== 'className') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
$braceOpenIndex,
|
||||
$braceCloseIndex,
|
||||
];
|
||||
}
|
||||
|
||||
private function fixClassNameMethodUsage(
|
||||
Tokens $tokens,
|
||||
int $index,
|
||||
int $braceOpenIndex,
|
||||
int $braceCloseIndex
|
||||
): void {
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($braceCloseIndex);
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($braceOpenIndex);
|
||||
$tokens->clearAt($index);
|
||||
$tokens->insertAt($index, new Token([CT::T_CLASS_CONSTANT, 'class']));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\CS\Fixer\Whitespace;
|
||||
|
||||
use Ely\CS\Fixer\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\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
use PhpCsFixer\Tokenizer\TokensAnalyzer;
|
||||
use SplFileInfo;
|
||||
|
||||
/**
|
||||
* This fixer conflicts with the CurlyBracesPositionFixer (which is part of the BracesFixer),
|
||||
* because CurlyBracesPositionFixer always tries to remove any new lines between class beginning
|
||||
* and the first meaningful statement. And then this fixer restores those spaces back.
|
||||
*
|
||||
* That is the reason, why you always see a "braces, Ely/blank_line_around_class_body" in verbose output.
|
||||
*/
|
||||
final class BlankLineAroundClassBodyFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface {
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public const C_BLANK_LINES_COUNT = 'blank_lines_count';
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public const C_APPLY_TO_ANONYMOUS_CLASSES = 'apply_to_anonymous_classes';
|
||||
|
||||
public function getDefinition(): FixerDefinitionInterface {
|
||||
return new FixerDefinition(
|
||||
'Ensure that class body contains one blank line after class definition and before its end.',
|
||||
[
|
||||
new CodeSample(
|
||||
'<?php
|
||||
class Sample
|
||||
{
|
||||
protected function foo()
|
||||
{
|
||||
}
|
||||
}
|
||||
',
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php
|
||||
new class extends Foo {
|
||||
|
||||
protected function foo()
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
',
|
||||
[self::C_APPLY_TO_ANONYMOUS_CLASSES => false],
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php
|
||||
new class extends Foo {
|
||||
protected function foo()
|
||||
{
|
||||
}
|
||||
};
|
||||
',
|
||||
[self::C_APPLY_TO_ANONYMOUS_CLASSES => true],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public function isCandidate(Tokens $tokens): bool {
|
||||
return $tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds());
|
||||
}
|
||||
|
||||
protected function applyFix(SplFileInfo $file, Tokens $tokens): void {
|
||||
$analyzer = new TokensAnalyzer($tokens);
|
||||
/** @var Token $token */
|
||||
foreach ($tokens as $index => $token) {
|
||||
if (!$token->isClassy()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$countLines = $this->configuration[self::C_BLANK_LINES_COUNT];
|
||||
if (!$this->configuration[self::C_APPLY_TO_ANONYMOUS_CLASSES] && $analyzer->isAnonymousClass($index)) {
|
||||
$countLines = 0;
|
||||
}
|
||||
|
||||
$startBraceIndex = $tokens->getNextTokenOfKind($index, ['{']);
|
||||
/** @var Token $nextAfterBraceToken */
|
||||
$nextAfterBraceToken = $tokens[$startBraceIndex + 1];
|
||||
if ($nextAfterBraceToken->isWhitespace()) {
|
||||
$nextStatementIndex = $tokens->getNextMeaningfulToken($startBraceIndex);
|
||||
/** @var Token $nextStatementToken */
|
||||
$nextStatementToken = $tokens[$nextStatementIndex];
|
||||
// Traits should be placed right after a class opening brace
|
||||
if ($nextStatementToken->getContent() !== 'use') {
|
||||
$this->ensureBlankLines($tokens, $startBraceIndex + 1, $countLines);
|
||||
}
|
||||
}
|
||||
|
||||
$endBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $startBraceIndex);
|
||||
if ($tokens[$endBraceIndex - 1]->isWhitespace()) {
|
||||
$this->ensureBlankLines($tokens, $endBraceIndex - 1, $countLines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface {
|
||||
return new FixerConfigurationResolver([
|
||||
(new FixerOptionBuilder(self::C_BLANK_LINES_COUNT, 'adjusts the number of blank lines.'))
|
||||
->setAllowedTypes(['int'])
|
||||
->setDefault(1)
|
||||
->getOption(),
|
||||
(new FixerOptionBuilder(self::C_APPLY_TO_ANONYMOUS_CLASSES, 'whether this fixer should be applied to anonymous classes.'))
|
||||
->setAllowedTypes(['bool'])
|
||||
->setDefault(true)
|
||||
->getOption(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function ensureBlankLines(Tokens $tokens, int $index, int $countLines): void {
|
||||
$content = $tokens[$index]->getContent();
|
||||
// Apply fix only when the lines count doesn't equal to expected
|
||||
// Don't check for \r\n sequence since it's still contains \n part
|
||||
if (substr_count($content, "\n") === $countLines + 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use regexp to extract contents between line breaks
|
||||
Preg::matchAll('/[^\n\r]+[\r\n]*/', $content, $matches);
|
||||
$lines = $matches[0];
|
||||
$eol = $this->whitespacesConfig->getLineEnding();
|
||||
$tokens->ensureWhitespaceAtIndex($index, 0, str_repeat($eol, $countLines + 1) . end($lines));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\CS\Fixer\Whitespace;
|
||||
|
||||
use Ely\CS\Fixer\AbstractFixer;
|
||||
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
use SplFileInfo;
|
||||
|
||||
/**
|
||||
* This is extended version of the original `blank_line_before_statement` fixer.
|
||||
* It applies only to `return` statements and only in cases, when on the current nesting level more than one statements.
|
||||
*
|
||||
* @url https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/5c5de791ab/src/Fixer/Whitespace/BlankLineBeforeStatementFixer.php
|
||||
*
|
||||
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
* @author Andreas Möller <am@localheinz.com>
|
||||
* @author SpacePossum
|
||||
*/
|
||||
final class BlankLineBeforeReturnFixer extends AbstractFixer implements WhitespacesAwareFixerInterface {
|
||||
|
||||
public function getDefinition(): FixerDefinitionInterface {
|
||||
return new FixerDefinition(
|
||||
'An empty line feed should precede a return statement.',
|
||||
[new CodeSample("<?php\nfunction A()\n{\n echo 1;\n echo 2;\n return 1;\n}\n")],
|
||||
);
|
||||
}
|
||||
|
||||
public function isCandidate(Tokens $tokens): bool {
|
||||
return $tokens->isTokenKindFound(T_RETURN);
|
||||
}
|
||||
|
||||
public function getPriority(): int {
|
||||
// should be run after NoUselessReturnFixer, ClassDefinitionFixer and BracesFixer
|
||||
return -26;
|
||||
}
|
||||
|
||||
protected function applyFix(SplFileInfo $file, Tokens $tokens): void {
|
||||
for ($index = 0, $limit = $tokens->count(); $index < $limit; ++$index) {
|
||||
$token = $tokens[$index];
|
||||
if (!$token->isGivenKind(T_RETURN)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$eol = $this->whitespacesConfig->getLineEnding();
|
||||
|
||||
$prevNonWhitespaceToken = $tokens[$tokens->getPrevNonWhitespace($index)];
|
||||
if (!$prevNonWhitespaceToken->equalsAny([';', '}'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$prevIndex = $index - 1;
|
||||
$prevToken = $tokens[$prevIndex];
|
||||
if ($prevToken->isWhitespace()) {
|
||||
$countParts = substr_count($prevToken->getContent(), "\n");
|
||||
if ($countParts === 0) {
|
||||
$tokens[$prevIndex] = new Token([T_WHITESPACE, rtrim($prevToken->getContent(), " \t") . $eol . $eol]);
|
||||
} elseif ($countParts === 1) {
|
||||
$backwardIndex = $prevIndex;
|
||||
do {
|
||||
if (--$backwardIndex < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/** @var Token $backwardToken */
|
||||
$backwardToken = $tokens[$backwardIndex];
|
||||
/** @var Token $nextToken */
|
||||
$nextToken = $tokens[$backwardIndex + 1];
|
||||
// Exclude string interpolation: "str {$var}"
|
||||
if ($backwardToken->getContent() === '{' && !$nextToken->isGivenKind(T_VARIABLE)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($backwardToken->isWhitespace()) {
|
||||
$countParts += substr_count($backwardToken->getContent(), "\n");
|
||||
}
|
||||
} while ($countParts < 3);
|
||||
|
||||
if ($countParts !== 2) {
|
||||
$tokens[$prevIndex] = new Token([T_WHITESPACE, $eol . $prevToken->getContent()]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$tokens->insertSlices([$index => new Token([T_WHITESPACE, $eol . $eol])]);
|
||||
++$index;
|
||||
++$limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\CS\Fixer\Whitespace;
|
||||
|
||||
use Ely\CS\Fixer\AbstractFixer;
|
||||
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Preg;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
use SplFileInfo;
|
||||
|
||||
/**
|
||||
* This is rewritten version of the original fixer created by @PedroTroller with improved cases validation and
|
||||
* targeted to the PHP-CS-Fixer 2.11 version.
|
||||
*
|
||||
* @url https://github.com/PedroTroller/PhpCSFixer-Custom-Fixers/blob/affdf99f51/src/PedroTroller/CS/Fixer/CodingStyle/LineBreakBetweenStatementsFixer.php
|
||||
*
|
||||
* @author ErickSkrauch <erickskrauch@ely.by>
|
||||
*/
|
||||
final class LineBreakAfterStatementsFixer extends AbstractFixer implements WhitespacesAwareFixerInterface {
|
||||
|
||||
/**
|
||||
* There is no 'do', 'cause the processing of the 'while' also includes do {} while (); construction
|
||||
*/
|
||||
private const STATEMENTS = [
|
||||
T_IF,
|
||||
T_SWITCH,
|
||||
T_FOR,
|
||||
T_FOREACH,
|
||||
T_WHILE,
|
||||
];
|
||||
|
||||
public function getDefinition(): FixerDefinitionInterface {
|
||||
return new FixerDefinition(
|
||||
'Ensures that there is one blank line above the control statements.',
|
||||
[
|
||||
new CodeSample(
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
/**
|
||||
* @return null
|
||||
*/
|
||||
public function foo() {
|
||||
do {
|
||||
// ...
|
||||
} while (true);
|
||||
foreach (["foo", "bar"] as $str) {
|
||||
// ...
|
||||
}
|
||||
if (true === false) {
|
||||
// ...
|
||||
}
|
||||
foreach (["foo", "bar"] as $str) {
|
||||
if ($str === "foo") {
|
||||
// smth
|
||||
}
|
||||
|
||||
}
|
||||
while (true) {
|
||||
// ...
|
||||
}
|
||||
switch("123") {
|
||||
case "123":
|
||||
break;
|
||||
}
|
||||
$a = "next statement";
|
||||
}
|
||||
}
|
||||
',
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public function isCandidate(Tokens $tokens): bool {
|
||||
return $tokens->isAnyTokenKindsFound(self::STATEMENTS);
|
||||
}
|
||||
|
||||
public function getPriority(): int {
|
||||
// for the best result should be run after the BracesFixer
|
||||
return -26;
|
||||
}
|
||||
|
||||
protected function applyFix(SplFileInfo $file, Tokens $tokens): void {
|
||||
foreach ($tokens as $index => $token) {
|
||||
if (!$token->isGivenKind(self::STATEMENTS)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$endStatementIndex = $this->findStatementEnd($tokens, $index);
|
||||
$nextStatementIndex = $tokens->getNextMeaningfulToken($endStatementIndex);
|
||||
if ($nextStatementIndex === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokens[$nextStatementIndex]->equals('}')) {
|
||||
$this->fixBlankLines($tokens, $endStatementIndex + 1, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->fixBlankLines($tokens, $endStatementIndex + 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private function fixBlankLines(Tokens $tokens, int $index, int $countLines): void {
|
||||
$content = $tokens[$index]->getContent();
|
||||
// Apply fix only in the case when the count lines do not equals to expected
|
||||
if (substr_count($content, "\n") === $countLines + 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The final bit of the whitespace must be the next statement's indentation
|
||||
Preg::matchAll('/[^\n\r]+[\r\n]*/', $content, $matches);
|
||||
$lines = $matches[0];
|
||||
$eol = $this->whitespacesConfig->getLineEnding();
|
||||
$tokens[$index] = new Token([T_WHITESPACE, str_repeat($eol, $countLines + 1) . end($lines)]);
|
||||
}
|
||||
|
||||
private function findStatementEnd(Tokens $tokens, int $index): int {
|
||||
$nextIndex = $tokens->getNextMeaningfulToken($index);
|
||||
$nextToken = $tokens[$nextIndex];
|
||||
|
||||
if ($nextToken->equals('(')) {
|
||||
$parenthesisEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextIndex);
|
||||
$possibleBeginBraceIndex = $tokens->getNextNonWhitespace($parenthesisEndIndex);
|
||||
} else {
|
||||
$possibleBeginBraceIndex = $nextIndex;
|
||||
}
|
||||
|
||||
// `do {} while ();`
|
||||
if ($tokens[$index]->isGivenKind(T_WHILE) && $tokens[$possibleBeginBraceIndex]->equals(';')) {
|
||||
return $possibleBeginBraceIndex;
|
||||
}
|
||||
|
||||
$possibleBeginBrace = $tokens[$possibleBeginBraceIndex];
|
||||
if ($possibleBeginBrace->equals('{')) {
|
||||
$blockEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $possibleBeginBraceIndex);
|
||||
} else {
|
||||
$blockEnd = $tokens->getNextTokenOfKind($possibleBeginBraceIndex, [';']);
|
||||
}
|
||||
|
||||
$nextStatementIndex = $tokens->getNextMeaningfulToken($blockEnd);
|
||||
if ($nextStatementIndex === null) {
|
||||
return $blockEnd;
|
||||
}
|
||||
|
||||
// `if () {} elseif {}`
|
||||
if ($tokens[$nextStatementIndex]->isGivenKind(T_ELSEIF)) {
|
||||
return $this->findStatementEnd($tokens, $nextStatementIndex);
|
||||
}
|
||||
|
||||
// `if () {} else if {}` or simple `if () {} else {}`
|
||||
if ($tokens[$nextStatementIndex]->isGivenKind(T_ELSE)) {
|
||||
$nextNextStatementIndex = $tokens->getNextMeaningfulToken($nextStatementIndex);
|
||||
if ($tokens[$nextNextStatementIndex]->isGivenKind(T_IF)) {
|
||||
return $this->findStatementEnd($tokens, $nextNextStatementIndex);
|
||||
}
|
||||
|
||||
return $this->findStatementEnd($tokens, $nextStatementIndex);
|
||||
}
|
||||
|
||||
return $blockEnd;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\CS\Fixer\Whitespace;
|
||||
|
||||
use Ely\CS\Fixer\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\Tokenizer\Analyzer\WhitespacesAnalyzer;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
use PhpCsFixer\Tokenizer\TokensAnalyzer;
|
||||
use SplFileInfo;
|
||||
|
||||
final class MultilineIfStatementBracesFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface {
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public const C_KEEP_ON_OWN_LINE = 'keep_on_own_line';
|
||||
|
||||
public function getDefinition(): FixerDefinitionInterface {
|
||||
return new FixerDefinition(
|
||||
'Ensures that multiline if statement body curly brace placed on the right line.',
|
||||
[
|
||||
new CodeSample(
|
||||
'<?php
|
||||
if ($condition1 == true
|
||||
&& $condition2 === false) {}
|
||||
',
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php
|
||||
if ($condition1 == true
|
||||
&& $condition2 === false
|
||||
) {}
|
||||
',
|
||||
[self::C_KEEP_ON_OWN_LINE => false],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public function isCandidate(Tokens $tokens): bool {
|
||||
return $tokens->isTokenKindFound(T_IF);
|
||||
}
|
||||
|
||||
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface {
|
||||
return new FixerConfigurationResolver([
|
||||
(new FixerOptionBuilder(self::C_KEEP_ON_OWN_LINE, 'adjusts the position of condition closing brace.'))
|
||||
->setAllowedTypes(['bool'])
|
||||
->setDefault(true)
|
||||
->getOption(),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function applyFix(SplFileInfo $file, Tokens $tokens): void {
|
||||
$keepOnOwnLine = $this->configuration[self::C_KEEP_ON_OWN_LINE];
|
||||
$tokensAnalyzer = new TokensAnalyzer($tokens);
|
||||
$eol = $this->whitespacesConfig->getLineEnding();
|
||||
foreach ($tokens as $i => $token) {
|
||||
if (!$token->isGivenKind(T_IF)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$openBraceIndex = $tokens->getNextTokenOfKind($i, ['(']);
|
||||
if (!$tokensAnalyzer->isBlockMultiline($tokens, $openBraceIndex)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$closingBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openBraceIndex);
|
||||
/** @var \PhpCsFixer\Tokenizer\Token $statementBeforeClosingBrace */
|
||||
$statementBeforeClosingBrace = $tokens[$closingBraceIndex - 1];
|
||||
if ($keepOnOwnLine) {
|
||||
if (!$statementBeforeClosingBrace->isWhitespace()
|
||||
|| !str_contains($statementBeforeClosingBrace->getContent(), $eol)
|
||||
) {
|
||||
$indent = WhitespacesAnalyzer::detectIndent($tokens, $i);
|
||||
$tokens->ensureWhitespaceAtIndex($closingBraceIndex, 0, $eol . $indent);
|
||||
}
|
||||
} else {
|
||||
$tokens->removeLeadingWhitespace($closingBraceIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\CS;
|
||||
|
||||
use ArrayIterator;
|
||||
use IteratorAggregate;
|
||||
use PhpCsFixer\Finder;
|
||||
use PhpCsFixer\Fixer\FixerInterface;
|
||||
use ReflectionClass;
|
||||
use Traversable;
|
||||
|
||||
class Fixers implements IteratorAggregate {
|
||||
|
||||
public function getIterator(): Traversable {
|
||||
$finder = new Finder();
|
||||
$finder->in(__DIR__ . '/Fixer')->name('*.php');
|
||||
$classes = [];
|
||||
foreach ($finder as $file) {
|
||||
$class = '\\Ely\\CS' . str_replace('/', '\\', mb_substr($file->getPathname(), mb_strlen(__DIR__), -4));
|
||||
if (!class_exists($class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$rfl = new ReflectionClass($class);
|
||||
if (!$rfl->implementsInterface(FixerInterface::class) || $rfl->isAbstract()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$classes[] = $class;
|
||||
}
|
||||
|
||||
return new ArrayIterator(array_map(fn($class) => new $class(), $classes));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Ely\CS;
|
||||
|
||||
class Rules {
|
||||
final class Rules {
|
||||
|
||||
public static function create(array $overwrittenRules = []): array {
|
||||
return array_merge([
|
||||
@@ -219,17 +219,17 @@ class Rules {
|
||||
],
|
||||
|
||||
// Our custom or extended fixers
|
||||
'Ely/align_multiline_parameters' => [
|
||||
'ErickSkrauch/align_multiline_parameters' => [
|
||||
'variables' => false,
|
||||
'defaults' => false,
|
||||
],
|
||||
'Ely/blank_line_around_class_body' => [
|
||||
'ErickSkrauch/blank_line_around_class_body' => [
|
||||
'apply_to_anonymous_classes' => false,
|
||||
],
|
||||
'Ely/blank_line_before_return' => true,
|
||||
'Ely/line_break_after_statements' => true,
|
||||
'Ely/multiline_if_statement_braces' => true,
|
||||
'Ely/remove_class_name_method_usages' => true,
|
||||
'ErickSkrauch/blank_line_before_return' => true,
|
||||
'ErickSkrauch/line_break_after_statements' => true,
|
||||
'ErickSkrauch/multiline_if_statement_braces' => true,
|
||||
'ErickSkrauch/remove_class_name_method_usages' => true,
|
||||
], $overwrittenRules);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user