Resolves #16. Split repository and move all custom fixers to separate repository

This commit is contained in:
ErickSkrauch 2023-05-17 19:54:10 +02:00
parent 84dd1184b8
commit 62932ec0f7
No known key found for this signature in database
GPG Key ID: 669339FCBB30EE0E
24 changed files with 86 additions and 4967 deletions

View File

@ -49,10 +49,3 @@ jobs:
- name: PHP-CS-Fixer - name: PHP-CS-Fixer
if: matrix.php-versions == '8.1' if: matrix.php-versions == '8.1'
run: vendor/bin/php-cs-fixer fix --dry-run --format=checkstyle | cs2pr run: vendor/bin/php-cs-fixer fix --dry-run --format=checkstyle | cs2pr
# https://github.com/marketplace/actions/setup-php-action#phpunit
- name: Setup problem matchers for PHPUnit
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
- name: PHPUnit
run: vendor/bin/phpunit

1
.gitignore vendored
View File

@ -1,5 +1,4 @@
vendor vendor
dist dist
.phpunit.result.cache
.php-cs-fixer .php-cs-fixer
.php-cs-fixer.cache .php-cs-fixer.cache

View File

@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [Unreleased] ## [Unreleased]
### Removed
- Iss #16: All custom fixers have been moved to the [separate repository](https://github.com/erickskrauch/php-cs-fixer-custom-fixers)
## [0.5.0] - 2023-04-08 ## [0.5.0] - 2023-04-08
### Added ### Added

View File

