Missing dependancies
This commit is contained in:
@@ -0,0 +1,267 @@
|
||||
<?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\Basic;
|
||||
|
||||
use PhpCsFixer\AbstractProxyFixer;
|
||||
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
|
||||
use PhpCsFixer\Fixer\ControlStructure\ControlStructureBracesFixer;
|
||||
use PhpCsFixer\Fixer\ControlStructure\ControlStructureContinuationPositionFixer;
|
||||
use PhpCsFixer\Fixer\LanguageConstruct\DeclareParenthesesFixer;
|
||||
use PhpCsFixer\Fixer\LanguageConstruct\SingleSpaceAfterConstructFixer;
|
||||
use PhpCsFixer\Fixer\Whitespace\NoExtraBlankLinesFixer;
|
||||
use PhpCsFixer\Fixer\Whitespace\StatementIndentationFixer;
|
||||
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\CT;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
/**
|
||||
* Fixer for rules defined in PSR2 ¶4.1, ¶4.4, ¶5.
|
||||
*
|
||||
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*/
|
||||
final class BracesFixer extends AbstractProxyFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
|
||||
{
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public const LINE_NEXT = 'next';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public const LINE_SAME = 'same';
|
||||
|
||||
/**
|
||||
* @var null|CurlyBracesPositionFixer
|
||||
*/
|
||||
private $curlyBracesPositionFixer;
|
||||
|
||||
/**
|
||||
* @var null|ControlStructureContinuationPositionFixer
|
||||
*/
|
||||
private $controlStructureContinuationPositionFixer;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'The body of each structure MUST be enclosed by braces. Braces should be properly placed. Body of braces should be properly indented.',
|
||||
[
|
||||
new CodeSample(
|
||||
'<?php
|
||||
|
||||
class Foo {
|
||||
public function bar($baz) {
|
||||
if ($baz = 900) echo "Hello!";
|
||||
|
||||
if ($baz = 9000)
|
||||
echo "Wait!";
|
||||
|
||||
if ($baz == true)
|
||||
{
|
||||
echo "Why?";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "Ha?";
|
||||
}
|
||||
|
||||
if (is_array($baz))
|
||||
foreach ($baz as $b)
|
||||
{
|
||||
echo $b;
|
||||
}
|
||||
}
|
||||
}
|
||||
'
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php
|
||||
$positive = function ($item) { return $item >= 0; };
|
||||
$negative = function ($item) {
|
||||
return $item < 0; };
|
||||
',
|
||||
['allow_single_line_closure' => true]
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php
|
||||
|
||||
class Foo
|
||||
{
|
||||
public function bar($baz)
|
||||
{
|
||||
if ($baz = 900) echo "Hello!";
|
||||
|
||||
if ($baz = 9000)
|
||||
echo "Wait!";
|
||||
|
||||
if ($baz == true)
|
||||
{
|
||||
echo "Why?";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "Ha?";
|
||||
}
|
||||
|
||||
if (is_array($baz))
|
||||
foreach ($baz as $b)
|
||||
{
|
||||
echo $b;
|
||||
}
|
||||
}
|
||||
}
|
||||
',
|
||||
['position_after_functions_and_oop_constructs' => self::LINE_SAME]
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run before HeredocIndentationFixer.
|
||||
* Must run after ClassAttributesSeparationFixer, ClassDefinitionFixer, EmptyLoopBodyFixer, NoAlternativeSyntaxFixer, NoEmptyStatementFixer, NoUselessElseFixer, SingleLineThrowFixer, SingleSpaceAfterConstructFixer, SingleTraitInsertPerStatementFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 35;
|
||||
}
|
||||
|
||||
public function configure(array $configuration = null): void
|
||||
{
|
||||
parent::configure($configuration);
|
||||
|
||||
$this->getCurlyBracesPositionFixer()->configure([
|
||||
'control_structures_opening_brace' => $this->translatePositionOption($this->configuration['position_after_control_structures']),
|
||||
'functions_opening_brace' => $this->translatePositionOption($this->configuration['position_after_functions_and_oop_constructs']),
|
||||
'anonymous_functions_opening_brace' => $this->translatePositionOption($this->configuration['position_after_anonymous_constructs']),
|
||||
'classes_opening_brace' => $this->translatePositionOption($this->configuration['position_after_functions_and_oop_constructs']),
|
||||
'anonymous_classes_opening_brace' => $this->translatePositionOption($this->configuration['position_after_anonymous_constructs']),
|
||||
'allow_single_line_empty_anonymous_classes' => $this->configuration['allow_single_line_anonymous_class_with_empty_body'],
|
||||
'allow_single_line_anonymous_functions' => $this->configuration['allow_single_line_closure'],
|
||||
]);
|
||||
|
||||
$this->getControlStructureContinuationPositionFixer()->configure([
|
||||
'position' => self::LINE_NEXT === $this->configuration['position_after_control_structures']
|
||||
? ControlStructureContinuationPositionFixer::NEXT_LINE
|
||||
: ControlStructureContinuationPositionFixer::SAME_LINE,
|
||||
]);
|
||||
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
for ($index = \count($tokens) - 1; $index > 0; --$index) {
|
||||
if ($tokens[$index]->isGivenKind(CT::T_USE_LAMBDA)) {
|
||||
$tokens->ensureWhitespaceAtIndex($index - 1, 1, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
parent::applyFix($file, $tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
|
||||
{
|
||||
return new FixerConfigurationResolver([
|
||||
(new FixerOptionBuilder('allow_single_line_anonymous_class_with_empty_body', 'Whether single line anonymous class with empty body notation should be allowed.'))
|
||||
->setAllowedTypes(['bool'])
|
||||
->setDefault(false)
|
||||
->getOption(),
|
||||
(new FixerOptionBuilder('allow_single_line_closure', 'Whether single line lambda notation should be allowed.'))
|
||||
->setAllowedTypes(['bool'])
|
||||
->setDefault(false)
|
||||
->getOption(),
|
||||
(new FixerOptionBuilder('position_after_functions_and_oop_constructs', 'whether the opening brace should be placed on "next" or "same" line after classy constructs (non-anonymous classes, interfaces, traits, methods and non-lambda functions).'))
|
||||
->setAllowedValues([self::LINE_NEXT, self::LINE_SAME])
|
||||
->setDefault(self::LINE_NEXT)
|
||||
->getOption(),
|
||||
(new FixerOptionBuilder('position_after_control_structures', 'whether the opening brace should be placed on "next" or "same" line after control structures.'))
|
||||
->setAllowedValues([self::LINE_NEXT, self::LINE_SAME])
|
||||
->setDefault(self::LINE_SAME)
|
||||
->getOption(),
|
||||
(new FixerOptionBuilder('position_after_anonymous_constructs', 'whether the opening brace should be placed on "next" or "same" line after anonymous constructs (anonymous classes and lambda functions).'))
|
||||
->setAllowedValues([self::LINE_NEXT, self::LINE_SAME])
|
||||
->setDefault(self::LINE_SAME)
|
||||
->getOption(),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function createProxyFixers(): array
|
||||
{
|
||||
$singleSpaceAfterConstructFixer = new SingleSpaceAfterConstructFixer();
|
||||
$singleSpaceAfterConstructFixer->configure([
|
||||
'constructs' => ['elseif', 'for', 'foreach', 'if', 'match', 'while', 'use_lambda'],
|
||||
]);
|
||||
|
||||
$noExtraBlankLinesFixer = new NoExtraBlankLinesFixer();
|
||||
$noExtraBlankLinesFixer->configure([
|
||||
'tokens' => ['curly_brace_block'],
|
||||
]);
|
||||
|
||||
return [
|
||||
$singleSpaceAfterConstructFixer,
|
||||
new ControlStructureBracesFixer(),
|
||||
$noExtraBlankLinesFixer,
|
||||
$this->getCurlyBracesPositionFixer(),
|
||||
$this->getControlStructureContinuationPositionFixer(),
|
||||
new DeclareParenthesesFixer(),
|
||||
new NoMultipleStatementsPerLineFixer(),
|
||||
new StatementIndentationFixer(true),
|
||||
];
|
||||
}
|
||||
|
||||
private function getCurlyBracesPositionFixer(): CurlyBracesPositionFixer
|
||||
{
|
||||
if (null === $this->curlyBracesPositionFixer) {
|
||||
$this->curlyBracesPositionFixer = new CurlyBracesPositionFixer();
|
||||
}
|
||||
|
||||
return $this->curlyBracesPositionFixer;
|
||||
}
|
||||
|
||||
private function getControlStructureContinuationPositionFixer(): ControlStructureContinuationPositionFixer
|
||||
{
|
||||
if (null === $this->controlStructureContinuationPositionFixer) {
|
||||
$this->controlStructureContinuationPositionFixer = new ControlStructureContinuationPositionFixer();
|
||||
}
|
||||
|
||||
return $this->controlStructureContinuationPositionFixer;
|
||||
}
|
||||
|
||||
private function translatePositionOption(string $option): string
|
||||
{
|
||||
return self::LINE_NEXT === $option
|
||||
? CurlyBracesPositionFixer::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END
|
||||
: CurlyBracesPositionFixer::SAME_LINE
|
||||
;
|
||||
}
|
||||
}
|
||||
+429
@@ -0,0 +1,429 @@
|
||||
<?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\Basic;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
|
||||
use PhpCsFixer\Fixer\Indentation;
|
||||
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\FixerDefinition\VersionSpecification;
|
||||
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
|
||||
use PhpCsFixer\Preg;
|
||||
use PhpCsFixer\Tokenizer\CT;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
use PhpCsFixer\Tokenizer\TokensAnalyzer;
|
||||
|
||||
final class CurlyBracesPositionFixer extends AbstractFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
|
||||
{
|
||||
use Indentation;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public const NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END = 'next_line_unless_newline_at_signature_end';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public const SAME_LINE = 'same_line';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Curly braces must be placed as configured.',
|
||||
[
|
||||
new CodeSample(
|
||||
'<?php
|
||||
class Foo {
|
||||
}
|
||||
|
||||
function foo() {
|
||||
}
|
||||
|
||||
$foo = function()
|
||||
{
|
||||
};
|
||||
|
||||
if (foo())
|
||||
{
|
||||
bar();
|
||||
}
|
||||
'
|
||||
),
|
||||
new VersionSpecificCodeSample(
|
||||
'<?php
|
||||
$foo = new class
|
||||
{
|
||||
};
|
||||
',
|
||||
new VersionSpecification(70000)
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php
|
||||
if (foo()) {
|
||||
bar();
|
||||
}
|
||||
',
|
||||
['control_structures_opening_brace' => self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END]
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php
|
||||
function foo()
|
||||
{
|
||||
}
|
||||
',
|
||||
['functions_opening_brace' => self::SAME_LINE]
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php
|
||||
$foo = function () {
|
||||
};
|
||||
',
|
||||
['anonymous_functions_opening_brace' => self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END]
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php
|
||||
class Foo
|
||||
{
|
||||
}
|
||||
',
|
||||
['classes_opening_brace' => self::SAME_LINE]
|
||||
),
|
||||
new VersionSpecificCodeSample(
|
||||
'<?php
|
||||
$foo = new class {
|
||||
};
|
||||
',
|
||||
new VersionSpecification(70000),
|
||||
['anonymous_classes_opening_brace' => self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END]
|
||||
),
|
||||
new VersionSpecificCodeSample(
|
||||
'<?php
|
||||
$foo = new class { };
|
||||
$bar = new class { private $baz; };
|
||||
',
|
||||
new VersionSpecification(70000),
|
||||
['allow_single_line_empty_anonymous_classes' => true]
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php
|
||||
$foo = function () { return true; };
|
||||
$bar = function () { $result = true;
|
||||
return $result; };
|
||||
',
|
||||
['allow_single_line_anonymous_functions' => true]
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isTokenKindFound('{');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run after ControlStructureBracesFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return parent::getPriority();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
$classyTokens = Token::getClassyTokenKinds();
|
||||
$controlStructureTokens = [T_DECLARE, T_DO, T_ELSE, T_ELSEIF, T_FINALLY, T_FOR, T_FOREACH, T_IF, T_WHILE, T_TRY, T_CATCH, T_SWITCH];
|
||||
// @TODO: drop condition when PHP 8.0+ is required
|
||||
if (\defined('T_MATCH')) {
|
||||
$controlStructureTokens[] = T_MATCH;
|
||||
}
|
||||
|
||||
$tokensAnalyzer = new TokensAnalyzer($tokens);
|
||||
|
||||
$allowSingleLineUntil = null;
|
||||
|
||||
foreach ($tokens as $index => $token) {
|
||||
$allowSingleLine = false;
|
||||
$allowSingleLineIfEmpty = false;
|
||||
|
||||
if ($token->isGivenKind($classyTokens)) {
|
||||
$openBraceIndex = $tokens->getNextTokenOfKind($index, ['{']);
|
||||
|
||||
if ($tokensAnalyzer->isAnonymousClass($index)) {
|
||||
$allowSingleLineIfEmpty = $this->configuration['allow_single_line_empty_anonymous_classes'];
|
||||
$positionOption = 'anonymous_classes_opening_brace';
|
||||
} else {
|
||||
$positionOption = 'classes_opening_brace';
|
||||
}
|
||||
} elseif ($token->isGivenKind(T_FUNCTION)) {
|
||||
$openBraceIndex = $tokens->getNextTokenOfKind($index, ['{', ';']);
|
||||
|
||||
if ($tokens[$openBraceIndex]->equals(';')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($tokensAnalyzer->isLambda($index)) {
|
||||
$allowSingleLine = $this->configuration['allow_single_line_anonymous_functions'];
|
||||
$positionOption = 'anonymous_functions_opening_brace';
|
||||
} else {
|
||||
$positionOption = 'functions_opening_brace';
|
||||
}
|
||||
} elseif ($token->isGivenKind($controlStructureTokens)) {
|
||||
$parenthesisEndIndex = $this->findParenthesisEnd($tokens, $index);
|
||||
$openBraceIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
|
||||
|
||||
if (!$tokens[$openBraceIndex]->equals('{')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$positionOption = 'control_structures_opening_brace';
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
$closeBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $openBraceIndex);
|
||||
|
||||
$addNewlinesInsideBraces = true;
|
||||
if ($allowSingleLine || $allowSingleLineIfEmpty || $index < $allowSingleLineUntil) {
|
||||
$addNewlinesInsideBraces = false;
|
||||
|
||||
for ($indexInsideBraces = $openBraceIndex + 1; $indexInsideBraces < $closeBraceIndex; ++$indexInsideBraces) {
|
||||
$tokenInsideBraces = $tokens[$indexInsideBraces];
|
||||
|
||||
if (
|
||||
($allowSingleLineIfEmpty && !$tokenInsideBraces->isWhitespace() && !$tokenInsideBraces->isComment())
|
||||
|| ($tokenInsideBraces->isWhitespace() && 1 === Preg::match('/\R/', $tokenInsideBraces->getContent()))
|
||||
) {
|
||||
$addNewlinesInsideBraces = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$addNewlinesInsideBraces && null === $allowSingleLineUntil) {
|
||||
$allowSingleLineUntil = $closeBraceIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
$addNewlinesInsideBraces
|
||||
&& !$this->isFollowedByNewLine($tokens, $openBraceIndex)
|
||||
&& !$this->hasCommentOnSameLine($tokens, $openBraceIndex)
|
||||
&& !$tokens[$tokens->getNextMeaningfulToken($openBraceIndex)]->isGivenKind(T_CLOSE_TAG)
|
||||
) {
|
||||
$whitespace = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $openBraceIndex);
|
||||
if ($tokens->ensureWhitespaceAtIndex($openBraceIndex + 1, 0, $whitespace)) {
|
||||
++$closeBraceIndex;
|
||||
}
|
||||
}
|
||||
|
||||
$whitespace = ' ';
|
||||
if (self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END === $this->configuration[$positionOption]) {
|
||||
$whitespace = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $index);
|
||||
|
||||
$previousTokenIndex = $openBraceIndex;
|
||||
do {
|
||||
$previousTokenIndex = $tokens->getPrevMeaningfulToken($previousTokenIndex);
|
||||
} while ($tokens[$previousTokenIndex]->isGivenKind([CT::T_TYPE_COLON, CT::T_NULLABLE_TYPE, T_STRING, T_NS_SEPARATOR, CT::T_ARRAY_TYPEHINT, T_STATIC, CT::T_TYPE_ALTERNATION, CT::T_TYPE_INTERSECTION]));
|
||||
|
||||
if ($tokens[$previousTokenIndex]->equals(')')) {
|
||||
if ($tokens[--$previousTokenIndex]->isComment()) {
|
||||
--$previousTokenIndex;
|
||||
}
|
||||
if (
|
||||
$tokens[$previousTokenIndex]->isWhitespace()
|
||||
&& 1 === Preg::match('/\R/', $tokens[$previousTokenIndex]->getContent())
|
||||
) {
|
||||
$whitespace = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$moveBraceToIndex = null;
|
||||
|
||||
if (' ' === $whitespace) {
|
||||
$previousMeaningfulIndex = $tokens->getPrevMeaningfulToken($openBraceIndex);
|
||||
for ($indexBeforeOpenBrace = $openBraceIndex - 1; $indexBeforeOpenBrace > $previousMeaningfulIndex; --$indexBeforeOpenBrace) {
|
||||
if (!$tokens[$indexBeforeOpenBrace]->isComment()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tokenBeforeOpenBrace = $tokens[--$indexBeforeOpenBrace];
|
||||
if ($tokenBeforeOpenBrace->isWhitespace()) {
|
||||
$moveBraceToIndex = $indexBeforeOpenBrace;
|
||||
} elseif ($indexBeforeOpenBrace === $previousMeaningfulIndex) {
|
||||
$moveBraceToIndex = $previousMeaningfulIndex + 1;
|
||||
}
|
||||
}
|
||||
} elseif (!$tokens[$openBraceIndex - 1]->isWhitespace() || !Preg::match('/\R/', $tokens[$openBraceIndex - 1]->getContent())) {
|
||||
for ($indexAfterOpenBrace = $openBraceIndex + 1; $indexAfterOpenBrace < $closeBraceIndex; ++$indexAfterOpenBrace) {
|
||||
if ($tokens[$indexAfterOpenBrace]->isWhitespace() && Preg::match('/\R/', $tokens[$indexAfterOpenBrace]->getContent())) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($tokens[$indexAfterOpenBrace]->isComment() && !str_starts_with($tokens[$indexAfterOpenBrace]->getContent(), '/*')) {
|
||||
$moveBraceToIndex = $indexAfterOpenBrace + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $moveBraceToIndex) {
|
||||
/** @var Token $movedToken */
|
||||
$movedToken = clone $tokens[$openBraceIndex];
|
||||
|
||||
$delta = $openBraceIndex < $moveBraceToIndex ? 1 : -1;
|
||||
|
||||
if ($tokens[$openBraceIndex + $delta]->isWhitespace()) {
|
||||
if (-1 === $delta && Preg::match('/\R/', $tokens[$openBraceIndex - 1]->getContent())) {
|
||||
$content = Preg::replace('/^(\h*?\R)?\h*/', '', $tokens[$openBraceIndex + 1]->getContent());
|
||||
if ('' !== $content) {
|
||||
$tokens[$openBraceIndex + 1] = new Token([T_WHITESPACE, $content]);
|
||||
} else {
|
||||
$tokens->clearAt($openBraceIndex + 1);
|
||||
}
|
||||
} else {
|
||||
$tokens->clearAt($openBraceIndex - 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (; $openBraceIndex !== $moveBraceToIndex; $openBraceIndex += $delta) {
|
||||
/** @var Token $siblingToken */
|
||||
$siblingToken = $tokens[$openBraceIndex + $delta];
|
||||
$tokens[$openBraceIndex] = $siblingToken;
|
||||
}
|
||||
|
||||
$tokens[$openBraceIndex] = $movedToken;
|
||||
|
||||
$openBraceIndex = $moveBraceToIndex;
|
||||
}
|
||||
|
||||
if ($tokens->ensureWhitespaceAtIndex($openBraceIndex - 1, 1, $whitespace)) {
|
||||
++$closeBraceIndex;
|
||||
if (null !== $allowSingleLineUntil) {
|
||||
++$allowSingleLineUntil;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!$addNewlinesInsideBraces
|
||||
|| $tokens[$tokens->getPrevMeaningfulToken($closeBraceIndex)]->isGivenKind(T_OPEN_TAG)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for ($prevIndex = $closeBraceIndex - 1; $tokens->isEmptyAt($prevIndex); --$prevIndex);
|
||||
|
||||
$prevToken = $tokens[$prevIndex];
|
||||
if ($prevToken->isWhitespace() && 1 === Preg::match('/\R/', $prevToken->getContent())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$whitespace = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $openBraceIndex);
|
||||
$tokens->ensureWhitespaceAtIndex($prevIndex, 1, $whitespace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
|
||||
{
|
||||
return new FixerConfigurationResolver([
|
||||
(new FixerOptionBuilder('control_structures_opening_brace', 'the position of the opening brace of control structures body.'))
|
||||
->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
|
||||
->setDefault(self::SAME_LINE)
|
||||
->getOption(),
|
||||
(new FixerOptionBuilder('functions_opening_brace', 'the position of the opening brace of functions body.'))
|
||||
->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
|
||||
->setDefault(self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END)
|
||||
->getOption(),
|
||||
(new FixerOptionBuilder('anonymous_functions_opening_brace', 'the position of the opening brace of anonymous functions body.'))
|
||||
->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
|
||||
->setDefault(self::SAME_LINE)
|
||||
->getOption(),
|
||||
(new FixerOptionBuilder('classes_opening_brace', 'the position of the opening brace of classes body.'))
|
||||
->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
|
||||
->setDefault(self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END)
|
||||
->getOption(),
|
||||
(new FixerOptionBuilder('anonymous_classes_opening_brace', 'the position of the opening brace of anonymous classes body.'))
|
||||
->setAllowedValues([self::NEXT_LINE_UNLESS_NEWLINE_AT_SIGNATURE_END, self::SAME_LINE])
|
||||
->setDefault(self::SAME_LINE)
|
||||
->getOption(),
|
||||
(new FixerOptionBuilder('allow_single_line_empty_anonymous_classes', 'allow anonymous classes to have opening and closing braces on the same line.'))
|
||||
->setAllowedTypes(['bool'])
|
||||
->setDefault(true)
|
||||
->getOption(),
|
||||
(new FixerOptionBuilder('allow_single_line_anonymous_functions', 'allow anonymous functions to have opening and closing braces on the same line.'))
|
||||
->setAllowedTypes(['bool'])
|
||||
->setDefault(true)
|
||||
->getOption(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function findParenthesisEnd(Tokens $tokens, int $structureTokenIndex): int
|
||||
{
|
||||
$nextIndex = $tokens->getNextMeaningfulToken($structureTokenIndex);
|
||||
$nextToken = $tokens[$nextIndex];
|
||||
|
||||
// return if next token is not opening parenthesis
|
||||
if (!$nextToken->equals('(')) {
|
||||
return $structureTokenIndex;
|
||||
}
|
||||
|
||||
return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $nextIndex);
|
||||
}
|
||||
|
||||
private function isFollowedByNewLine(Tokens $tokens, int $index): bool
|
||||
{
|
||||
for (++$index, $max = \count($tokens) - 1; $index < $max; ++$index) {
|
||||
$token = $tokens[$index];
|
||||
if (!$token->isComment()) {
|
||||
return $token->isWhitespace() && 1 === Preg::match('/\R/', $token->getContent());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function hasCommentOnSameLine(Tokens $tokens, int $index): bool
|
||||
{
|
||||
$token = $tokens[$index + 1];
|
||||
|
||||
if ($token->isWhitespace() && 1 !== Preg::match('/\R/', $token->getContent())) {
|
||||
$token = $tokens[$index + 2];
|
||||
}
|
||||
|
||||
return $token->isComment();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?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\Basic;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
/**
|
||||
* Fixer for rules defined in PSR1 ¶2.2.
|
||||
*
|
||||
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
*/
|
||||
final class EncodingFixer extends AbstractFixer
|
||||
{
|
||||
private string $BOM;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->BOM = pack('CCC', 0xEF, 0xBB, 0xBF);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'PHP code MUST use only UTF-8 without BOM (remove BOM).',
|
||||
[
|
||||
new CodeSample(
|
||||
$this->BOM.'<?php
|
||||
|
||||
echo "Hello!";
|
||||
'
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
// must run first (at least before Fixers that using Tokens) - for speed reason of whole fixing process
|
||||
return 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
$content = $tokens[0]->getContent();
|
||||
|
||||
if (0 === strncmp($content, $this->BOM, 3)) {
|
||||
$newContent = substr($content, 3);
|
||||
|
||||
if ('' === $newContent) {
|
||||
$tokens->clearAt(0);
|
||||
} else {
|
||||
$tokens[0] = new Token([$tokens[0]->getId(), $newContent]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+109
@@ -0,0 +1,109 @@
|
||||
<?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\Basic;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\Fixer\Indentation;
|
||||
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
|
||||
use PhpCsFixer\FixerDefinition\CodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Preg;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
/**
|
||||
* Fixer for rules defined in PSR2 ¶2.3 Lines: There must not be more than one statement per line.
|
||||
*/
|
||||
final class NoMultipleStatementsPerLineFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
|
||||
{
|
||||
use Indentation;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'There must not be more than one statement per line.',
|
||||
[new CodeSample("<?php\nfoo(); bar();\n")]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Must run after ControlStructureBracesFixer, NoEmptyStatementFixer.
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isTokenKindFound(';');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
for ($index = 1, $max = \count($tokens) - 1; $index < $max; ++$index) {
|
||||
if ($tokens[$index]->isGivenKind(T_FOR)) {
|
||||
$index = $tokens->findBlockEnd(
|
||||
Tokens::BLOCK_TYPE_PARENTHESIS_BRACE,
|
||||
$tokens->getNextTokenOfKind($index, ['('])
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$tokens[$index]->equals(';')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for ($nextIndex = $index + 1; $nextIndex < $max; ++$nextIndex) {
|
||||
$token = $tokens[$nextIndex];
|
||||
|
||||
if ($token->isWhitespace() || $token->isComment()) {
|
||||
if (1 === Preg::match('/\R/', $token->getContent())) {
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$token->equalsAny(['}', [T_CLOSE_TAG], [T_ENDIF], [T_ENDFOR], [T_ENDSWITCH], [T_ENDWHILE], [T_ENDFOREACH]])) {
|
||||
$whitespaceIndex = $index;
|
||||
do {
|
||||
$token = $tokens[++$whitespaceIndex];
|
||||
} while ($token->isComment());
|
||||
|
||||
$newline = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $index);
|
||||
|
||||
if ($tokens->ensureWhitespaceAtIndex($whitespaceIndex, 0, $newline)) {
|
||||
++$max;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+163
@@ -0,0 +1,163 @@
|
||||
<?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\Basic;
|
||||
|
||||
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\Tokenizer\Analyzer\AttributeAnalyzer;
|
||||
use PhpCsFixer\Tokenizer\CT;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
final class NoTrailingCommaInSinglelineFixer extends AbstractFixer implements ConfigurableFixerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'If a list of values separated by a comma is contained on a single line, then the last item MUST NOT have a trailing comma.',
|
||||
[
|
||||
new CodeSample("<?php\nfoo(\$a,);\n\$foo = array(1,);\n[\$foo, \$bar,] = \$array;\nuse a\\{ClassA, ClassB,};\n"),
|
||||
new CodeSample("<?php\nfoo(\$a,);\n[\$foo, \$bar,] = \$array;\n", ['elements' => ['array_destructuring']]),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return
|
||||
$tokens->isTokenKindFound(',')
|
||||
&& $tokens->isAnyTokenKindsFound([')', CT::T_ARRAY_SQUARE_BRACE_CLOSE, CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE, CT::T_GROUP_IMPORT_BRACE_CLOSE])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
|
||||
{
|
||||
$elements = ['arguments', 'array_destructuring', 'array', 'group_import'];
|
||||
|
||||
return new FixerConfigurationResolver([
|
||||
(new FixerOptionBuilder('elements', 'Which elements to fix.'))
|
||||
->setAllowedTypes(['array'])
|
||||
->setAllowedValues([new AllowedValueSubset($elements)])
|
||||
->setDefault($elements)
|
||||
->getOption(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
for ($index = $tokens->count() - 1; $index >= 0; --$index) {
|
||||
if (!$tokens[$index]->equals(')') && !$tokens[$index]->isGivenKind([CT::T_ARRAY_SQUARE_BRACE_CLOSE, CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE, CT::T_GROUP_IMPORT_BRACE_CLOSE])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$commaIndex = $tokens->getPrevMeaningfulToken($index);
|
||||
|
||||
if (!$tokens[$commaIndex]->equals(',')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$block = Tokens::detectBlockType($tokens[$index]);
|
||||
$blockOpenIndex = $tokens->findBlockStart($block['type'], $index);
|
||||
|
||||
if ($tokens->isPartialCodeMultiline($blockOpenIndex, $index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->shouldBeCleared($tokens, $blockOpenIndex)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
do {
|
||||
$tokens->clearTokenAndMergeSurroundingWhitespace($commaIndex);
|
||||
$commaIndex = $tokens->getPrevMeaningfulToken($commaIndex);
|
||||
} while ($tokens[$commaIndex]->equals(','));
|
||||
|
||||
$tokens->removeTrailingWhitespace($commaIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private function shouldBeCleared(Tokens $tokens, int $openIndex): bool
|
||||
{
|
||||
/** @var string[] $elements */
|
||||
$elements = $this->configuration['elements'];
|
||||
|
||||
if ($tokens[$openIndex]->isGivenKind(CT::T_ARRAY_SQUARE_BRACE_OPEN)) {
|
||||
return \in_array('array', $elements, true);
|
||||
}
|
||||
|
||||
if ($tokens[$openIndex]->isGivenKind(CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN)) {
|
||||
return \in_array('array_destructuring', $elements, true);
|
||||
}
|
||||
|
||||
if ($tokens[$openIndex]->isGivenKind(CT::T_GROUP_IMPORT_BRACE_OPEN)) {
|
||||
return \in_array('group_import', $elements, true);
|
||||
}
|
||||
|
||||
if (!$tokens[$openIndex]->equals('(')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$beforeOpen = $tokens->getPrevMeaningfulToken($openIndex);
|
||||
|
||||
if ($tokens[$beforeOpen]->isGivenKind(T_ARRAY)) {
|
||||
return \in_array('array', $elements, true);
|
||||
}
|
||||
|
||||
if ($tokens[$beforeOpen]->isGivenKind(T_LIST)) {
|
||||
return \in_array('array_destructuring', $elements, true);
|
||||
}
|
||||
|
||||
if ($tokens[$beforeOpen]->isGivenKind([T_UNSET, T_ISSET, T_VARIABLE, T_CLASS])) {
|
||||
return \in_array('arguments', $elements, true);
|
||||
}
|
||||
|
||||
if ($tokens[$beforeOpen]->isGivenKind(T_STRING)) {
|
||||
return !AttributeAnalyzer::isAttribute($tokens, $beforeOpen) && \in_array('arguments', $elements, true);
|
||||
}
|
||||
|
||||
if ($tokens[$beforeOpen]->equalsAny([')', ']', [CT::T_DYNAMIC_VAR_BRACE_CLOSE], [CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE]])) {
|
||||
$block = Tokens::detectBlockType($tokens[$beforeOpen]);
|
||||
|
||||
return
|
||||
(
|
||||
Tokens::BLOCK_TYPE_ARRAY_INDEX_CURLY_BRACE === $block['type']
|
||||
|| Tokens::BLOCK_TYPE_DYNAMIC_VAR_BRACE === $block['type']
|
||||
|| Tokens::BLOCK_TYPE_INDEX_SQUARE_BRACE === $block['type']
|
||||
|| Tokens::BLOCK_TYPE_PARENTHESIS_BRACE === $block['type']
|
||||
) && \in_array('arguments', $elements, true)
|
||||
;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
<?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\Basic;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Removes Zero-width space (ZWSP), Non-breaking space (NBSP) and other invisible unicode symbols.
|
||||
*
|
||||
* @author Ivan Boprzenkov <ivan.borzenkov@gmail.com>
|
||||
*/
|
||||
final class NonPrintableCharacterFixer extends AbstractFixer implements ConfigurableFixerInterface
|
||||
{
|
||||
/**
|
||||
* @var array<string, string[]>
|
||||
*/
|
||||
private array $symbolsReplace;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
private static array $tokens = [
|
||||
T_STRING_VARNAME,
|
||||
T_INLINE_HTML,
|
||||
T_VARIABLE,
|
||||
T_COMMENT,
|
||||
T_ENCAPSED_AND_WHITESPACE,
|
||||
T_CONSTANT_ENCAPSED_STRING,
|
||||
T_DOC_COMMENT,
|
||||
];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->symbolsReplace = [
|
||||
pack('H*', 'e2808b') => ['', '200b'], // ZWSP U+200B
|
||||
pack('H*', 'e28087') => [' ', '2007'], // FIGURE SPACE U+2007
|
||||
pack('H*', 'e280af') => [' ', '202f'], // NBSP U+202F
|
||||
pack('H*', 'e281a0') => ['', '2060'], // WORD JOINER U+2060
|
||||
pack('H*', 'c2a0') => [' ', 'a0'], // NO-BREAK SPACE U+A0
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Remove Zero-width space (ZWSP), Non-breaking space (NBSP) and other invisible unicode symbols.',
|
||||
[
|
||||
new CodeSample(
|
||||
'<?php echo "'.pack('H*', 'e2808b').'Hello'.pack('H*', 'e28087').'World'.pack('H*', 'c2a0')."!\";\n"
|
||||
),
|
||||
new CodeSample(
|
||||
'<?php echo "'.pack('H*', 'e2808b').'Hello'.pack('H*', 'e28087').'World'.pack('H*', 'c2a0')."!\";\n",
|
||||
['use_escape_sequences_in_strings' => false]
|
||||
),
|
||||
],
|
||||
null,
|
||||
'Risky when strings contain intended invisible characters.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRisky(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isAnyTokenKindsFound(self::$tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
|
||||
{
|
||||
return new FixerConfigurationResolver([
|
||||
(new FixerOptionBuilder('use_escape_sequences_in_strings', 'Whether characters should be replaced with escape sequences in strings.'))
|
||||
->setAllowedTypes(['bool'])
|
||||
->setDefault(true)
|
||||
->getOption(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
$replacements = [];
|
||||
$escapeSequences = [];
|
||||
|
||||
foreach ($this->symbolsReplace as $character => [$replacement, $codepoint]) {
|
||||
$replacements[$character] = $replacement;
|
||||
$escapeSequences[$character] = '\u{'.$codepoint.'}';
|
||||
}
|
||||
|
||||
foreach ($tokens as $index => $token) {
|
||||
$content = $token->getContent();
|
||||
|
||||
if (
|
||||
$this->configuration['use_escape_sequences_in_strings']
|
||||
&& $token->isGivenKind([T_CONSTANT_ENCAPSED_STRING, T_ENCAPSED_AND_WHITESPACE])
|
||||
) {
|
||||
if (!Preg::match('/'.implode('|', array_keys($escapeSequences)).'/', $content)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$previousToken = $tokens[$index - 1];
|
||||
$stringTypeChanged = false;
|
||||
$swapQuotes = false;
|
||||
|
||||
if ($previousToken->isGivenKind(T_START_HEREDOC)) {
|
||||
$previousTokenContent = $previousToken->getContent();
|
||||
|
||||
if (str_contains($previousTokenContent, '\'')) {
|
||||
$tokens[$index - 1] = new Token([T_START_HEREDOC, str_replace('\'', '', $previousTokenContent)]);
|
||||
$stringTypeChanged = true;
|
||||
}
|
||||
} elseif (str_starts_with($content, "'")) {
|
||||
$stringTypeChanged = true;
|
||||
$swapQuotes = true;
|
||||
}
|
||||
|
||||
if ($swapQuotes) {
|
||||
$content = str_replace("\\'", "'", $content);
|
||||
}
|
||||
|
||||
if ($stringTypeChanged) {
|
||||
$content = Preg::replace('/(\\\\{1,2})/', '\\\\\\\\', $content);
|
||||
$content = str_replace('$', '\$', $content);
|
||||
}
|
||||
|
||||
if ($swapQuotes) {
|
||||
$content = str_replace('"', '\"', $content);
|
||||
$content = Preg::replace('/^\'(.*)\'$/s', '"$1"', $content);
|
||||
}
|
||||
|
||||
$tokens[$index] = new Token([$token->getId(), strtr($content, $escapeSequences)]);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token->isGivenKind(self::$tokens)) {
|
||||
$newContent = strtr($content, $replacements);
|
||||
|
||||
// variable name cannot contain space
|
||||
if ($token->isGivenKind([T_STRING_VARNAME, T_VARIABLE]) && str_contains($newContent, ' ')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// multiline comment must have "*/" only at the end
|
||||
if ($token->isGivenKind([T_COMMENT, T_DOC_COMMENT]) && str_starts_with($newContent, '/*') && strpos($newContent, '*/') !== \strlen($newContent) - 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tokens[$index] = new Token([$token->getId(), $newContent]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
<?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\Basic;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\FixerDefinition\VersionSpecification;
|
||||
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
|
||||
use PhpCsFixer\Preg;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
final class OctalNotationFixer extends AbstractFixer
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Literal octal must be in `0o` notation.',
|
||||
[
|
||||
new VersionSpecificCodeSample(
|
||||
"<?php \$foo = 0123;\n",
|
||||
new VersionSpecification(80100)
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return \PHP_VERSION_ID >= 80100 && $tokens->isTokenKindFound(T_LNUMBER);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
foreach ($tokens as $index => $token) {
|
||||
if (!$token->isGivenKind(T_LNUMBER)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$content = $token->getContent();
|
||||
|
||||
if (1 !== Preg::match('#^0\d+$#', $content)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tokens[$index] = 1 === Preg::match('#^0+$#', $content)
|
||||
? new Token([T_LNUMBER, '0'])
|
||||
: new Token([T_LNUMBER, '0o'.substr($content, 1)])
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
+298
@@ -0,0 +1,298 @@
|
||||
<?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\Basic;
|
||||
|
||||
use PhpCsFixer\AbstractFixer;
|
||||
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
|
||||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
|
||||
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
|
||||
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
|
||||
use PhpCsFixer\FixerDefinition\FileSpecificCodeSample;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinition;
|
||||
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
|
||||
use PhpCsFixer\Preg;
|
||||
use PhpCsFixer\StdinFileInfo;
|
||||
use PhpCsFixer\Tokenizer\Token;
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
use PhpCsFixer\Tokenizer\TokensAnalyzer;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
|
||||
* @author Bram Gotink <bram@gotink.me>
|
||||
* @author Graham Campbell <hello@gjcampbell.co.uk>
|
||||
* @author Kuba Werłos <werlos@gmail.com>
|
||||
*/
|
||||
final class PsrAutoloadingFixer extends AbstractFixer implements ConfigurableFixerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(): FixerDefinitionInterface
|
||||
{
|
||||
return new FixerDefinition(
|
||||
'Classes must be in a path that matches their namespace, be at least one namespace deep and the class name should match the file name.',
|
||||
[
|
||||
new FileSpecificCodeSample(
|
||||
'<?php
|
||||
namespace PhpCsFixer\FIXER\Basic;
|
||||
class InvalidName {}
|
||||
',
|
||||
new \SplFileInfo(__FILE__)
|
||||
),
|
||||
new FileSpecificCodeSample(
|
||||
'<?php
|
||||
namespace PhpCsFixer\FIXER\Basic;
|
||||
class InvalidName {}
|
||||
',
|
||||
new \SplFileInfo(__FILE__),
|
||||
['dir' => './src']
|
||||
),
|
||||
],
|
||||
null,
|
||||
'This fixer may change your class name, which will break the code that depends on the old name.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
parent::configure($configuration);
|
||||
|
||||
if (null !== $this->configuration['dir']) {
|
||||
$realpath = realpath($this->configuration['dir']);
|
||||
|
||||
if (false === $realpath) {
|
||||
throw new \InvalidArgumentException(sprintf('Failed to resolve configured directory "%s".', $this->configuration['dir']));
|
||||
}
|
||||
|
||||
$this->configuration['dir'] = $realpath;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCandidate(Tokens $tokens): bool
|
||||
{
|
||||
return $tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRisky(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPriority(): int
|
||||
{
|
||||
return -10;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(\SplFileInfo $file): bool
|
||||
{
|
||||
if ($file instanceof StdinFileInfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
// ignore file with extension other than php
|
||||
('php' !== $file->getExtension())
|
||||
// ignore file with name that cannot be a class name
|
||||
|| 0 === Preg::match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $file->getBasename('.php'))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$tokens = Tokens::fromCode(sprintf('<?php class %s {}', $file->getBasename('.php')));
|
||||
|
||||
if ($tokens[3]->isKeyword() || $tokens[3]->isMagicConstant()) {
|
||||
// name cannot be a class name - detected by PHP 5.x
|
||||
return false;
|
||||
}
|
||||
} catch (\ParseError $e) {
|
||||
// name cannot be a class name - detected by PHP 7.x
|
||||
return false;
|
||||
}
|
||||
|
||||
// ignore stubs/fixtures, since they typically contain invalid files for various reasons
|
||||
return !Preg::match('{[/\\\\](stub|fixture)s?[/\\\\]}i', $file->getRealPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
|
||||
{
|
||||
return new FixerConfigurationResolver([
|
||||
(new FixerOptionBuilder('dir', 'If provided, the directory where the project code is placed.'))
|
||||
->setAllowedTypes(['null', 'string'])
|
||||
->setDefault(null)
|
||||
->getOption(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
|
||||
{
|
||||
$tokenAnalyzer = new TokensAnalyzer($tokens);
|
||||
|
||||
if (null !== $this->configuration['dir'] && !str_starts_with($file->getRealPath(), $this->configuration['dir'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$namespace = null;
|
||||
$namespaceStartIndex = null;
|
||||
$namespaceEndIndex = null;
|
||||
|
||||
$classyName = null;
|
||||
$classyIndex = null;
|
||||
|
||||
foreach ($tokens as $index => $token) {
|
||||
if ($token->isGivenKind(T_NAMESPACE)) {
|
||||
if (null !== $namespace) {
|
||||
return;
|
||||
}
|
||||
|
||||
$namespaceStartIndex = $tokens->getNextMeaningfulToken($index);
|
||||
$namespaceEndIndex = $tokens->getNextTokenOfKind($namespaceStartIndex, [';']);
|
||||
$namespace = trim($tokens->generatePartialCode($namespaceStartIndex, $namespaceEndIndex - 1));
|
||||
} elseif ($token->isClassy()) {
|
||||
if ($tokenAnalyzer->isAnonymousClass($index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (null !== $classyName) {
|
||||
return;
|
||||
}
|
||||
|
||||
$classyIndex = $tokens->getNextMeaningfulToken($index);
|
||||
$classyName = $tokens[$classyIndex]->getContent();
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $classyName) {
|
||||
return;
|
||||
}
|
||||
|
||||
$expectedClassyName = $this->calculateClassyName($file, $namespace, $classyName);
|
||||
|
||||
if ($classyName !== $expectedClassyName) {
|
||||
$tokens[$classyIndex] = new Token([T_STRING, $expectedClassyName]);
|
||||
}
|
||||
|
||||
if (null === $this->configuration['dir'] || null === $namespace) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_dir($this->configuration['dir'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$configuredDir = realpath($this->configuration['dir']);
|
||||
$fileDir = \dirname($file->getRealPath());
|
||||
|
||||
if (\strlen($configuredDir) >= \strlen($fileDir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$newNamespace = substr(str_replace('/', '\\', $fileDir), \strlen($configuredDir) + 1);
|
||||
$originalNamespace = substr($namespace, -\strlen($newNamespace));
|
||||
|
||||
if ($originalNamespace !== $newNamespace && strtolower($originalNamespace) === strtolower($newNamespace)) {
|
||||
$tokens->clearRange($namespaceStartIndex, $namespaceEndIndex);
|
||||
$namespace = substr($namespace, 0, -\strlen($newNamespace)).$newNamespace;
|
||||
|
||||
$newNamespace = Tokens::fromCode('<?php namespace '.$namespace.';');
|
||||
$newNamespace->clearRange(0, 2);
|
||||
$newNamespace->clearEmptyTokens();
|
||||
|
||||
$tokens->insertAt($namespaceStartIndex, $newNamespace);
|
||||
}
|
||||
}
|
||||
|
||||
private function calculateClassyName(\SplFileInfo $file, ?string $namespace, string $currentName): string
|
||||
{
|
||||
$name = $file->getBasename('.php');
|
||||
$maxNamespace = $this->calculateMaxNamespace($file, $namespace);
|
||||
|
||||
if (null !== $this->configuration['dir']) {
|
||||
return ('' !== $maxNamespace ? (str_replace('\\', '_', $maxNamespace).'_') : '').$name;
|
||||
}
|
||||
|
||||
$namespaceParts = array_reverse(explode('\\', $maxNamespace));
|
||||
|
||||
foreach ($namespaceParts as $namespacePart) {
|
||||
$nameCandidate = sprintf('%s_%s', $namespacePart, $name);
|
||||
|
||||
if (strtolower($nameCandidate) !== strtolower(substr($currentName, -\strlen($nameCandidate)))) {
|
||||
break;
|
||||
}
|
||||
|
||||
$name = $nameCandidate;
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
private function calculateMaxNamespace(\SplFileInfo $file, ?string $namespace): string
|
||||
{
|
||||
if (null === $this->configuration['dir']) {
|
||||
$root = \dirname($file->getRealPath());
|
||||
|
||||
while ($root !== \dirname($root)) {
|
||||
$root = \dirname($root);
|
||||
}
|
||||
} else {
|
||||
$root = realpath($this->configuration['dir']);
|
||||
}
|
||||
|
||||
$namespaceAccordingToFileLocation = trim(str_replace(\DIRECTORY_SEPARATOR, '\\', substr(\dirname($file->getRealPath()), \strlen($root))), '\\');
|
||||
|
||||
if (null === $namespace) {
|
||||
return $namespaceAccordingToFileLocation;
|
||||
}
|
||||
|
||||
$namespaceAccordingToFileLocationPartsReversed = array_reverse(explode('\\', $namespaceAccordingToFileLocation));
|
||||
$namespacePartsReversed = array_reverse(explode('\\', $namespace));
|
||||
|
||||
foreach ($namespacePartsReversed as $key => $namespaceParte) {
|
||||
if (!isset($namespaceAccordingToFileLocationPartsReversed[$key])) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (strtolower($namespaceParte) !== strtolower($namespaceAccordingToFileLocationPartsReversed[$key])) {
|
||||
break;
|
||||
}
|
||||
|
||||
unset($namespaceAccordingToFileLocationPartsReversed[$key]);
|
||||
}
|
||||
|
||||
return implode('\\', array_reverse($namespaceAccordingToFileLocationPartsReversed));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user