@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier same "printed page" as the copyright notice for easier
identification within third-party archives. identification within third-party archives.
Copyright 2023 Ely.by (http://ely.by) Copyright 2023 Ely.by (https://ely.by)
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

157
README.md
View File

@ -1,7 +1,6 @@
# Ely.by PHP-CS-Fixer rules # Ely.by PHP-CS-Fixer rules
Set of PHP-CS-Fixer rules used in development of Ely.by PHP projects. It's suited for PHP 7.4 and above. Set of PHP-CS-Fixer rules used in development of Ely.by PHP projects. It's suited for PHP 7.4 and above.
You can use it as a ready-made set of rules or [just some of them](#using-our-fixers).
[![Latest Version on Packagist][ico-version]][link-packagist] [![Latest Version on Packagist][ico-version]][link-packagist]
[![Total Downloads][ico-downloads]][link-downloads] [![Total Downloads][ico-downloads]][link-downloads]
@ -169,167 +168,11 @@ class Foo extends Bar implements FooInterface {
): void {} ): void {}
``` ```
## Using our fixers
First of all, you must install Ely.by PHP-CS-Fixer package as described in the [installation chapter](#installation).
After that you can enable our custom fixers with `registerCustomFixers` method:
```php
<?php
// Your Finder configuration
return \PhpCsFixer\Config::create()
->registerCustomFixers(new \Ely\CS\Fixers());
```
And then you'll be able to use our custom rules.
### `Ely/align_multiline_parameters`
Forces aligned or not aligned multiline function parameters:
```diff
--- Original
+++ New
@@ @@
function foo(
string $string,
- int $index = 0,
- $arg = 'no type',
+ int $index = 0,
+ $arg = 'no type',
): void {}
```
**Configuration:**
* `variables` - when set to `true`, forces variables alignment. On `false` forces strictly no alignment.
You can set it to `null` to disable touching of variables. **Default**: `true`.
* `defaults` - when set to `true`, forces defaults alignment. On `false` forces strictly no alignment.
You can set it to `null` to disable touching of defaults. **Default**: `false`.
### `Ely/blank_line_around_class_body`
Ensure that a class body contains one blank line after its definition and before its end:
```diff
--- Original
+++ New
@@ @@
<?php
class Test {
+
public function func() {
$obj = new class extends Foo {
+
public $prop;
+
}
}
+
}
```
**Configuration:**
* `apply_to_anonymous_classes` - should this fixer be applied to anonymous classes? If it is set to `false`, than
anonymous classes will be fixed to don't have empty lines around body. **Default**: `true`.
* `blank_lines_count` - adjusts an amount of the blank lines. **Default**: `1`.
### `Ely/blank_line_before_return`
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.
```diff
--- Original
+++ New
@@ @@
<?php
public function foo() {
$a = 'this';
$b = 'is';
+
return "$a $b awesome";
}
public function bar() {
$this->foo();
return 'okay';
}
```
### `Ely/line_break_after_statements`
Ensures that there is one blank line above the next statements: `if`, `switch`, `for`, `foreach`, `while`
and `do-while`.
```diff
--- Original
+++ New
@@ @@
<?php
$a = 123;
if ($a === 123) {
// Do something here
}
+
$b = [1, 2, 3];
foreach ($b as $number) {
if ($number === 3) {
echo 'it is three!';
}
}
+
$c = 'next statement';
```
### `Ely/multiline_if_statement_braces`
Ensures that multiline if statement body curly brace placed on the right line.
```diff
--- Original
+++ New
@@ @@
<?php
if ($condition1 === 123
- && $condition2 = 321) {
+ && $condition2 = 321
+) {
// Do something here
}
```
**Configuration:**
* `keep_on_own_line` - should this place closing bracket on its own line? If it's set to `false`, than
curly bracket will be placed right after the last condition statement. **Default**: `true`.
### `Ely/remove_class_name_method_usages` (Yii2)
Replaces Yii2 [`BaseObject::className()`](https://github.com/yiisoft/yii2/blob/e53fc0ded1/framework/base/BaseObject.php#L84)
usages with native `::class` keyword, introduced in PHP 5.5.
```diff
--- Original
+++ New
@@ @@
<?php
use common\models\User;
- $className = User::className();
+ $className = User::class;
```
[ico-version]: https://img.shields.io/packagist/v/ely/php-code-style.svg?style=flat-square [ico-version]: https://img.shields.io/packagist/v/ely/php-code-style.svg?style=flat-square
[ico-license]: https://img.shields.io/badge/license-Apache-green.svg?style=flat-square [ico-license]: https://img.shields.io/badge/license-Apache-green.svg?style=flat-square
[ico-downloads]: https://img.shields.io/packagist/dt/ely/php-code-style.svg?style=flat-square [ico-downloads]: https://img.shields.io/packagist/dt/ely/php-code-style.svg?style=flat-square
[ico-build-status]: https://img.shields.io/github/actions/workflow/status/elyby/php-code-style/ci.yml?branch=master&style=flat-square [ico-build-status]: https://img.shields.io/github/actions/workflow/status/elyby/php-code-style/ci.yml?branch=master&style=flat-square
[link-packagist]: https://packagist.org/packages/ely/php-code-style [link-packagist]: https://packagist.org/packages/ely/php-code-style
[link-contributors]: ../../contributors
[link-downloads]: https://packagist.org/packages/ely/php-code-style/stats [link-downloads]: https://packagist.org/packages/ely/php-code-style/stats
[link-build-status]: https://github.com/elyby/php-code-style/actions [link-build-status]: https://github.com/elyby/php-code-style/actions

View File

@ -20,37 +20,26 @@
"homepage": "https://github.com/elyby/php-code-style", "homepage": "https://github.com/elyby/php-code-style",
"require": { "require": {
"php": "^7.4 || ^8.0", "php": "^7.4 || ^8.0",
"erickskrauch/php-cs-fixer-custom-fixers": "dev-master#62c2a014cb9a8cf7665c82040a5d19d88ef8e583",
"friendsofphp/php-cs-fixer": "^3.13", "friendsofphp/php-cs-fixer": "^3.13",
"kubawerlos/php-cs-fixer-custom-fixers": "^3.13", "kubawerlos/php-cs-fixer-custom-fixers": "^3.13",
"symfony/polyfill-php80": "^1.15" "symfony/polyfill-php80": "^1.15"
}, },
"require-dev": { "require-dev": {
"ergebnis/composer-normalize": "^2.28", "ergebnis/composer-normalize": "^2.28"
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2",
"phpspec/prophecy": "^1.15",
"phpspec/prophecy-phpunit": "^2.0",
"phpunit/phpunit": "^9.5",
"phpunitgoodpractices/polyfill": "^1.5",
"phpunitgoodpractices/traits": "^1.9.1",
"symfony/phpunit-bridge": "^6.0"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Ely\\CS\\": "src/" "Ely\\CS\\": "src/"
} }
}, },
"autoload-dev": {
"psr-4": {
"Ely\\CS\\Test\\": "tests/",
"PhpCsFixer\\Tests\\": "vendor/friendsofphp/php-cs-fixer/tests/"
}
},
"config": { "config": {
"allow-plugins": { "allow-plugins": {
"ergebnis/composer-normalize": true "ergebnis/composer-normalize": true
}, },
"preferred-install": { "preferred-install": {
"friendsofphp/php-cs-fixer": "source" "friendsofphp/php-cs-fixer": "source"
} },
"sort-packages": true
} }
} }

2301
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
beStrictAboutChangesToGlobalState="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
bootstrap="./vendor/autoload.php"
colors="true"
columns="max"
verbose="true"
>
<testsuites>
<testsuite name="all">
<directory>./tests</directory>
</testsuite>
</testsuites>
<coverage>
<include>
<directory>./src</directory>
</include>
</coverage>
<php>
<ini name="zend.enable_gc" value="0"/>
<ini name="memory_limit" value="1G"/>
</php>
</phpunit>

View File

@ -3,17 +3,17 @@ declare(strict_types=1);
namespace Ely\CS; namespace Ely\CS;
use Ely\CS\Fixers as ElyFixers; use ErickSkrauch\PhpCsFixer\Fixers as ErickSkrauchFixers;
use PhpCsFixer\Config as PhpCsFixerConfig; use PhpCsFixer\Config as PhpCsFixerConfig;
use PhpCsFixer\ConfigInterface as PhpCsFixerConfigInterface; use PhpCsFixer\ConfigInterface as PhpCsFixerConfigInterface;
use PhpCsFixerCustomFixers\Fixers as KubawerlosFixers; use PhpCsFixerCustomFixers\Fixers as KubawerlosFixers;
class Config { final class Config {
public static function create(array $overwrittenRules = []): PhpCsFixerConfigInterface { public static function create(array $overwrittenRules = []): PhpCsFixerConfigInterface {
return (new PhpCsFixerConfig()) return (new PhpCsFixerConfig())
->setRiskyAllowed(true) ->setRiskyAllowed(true)
->registerCustomFixers(new ElyFixers()) ->registerCustomFixers(new ErickSkrauchFixers())
->registerCustomFixers(new KubawerlosFixers()) ->registerCustomFixers(new KubawerlosFixers())
->setRules(Rules::create($overwrittenRules)); ->setRules(Rules::create($overwrittenRules));
} }

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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']));
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}

View File

@ -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));
}
}

View File

@ -3,7 +3,7 @@ declare(strict_types=1);
namespace Ely\CS; namespace Ely\CS;
class Rules { final class Rules {
public static function create(array $overwrittenRules = []): array { public static function create(array $overwrittenRules = []): array {
return array_merge([ return array_merge([
@ -219,17 +219,17 @@ class Rules {
], ],
// Our custom or extended fixers // Our custom or extended fixers
'Ely/align_multiline_parameters' => [ 'ErickSkrauch/align_multiline_parameters' => [
'variables' => false, 'variables' => false,
'defaults' => false, 'defaults' => false,
], ],
'Ely/blank_line_around_class_body' => [ 'ErickSkrauch/blank_line_around_class_body' => [
'apply_to_anonymous_classes' => false, 'apply_to_anonymous_classes' => false,
], ],
'Ely/blank_line_before_return' => true, 'ErickSkrauch/blank_line_before_return' => true,
'Ely/line_break_after_statements' => true, 'ErickSkrauch/line_break_after_statements' => true,
'Ely/multiline_if_statement_braces' => true, 'ErickSkrauch/multiline_if_statement_braces' => true,
'Ely/remove_class_name_method_usages' => true, 'ErickSkrauch/remove_class_name_method_usages' => true,
], $overwrittenRules); ], $overwrittenRules);
} }

View File

@ -1,351 +0,0 @@
<?php
declare(strict_types=1);
namespace Ely\CS\Test\Fixer\FunctionNotation;
use Ely\CS\Fixer\FunctionNotation\AlignMultilineParametersFixer;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
/**
* @covers \Ely\CS\Fixer\FunctionNotation\AlignMultilineParametersFixer
*/
final class AlignMultilineParametersFixerTest extends AbstractFixerTestCase {
/**
* @dataProvider provideTrueCases
*/
public function testBothTrue(string $expected, ?string $input = null): void {
$this->fixer->configure([
AlignMultilineParametersFixer::C_VARIABLES => true,
AlignMultilineParametersFixer::C_DEFAULTS => true,
]);
$this->doTest($expected, $input);
}
public function provideTrueCases(): iterable {
yield 'empty function' => [
'<?php
function test(): void {}
',
];
yield 'empty multiline function' => [
'<?php
function test(
): void {}
',
];
yield 'single line function' => [
'<?php
function test(string $a, int $b): void {}
',
];
yield 'single line fn' => [
'<?php
fn(string $a, int $b) => $b;
',
];
yield 'function, no defaults' => [
'<?php
function test(
string $a,
int $b
): void {}
',
'<?php
function test(
string $a,
int $b
): void {}
',
];
yield 'function, one has default' => [
'<?php
function test(
string $a,
int $b = 0
): void {}
',
'<?php
function test(
string $a,
int $b = 0
): void {}
',
];
yield 'function, one has no type' => [
'<?php
function test(
string $a,
$b
): void {}
',
'<?php
function test(
string $a,
$b
): void {}
',
];
yield 'function, one has no type, but has default' => [
'<?php
function test(
string $a,
$b = 0
): void {}
',
'<?php
function test(
string $a,
$b = 0
): void {}
',
];
yield 'function, no types at all' => [
'<?php
function test(
$string = "string",
$int = 0
): void {}
',
'<?php
function test(
$string = "string",
$int = 0
): void {}
',
];
yield 'function, defaults' => [
'<?php
function test(
string $string = "string",
int $int = 0
): void {}
',
'<?php
function test(
string $string = "string",
int $int = 0
): void {}
',
];
yield 'class method, defaults' => [
'<?php
class Test {
public function foo(
string $string = "string",
int $int = 0
): void {}
}
',
'<?php
class Test {
public function foo(
string $string = "string",
int $int = 0
): void {}
}
',
];
yield 'fn, defaults' => [
'<?php
fn(
string $string = "string",
int $int = 0
) => $int;
',
'<?php
fn(
string $string = "string",
int $int = 0
) => $int;
',
];
}
/**
* @dataProvider provideFalseCases
*/
public function testBothFalse(string $expected, ?string $input = null): void {
$this->fixer->configure([
AlignMultilineParametersFixer::C_VARIABLES => false,
AlignMultilineParametersFixer::C_DEFAULTS => false,
]);
$this->doTest($expected, $input);
}
public function provideFalseCases(): iterable {
foreach ($this->provideTrueCases() as $key => $case) {
if (isset($case[1])) {
yield $key => [$case[1], $case[0]];
} else {
yield $key => $case;
}
}
}
/**
* @dataProvider provideNullCases
*/
public function testBothNull(string $expected, ?string $input = null): void {
$this->fixer->configure([
AlignMultilineParametersFixer::C_VARIABLES => null,
AlignMultilineParametersFixer::C_DEFAULTS => null,
]);
$this->doTest($expected, $input);
}
public function provideNullCases(): iterable {
foreach ($this->provideFalseCases() as $key => $case) {
yield $key => [$case[0]];
}
}
/**
* @dataProvider provide80TrueCases
* @requires PHP 8.0
*/
public function test80BothTrue(string $expected, ?string $input = null): void {
$this->fixer->configure([
AlignMultilineParametersFixer::C_VARIABLES => true,
AlignMultilineParametersFixer::C_DEFAULTS => true,
]);
$this->doTest($expected, $input);
}
public function provide80TrueCases(): iterable {
yield 'constructor promotion, defaults' => [
'<?php
class Test {
public function __construct(
public string $string = "string",
protected bool $bool = true
) {}
}
',
'<?php
class Test {
public function __construct(
public string $string = "string",
protected bool $bool = true
) {}
}
',
];
}
/**
* @dataProvider provideFalse80Cases
* @requires PHP 8.0
*/
public function test80BothFalse(string $expected, ?string $input = null): void {
$this->fixer->configure([
AlignMultilineParametersFixer::C_VARIABLES => false,
AlignMultilineParametersFixer::C_DEFAULTS => false,
]);
$this->doTest($expected, $input);
}
public function provideFalse80Cases(): iterable {
foreach ($this->provide80TrueCases() as $key => $case) {
if (isset($case[1])) {
yield $key => [$case[1], $case[0]];
} else {
yield $key => $case;
}
}
}
/**
* @dataProvider provide81TrueCases
* @requires PHP 8.1
*/
public function test81BothTrue(string $expected, ?string $input = null): void {
$this->fixer->configure([
AlignMultilineParametersFixer::C_VARIABLES => true,
AlignMultilineParametersFixer::C_DEFAULTS => true,
]);
$this->doTest($expected, $input);
}
public function provide81TrueCases(): iterable {
yield 'constructor promotion, readonly, defaults' => [
'<?php
class Test {
public function __construct(
public readonly string $string = "string",
protected readonly bool $bool = true
) {}
}
',
'<?php
class Test {
public function __construct(
public readonly string $string = "string",
protected readonly bool $bool = true
) {}
}
',
];
yield 'partial constructor promotion, readonly, defaults' => [
'<?php
class Test {
public function __construct(
readonly string $string = "string",
int $int = 0,
protected bool $bool = true,
$float = 0.0,
) {}
}
',
'<?php
class Test {
public function __construct(
readonly string $string = "string",
int $int = 0,
protected bool $bool = true,
$float = 0.0,
) {}
}
',
];
}
/**
* @dataProvider provideFalse81Cases
* @requires PHP 8.1
*/
public function test81BothFalse(string $expected, ?string $input = null): void {
$this->fixer->configure([
AlignMultilineParametersFixer::C_VARIABLES => false,
AlignMultilineParametersFixer::C_DEFAULTS => false,
]);
$this->doTest($expected, $input);
}
public function provideFalse81Cases(): iterable {
foreach ($this->provide81TrueCases() as $key => $case) {
if (isset($case[1])) {
yield $key => [$case[1], $case[0]];
} else {
yield $key => $case;
}
}
}
protected function createFixer(): AbstractFixer {
return new AlignMultilineParametersFixer();
}
}

View File

@ -1,66 +0,0 @@
<?php
declare(strict_types=1);
namespace Ely\CS\Test\Fixer\LanguageConstruct;
use Ely\CS\Fixer\LanguageConstruct\RemoveClassNameMethodUsagesFixer;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
/**
* @covers \Ely\CS\Fixer\LanguageConstruct\RemoveClassNameMethodUsagesFixer
*/
class RemoveClassNameMethodUsagesFixerTest extends AbstractFixerTestCase {
/**
* @dataProvider provideFixCases
*/
public function testFix(string $expected, ?string $input = null): void {
$this->doTest($expected, $input);
}
public function provideFixCases(): iterable {
yield [
'<?php echo className();',
];
yield [
'<?php
use Foo\Bar\Baz;
$exceptionString = Baz::classname();
',
];
yield [
'<?php
use Foo\Bar\Baz;
$className = Baz::class;
',
'<?php
use Foo\Bar\Baz;
$className = Baz::className();
',
];
yield [
'<?php
use Foo\Bar\Baz;
$exceptionString = "The class should be instance of " . Baz::class . " and nothing else";
',
'<?php
use Foo\Bar\Baz;
$exceptionString = "The class should be instance of " . Baz::className() . " and nothing else";
',
];
}
protected function createFixer(): AbstractFixer {
return new RemoveClassNameMethodUsagesFixer();
}
}

View File

@ -1,340 +0,0 @@
<?php
declare(strict_types=1);
namespace Ely\CS\Test\Fixer\Whitespace;
use Ely\CS\Fixer\Whitespace\BlankLineAroundClassBodyFixer;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
use PhpCsFixer\WhitespacesFixerConfig;
/**
* @author ErickSkrauch <erickskrauch@ely.by>
*
* @covers \Ely\CS\Fixer\Whitespace\BlankLineAroundClassBodyFixer
*/
final class BlankLineAroundClassBodyFixerTest extends AbstractFixerTestCase {
private static $configurationDoNotApplyForAnonymousClasses = ['apply_to_anonymous_classes' => false];
private static $configurationTwoEmptyLines = ['blank_lines_count' => 2];
/**
* @dataProvider provideFixCases
*/
public function testFix(string $expected, ?string $input = null, array $configuration = null): void {
if ($configuration !== null) {
$this->fixer->configure($configuration);
}
$this->doTest($expected, $input);
}
public function provideFixCases(): iterable {
yield [
'<?php
class Good
{
public function firstMethod()
{
//code here
}
}',
'<?php
class Good
{
public function firstMethod()
{
//code here
}
}',
];
yield [
'<?php
class Good
{
/**
* Also blank line before DocBlock
*/
public function firstMethod()
{
//code here
}
}',
'<?php
class Good
{
/**
* Also blank line before DocBlock
*/
public function firstMethod()
{
//code here
}
}',
];
yield [
'<?php
class Good
{
/**
* Too many whitespaces
*/
public function firstMethod()
{
//code here
}
}',
'<?php
class Good
{
/**
* Too many whitespaces
*/
public function firstMethod()
{
//code here
}
}',
];
yield [
'<?php
interface Good
{
/**
* Also blank line before DocBlock
*/
public function firstMethod();
}',
'<?php
interface Good
{
/**
* Also blank line before DocBlock
*/
public function firstMethod();
}',
];
yield [
'<?php
trait Good
{
/**
* Also no blank line before DocBlock
*/
public function firstMethod() {}
}',
'<?php
trait Good
{
/**
* Also no blank line before DocBlock
*/
public function firstMethod() {}
}',
];
yield [
'<?php
class Good
{
use Foo\bar;
public function firstMethod()
{
//code here
}
}',
'<?php
class Good
{
use Foo\bar;
public function firstMethod()
{
//code here
}
}',
];
yield [
'<?php
class Good
{
use Foo\bar;
use Foo\baz;
public function firstMethod()
{
//code here
}
}',
'<?php
class Good
{
use Foo\bar;
use Foo\baz;
public function firstMethod()
{
//code here
}
}',
];
yield [
'<?php
class Good
{
use Foo, Bar {
Bar::smallTalk insteadof A;
Foo::bigTalk insteadof B;
}
public function firstMethod()
{
//code here
}
}',
'<?php
class Good
{
use Foo, Bar {
Bar::smallTalk insteadof A;
Foo::bigTalk insteadof B;
}
public function firstMethod()
{
//code here
}
}',
];
yield [
'<?php
class Good
{
public function firstMethod()
{
//code here
}
}',
'<?php
class Good
{
public function firstMethod()
{
//code here
}
}',
self::$configurationTwoEmptyLines,
];
// check if some fancy whitespaces aren't modified
yield [
'<?php
class Good
{public
function firstMethod()
{
//code here
}
}',
];
yield [
'<?php
$class = new class extends \Foo {
public $field;
public function firstMethod() {}
};',
'<?php
$class = new class extends \Foo {
public $field;
public function firstMethod() {}
};',
];
yield [
'<?php
$class = new class extends \Foo {
public $field;
public function firstMethod() {}
};',
'<?php
$class = new class extends \Foo {
public $field;
public function firstMethod() {}
};',
self::$configurationDoNotApplyForAnonymousClasses,
];
}
/**
* @dataProvider provideMessyWhitespacesCases
*/
public function testMessyWhitespaces(string $expected, ?string $input = null): void {
/** @var \PhpCsFixer\Fixer\WhitespacesAwareFixerInterface $fixer */
$fixer = $this->fixer;
$fixer->setWhitespacesConfig(new WhitespacesFixerConfig("\t", "\r\n"));
$this->doTest($expected, $input);
}
public function provideMessyWhitespacesCases(): iterable {
yield [
"<?php\nclass Foo\n{\r\n\r\n public function bar() {}\r\n\r\n}",
"<?php\nclass Foo\n{\n public function bar() {}\n}",
];
yield [
"<?php\nclass Foo\n{\r\n\r\n public function bar() {}\r\n\r\n}",
"<?php\nclass Foo\n{\r\n\r\n\n\n public function bar() {}\n\n\n\n}",
];
}
protected function createFixer(): AbstractFixer {
return new BlankLineAroundClassBodyFixer();
}
}

View File

@ -1,227 +0,0 @@
<?php
declare(strict_types=1);
namespace Ely\CS\Test\Fixer\Whitespace;
use Ely\CS\Fixer\Whitespace\BlankLineBeforeReturnFixer;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
use PhpCsFixer\WhitespacesFixerConfig;
/**
* Original file copied from:
* @url https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/5c5de791ab/tests/Fixer/Whitespace/BlankLineBeforeStatementFixerTest.php
*
* @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
* @author Andreas Möller <am@localheinz.com>
* @author SpacePossum
*
* @internal
*
* @property BlankLineBeforeReturnFixer $fixer
*
* @covers \Ely\CS\Fixer\Whitespace\BlankLineBeforeReturnFixer
*/
final class BlankLineBeforeReturnFixerTest extends AbstractFixerTestCase {
/**
* @dataProvider provideFixCases
*/
public function testFix(string $expected, ?string $input = null): void {
$this->doTest($expected, $input);
}
public function provideFixCases(): iterable {
yield [
'$a = $a;
return $a;
',
];
yield [
'<?php
$a = $a;
return $a;',
'<?php
$a = $a; return $a;',
];
yield [
'<?php
$b = $b;
return $b;',
'<?php
$b = $b;return $b;',
];
yield [
'<?php
$c = $c;
return $c;',
'<?php
$c = $c;
return $c;',
];
yield [
'<?php
$d = $d;
return $d;',
'<?php
$d = $d;
return $d;',
];
yield [
'<?php
if (true) {
return 1;
}',
];
yield [
'<?php
if (true)
return 1;
',
];
yield [
'<?php
if (true) {
return 1;
} else {
return 2;
}',
];
yield [
'<?php
if (true)
return 1;
else
return 2;
',
];
yield [
'<?php
if (true) {
return 1;
} elseif (false) {
return 2;
}',
];
yield [
'<?php
if (true)
return 1;
elseif (false)
return 2;
',
];
yield [
'<?php
throw new Exception("return true;");',
];
yield [
'<?php
function foo()
{
// comment
return "foo";
}',
];
yield [
'<?php
function foo()
{
// comment
return "bar";
}',
];
yield [
'<?php
function foo()
{
// comment
return "bar";
}',
];
yield [
'<?php
function foo() {
$a = "a";
$b = "b";
return $a . $b;
}',
'<?php
function foo() {
$a = "a";
$b = "b";
return $a . $b;
}',
];
yield [
'<?php
function foo() {
$b = "b";
return $a . $b;
}',
];
yield [
'<?php
function foo() {
$a = "a";
return $a . "hello";
}
function bar() {
$b = "b";
return $b . "hello";
}
',
];
yield [
'<?php
if ($condition) {
$a = "Interpolation {$var}.";
return true;
}',
];
yield [
'<?php
if ($condition) {
$a = "Deprecated interpolation ${var}.";
return true;
}',
];
}
/**
* @dataProvider provideMessyWhitespacesCases
*/
public function testMessyWhitespaces(string $expected, ?string $input = null): void {
$this->fixer->setWhitespacesConfig(new WhitespacesFixerConfig("\t", "\r\n"));
$this->doTest($expected, $input);
}
public function provideMessyWhitespacesCases(): iterable {
yield [
"<?php\r\n\$a = \$a;\r\n\r\nreturn \$a;",
"<?php\r\n\$a = \$a; return \$a;",
];
yield [
"<?php\r\n\$b = \$b;\r\n\r\nreturn \$b;",
"<?php\r\n\$b = \$b;return \$b;",
];
yield [
"<?php\r\n\$c = \$c;\r\n\r\nreturn \$c;",
"<?php\r\n\$c = \$c;\r\nreturn \$c;",
];
}
protected function createFixer(): AbstractFixer {
return new BlankLineBeforeReturnFixer();
}
}

View File

@ -1,592 +0,0 @@
<?php
declare(strict_types=1);
namespace Ely\CS\Test\Fixer\Whitespace;
use Ely\CS\Fixer\Whitespace\LineBreakAfterStatementsFixer;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
/**
* @covers \Ely\CS\Fixer\Whitespace\LineBreakAfterStatementsFixer
*
* @author ErickSkrauch <erickskrauch@ely.by>
*/
class LineBreakAfterStatementsFixerTest extends AbstractFixerTestCase {
/**
* @dataProvider provideFixCases
*/
public function testFix(string $expected, ?string $input = null): void {
$this->doTest($expected, $input);
}
public function provideFixCases(): iterable {
// Simple cases
yield [
'<?php
class Foo
{
public function foo()
{
if ("a" === "b") {
// code
}
$a = "next statement";
}
}',
'<?php
class Foo
{
public function foo()
{
if ("a" === "b") {
// code
}
$a = "next statement";
}
}',
];
yield [
'<?php
class Foo
{
public function foo()
{
if ("a" === "b") {
// code
} else {
// another code
}
$a = "next statement";
}
}',
'<?php
class Foo
{
public function foo()
{
if ("a" === "b") {
// code
} else {
// another code
}
$a = "next statement";
}
}',
];
yield [
'<?php
class Foo
{
public function foo()
{
for ($i = 0; $i < 3; $i++) {
// code
}
$a = "next statement";
}
}',
'<?php
class Foo
{
public function foo()
{
for ($i = 0; $i < 3; $i++) {
// code
}
$a = "next statement";
}
}',
];
yield [
'<?php
class Foo
{
public function foo()
{
foreach (["foo", "bar"] as $str) {
// code
}
$a = "next statement";
}
}',
'<?php
class Foo
{
public function foo()
{
foreach (["foo", "bar"] as $str) {
// code
}
$a = "next statement";
}
}',
];
yield [
'<?php
class Foo
{
public function foo()
{
while ($i < 10) {
// code
}
$a = "next statement";
}
}',
'<?php
class Foo
{
public function foo()
{
while ($i < 10) {
// code
}
$a = "next statement";
}
}',
];
yield [
'<?php
class Foo
{
public function foo()
{
do {
// code
} while ($i < 10);
$a = "next statement";
}
}',
'<?php
class Foo
{
public function foo()
{
do {
// code
} while ($i < 10);
$a = "next statement";
}
}',
];
yield [
'<?php
class Foo
{
public function foo()
{
switch ("str") {
case "a":
break;
case "b":
break;
default:
// code
}
$a = "next statement";
}
}',
'<?php
class Foo
{
public function foo()
{
switch ("str") {
case "a":
break;
case "b":
break;
default:
// code
}
$a = "next statement";
}
}',
];
// Extended cases
yield [
'<?php
class Foo
{
public function bar()
{
if ("a" === "b") {
// code
} else if ("a" === "c") {
// code
} else if ("a" === "d") {
// code
}
$a = "next statement";
}
}',
'<?php
class Foo
{
public function bar()
{
if ("a" === "b") {
// code
} else if ("a" === "c") {
// code
} else if ("a" === "d") {
// code
}
$a = "next statement";
}
}',
];
yield [
'<?php
class Foo
{
public function bar()
{
if ("a" === "b") {
// code
} elseif ("a" === "c") {
// code
} elseif ("a" === "d") {
// code
}
$a = "next statement";
}
}',
'<?php
class Foo
{
public function bar()
{
if ("a" === "b") {
// code
} elseif ("a" === "c") {
// code
} elseif ("a" === "d") {
// code
}
$a = "next statement";
}
}',
];
yield [
'<?php
class Foo
{
public function bar()
{
foreach (["foo", "bar"] as $str) {
if ($str === "foo") {
// code
}
}
}
}',
'<?php
class Foo
{
public function bar()
{
foreach (["foo", "bar"] as $str) {
if ($str === "foo") {
// code
}
}
}
}',
];
yield [
'<?php
class Foo
{
public function foo()
{
switch ("str") {
case "a": {
break;
}
case "b": {
break;
}
default: {
// code
}
}
$a = "next statement";
}
}',
'<?php
class Foo
{
public function foo()
{
switch ("str") {
case "a": {
break;
}
case "b": {
break;
}
default: {
// code
}
}
$a = "next statement";
}
}',
];
yield [
'<?php
$a = "prev statement";
foreach ($coordinates as $coordinate) {
$points = explode(",", $coordinate);
}
',
];
// Issue 5
yield [
'<?php
class Foo
{
public function foo()
{
if ("a" === "b")
$this->bar();
$a = "next statement";
}
}',
'<?php
class Foo
{
public function foo()
{
if ("a" === "b")
$this->bar();
$a = "next statement";
}
}',
];
yield [
'<?php
class Foo
{
public function foo()
{
if ("a" === "b")
$this->bar();
else
$this->baz();
$a = "next statement";
}
}',
'<?php
class Foo
{
public function foo()
{
if ("a" === "b")
$this->bar();
else
$this->baz();
$a = "next statement";
}
}',
];
yield [
'<?php
class Foo
{
public function foo()
{
for ($i = 0; $i < 3; $i++)
$this->bar();
$a = "next statement";
}
}',
'<?php
class Foo
{
public function foo()
{
for ($i = 0; $i < 3; $i++)
$this->bar();
$a = "next statement";
}
}',
];
yield [
'<?php
class Foo
{
public function foo()
{
foreach (["foo", "bar"] as $str)
$this->bar();
$a = "next statement";
}
}',
'<?php
class Foo
{
public function foo()
{
foreach (["foo", "bar"] as $str)
$this->bar();
$a = "next statement";
}
}',
];
yield [
'<?php
class Foo
{
public function foo()
{
while ($i < 10)
$this->bar();
$a = "next statement";
}
}',
'<?php
class Foo
{
public function foo()
{
while ($i < 10)
$this->bar();
$a = "next statement";
}
}',
];
yield [
'<?php
class Foo
{
public function foo()
{
do
$this->bar();
while ($i < 10);
$a = "next statement";
}
}',
'<?php
class Foo
{
public function foo()
{
do
$this->bar();
while ($i < 10);
$a = "next statement";
}
}',
];
yield [
'<?php
class Foo
{
public function bar()
{
if ("a" === "b")
$this->foo();
else if ("a" === "c")
$this->bar();
else if ("a" === "d")
$this->baz();
$a = "next statement";
}
}',
'<?php
class Foo
{
public function bar()
{
if ("a" === "b")
$this->foo();
else if ("a" === "c")
$this->bar();
else if ("a" === "d")
$this->baz();
$a = "next statement";
}
}',
];
yield [
'<?php
class Foo
{
public function bar()
{
foreach (["foo", "bar"] as $str)
if ($str === "foo")
$this->bar();
return 3;
}
}',
'<?php
class Foo
{
public function bar()
{
foreach (["foo", "bar"] as $str)
if ($str === "foo")
$this->bar();
return 3;
}
}',
];
yield [
'<?php
do {
$a = 123;
} while ($value > 10); // comment here
',
];
}
protected function createFixer(): AbstractFixer {
return new LineBreakAfterStatementsFixer();
}
}

View File

@ -1,68 +0,0 @@
<?php
declare(strict_types=1);
namespace Ely\CS\Test\Fixer\Whitespace;
use Ely\CS\Fixer\Whitespace\MultilineIfStatementBracesFixer;
use PhpCsFixer\AbstractFixer;
use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
/**
* @covers \Ely\CS\Fixer\Whitespace\MultilineIfStatementBracesFixer
*/
class MultilineIfStatementBracesFixerTest extends AbstractFixerTestCase {
/**
* @dataProvider provideFixCases
*/
public function testFixOnNewLine(string $expected, ?string $input = null): void {
$this->doTest($expected, $input);
}
public function provideFixCases(): iterable {
yield 'simple' => [
'<?php
if ($condition1
&& $condition2
) {}',
'<?php
if ($condition1
&& $condition2) {}',
];
yield 'nested' => [
'<?php
function foo() {
if ($condition1
&& $condition2
) {}
}',
'<?php
function foo() {
if ($condition1
&& $condition2) {}
}',
];
}
/**
* @dataProvider provideInvertedFixCases
*/
public function testFixOnSameLine(string $expected, ?string $input = null): void {
$this->fixer->configure([
MultilineIfStatementBracesFixer::C_KEEP_ON_OWN_LINE => false,
]);
$this->doTest($expected, $input);
}
public function provideInvertedFixCases(): iterable {
foreach ($this->provideFixCases() as $name => $case) {
yield $name => [$case[1], $case[0]];
}
}
protected function createFixer(): AbstractFixer {
return new MultilineIfStatementBracesFixer();
}
}