mirror of
https://github.com/elyby/php-code-style.git
synced 2025-05-31 14:12:05 +05:30
Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
84dd1184b8 | ||
|
7d36ce912c | ||
|
e9ad4a94ab | ||
|
6d572ae421 | ||
|
050dfe6399 | ||
|
b85d0d5c01 | ||
|
5ae374f6cd | ||
|
6956e0271e | ||
|
4a4f556d7b | ||
|
6e9d815a1f | ||
|
22dcb418fb | ||
|
2d215dc761 | ||
|
6f14beec28 |
17
CHANGELOG.md
17
CHANGELOG.md
@@ -6,6 +6,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.5.0] - 2023-04-08
|
||||||
|
### Added
|
||||||
|
- Enh #12: Implemented `Ely\align_multiline_parameters` fixer.
|
||||||
|
- Enh #13: Implemented `Ely\multiline_if_statement_braces` fixer.
|
||||||
|
- Enabled `Ely\align_multiline_parameters` for Ely.by codestyle in `['types' => false, 'defaults' => false]` mode.
|
||||||
|
- Enabled `Ely\multiline_if_statement_braces` for Ely.by codestyle in `['keep_on_own_line' => true]` mode.
|
||||||
|
- Enabled
|
||||||
|
[`PhpCsFixerCustomFixers/multiline_promoted_properties`](https://github.com/kubawerlos/php-cs-fixer-custom-fixers#multilinepromotedpropertiesfixer)
|
||||||
|
fixer for Ely.by codestyle in 2+ parameters mode.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Bug #10: `Ely/blank_line_before_return` don't treat interpolation curly bracket as beginning of the scope.
|
||||||
|
- Bug #9: `Ely/line_break_after_statements` add space before next meaningful line of code and skip comments.
|
||||||
|
|
||||||
## [0.4.0] - 2022-12-06
|
## [0.4.0] - 2022-12-06
|
||||||
### Added
|
### Added
|
||||||
- `simple_to_complex_string_variable` fixer.
|
- `simple_to_complex_string_variable` fixer.
|
||||||
@@ -135,7 +149,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||||||
### Added
|
### Added
|
||||||
- First release
|
- First release
|
||||||
|
|
||||||
[Unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/0.4.0...HEAD
|
[Unreleased]: https://github.com/elyby/php-code-style/compare/0.5.0...HEAD
|
||||||
|
[0.5.0]: https://github.com/elyby/php-code-style/compare/0.4.0...0.5.0
|
||||||
[0.4.0]: https://github.com/elyby/php-code-style/compare/0.3.0...0.4.0
|
[0.4.0]: https://github.com/elyby/php-code-style/compare/0.3.0...0.4.0
|
||||||
[0.3.0]: https://github.com/elyby/php-code-style/compare/0.2.1...0.3.0
|
[0.3.0]: https://github.com/elyby/php-code-style/compare/0.2.1...0.3.0
|
||||||
[0.2.1]: https://github.com/elyby/php-code-style/compare/0.2.0...0.2.1
|
[0.2.1]: https://github.com/elyby/php-code-style/compare/0.2.0...0.2.1
|
||||||
|
2
LICENSE
2
LICENSE
@@ -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 2018 Ely.by (http://ely.by)
|
Copyright 2023 Ely.by (http://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.
|
||||||
|
76
README.md
76
README.md
@@ -43,12 +43,14 @@ vendor/bin/php-cs-fixer fix
|
|||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
You can pass a custom set of rules to the `\Ely\CS\Config::create()` call. For example, it can be used to validate a
|
You can pass a custom set of rules to the `\Ely\CS\Config::create()` call. For example, it can be used to validate a
|
||||||
project with PHP 7.0 compatibility:
|
project with PHP 7.4 compatibility:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
return \Ely\CS\Config::create([
|
return \Ely\CS\Config::create([
|
||||||
'visibility_required' => ['property', 'method'],
|
'trailing_comma_in_multiline' => [
|
||||||
|
'elements' => ['arrays', 'arguments'],
|
||||||
|
],
|
||||||
])->setFinder($finder);
|
])->setFinder($finder);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -76,12 +78,15 @@ class Foo extends Bar implements FooInterface {
|
|||||||
private const SAMPLE_1 = 123;
|
private const SAMPLE_1 = 123;
|
||||||
private const SAMPLE_2 = 321;
|
private const SAMPLE_2 = 321;
|
||||||
|
|
||||||
public $field1;
|
public Typed $field1;
|
||||||
|
|
||||||
public $field2;
|
public $field2;
|
||||||
|
|
||||||
public function sampleFunction(int $a, int $b = null): array {
|
public function sampleFunction(
|
||||||
if ($a === $b) {
|
int $a,
|
||||||
|
private readonly int $b = null,
|
||||||
|
): array {
|
||||||
|
if ($a === $this->b) {
|
||||||
$result = bar();
|
$result = bar();
|
||||||
} else {
|
} else {
|
||||||
$result = BazClass::bar($this->field1, $this->field2);
|
$result = BazClass::bar($this->field1, $this->field2);
|
||||||
@@ -89,7 +94,7 @@ class Foo extends Bar implements FooInterface {
|
|||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setToNull(): self {
|
public function setToNull(): self {
|
||||||
$this->field1 = null;
|
$this->field1 = null;
|
||||||
return $this;
|
return $this;
|
||||||
@@ -154,6 +159,16 @@ class Foo extends Bar implements FooInterface {
|
|||||||
echo 'the next statement is here';
|
echo 'the next statement is here';
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* There MUST be no alignment around multiline function parameters.
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
function foo(
|
||||||
|
string $input,
|
||||||
|
int $key = 0,
|
||||||
|
): void {}
|
||||||
|
```
|
||||||
|
|
||||||
## Using our fixers
|
## Using our fixers
|
||||||
|
|
||||||
First of all, you must install Ely.by PHP-CS-Fixer package as described in the [installation chapter](#installation).
|
First of all, you must install Ely.by PHP-CS-Fixer package as described in the [installation chapter](#installation).
|
||||||
@@ -169,6 +184,31 @@ return \PhpCsFixer\Config::create()
|
|||||||
|
|
||||||
And then you'll be able to use our custom rules.
|
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`
|
### `Ely/blank_line_around_class_body`
|
||||||
|
|
||||||
Ensure that a class body contains one blank line after its definition and before its end:
|
Ensure that a class body contains one blank line after its definition and before its end:
|
||||||
@@ -246,6 +286,28 @@ and `do-while`.
|
|||||||
$c = 'next statement';
|
$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)
|
### `Ely/remove_class_name_method_usages` (Yii2)
|
||||||
|
|
||||||
Replaces Yii2 [`BaseObject::className()`](https://github.com/yiisoft/yii2/blob/e53fc0ded1/framework/base/BaseObject.php#L84)
|
Replaces Yii2 [`BaseObject::className()`](https://github.com/yiisoft/yii2/blob/e53fc0ded1/framework/base/BaseObject.php#L84)
|
||||||
@@ -265,7 +327,7 @@ usages with native `::class` keyword, introduced in PHP 5.5.
|
|||||||
[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/workflow/status/elyby/php-code-style/CI.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
|
||||||
|
|
||||||
[link-packagist]: https://packagist.org/packages/ely/php-code-style
|
[link-packagist]: https://packagist.org/packages/ely/php-code-style
|
||||||
[link-contributors]: ../../contributors
|
[link-contributors]: ../../contributors
|
||||||
|
@@ -20,7 +20,9 @@
|
|||||||
"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",
|
||||||
"friendsofphp/php-cs-fixer": "^3.13"
|
"friendsofphp/php-cs-fixer": "^3.13",
|
||||||
|
"kubawerlos/php-cs-fixer-custom-fixers": "^3.13",
|
||||||
|
"symfony/polyfill-php80": "^1.15"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"ergebnis/composer-normalize": "^2.28",
|
"ergebnis/composer-normalize": "^2.28",
|
||||||
|
62
composer.lock
generated
62
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "984ffa466fb6bc9281a36b8701147fee",
|
"content-hash": "8f031e395244dc0c0de0135b5c34ceda",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "composer/pcre",
|
"name": "composer/pcre",
|
||||||
@@ -462,6 +462,52 @@
|
|||||||
],
|
],
|
||||||
"time": "2022-10-31T19:28:50+00:00"
|
"time": "2022-10-31T19:28:50+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "kubawerlos/php-cs-fixer-custom-fixers",
|
||||||
|
"version": "v3.13.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers.git",
|
||||||
|
"reference": "fb53d8bbe2224383a84c71f451d76eb7bc6c8e33"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/fb53d8bbe2224383a84c71f451d76eb7bc6c8e33",
|
||||||
|
"reference": "fb53d8bbe2224383a84c71f451d76eb7bc6c8e33",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-filter": "*",
|
||||||
|
"ext-tokenizer": "*",
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.6.0",
|
||||||
|
"php": "^7.4 || ^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^9.5.20"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PhpCsFixerCustomFixers\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Kuba Werłos",
|
||||||
|
"email": "werlos@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A set of custom fixers for PHP CS Fixer",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/issues",
|
||||||
|
"source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.13.0"
|
||||||
|
},
|
||||||
|
"time": "2023-02-15T18:51:16+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "psr/cache",
|
"name": "psr/cache",
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@@ -1662,16 +1708,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php80",
|
"name": "symfony/polyfill-php80",
|
||||||
"version": "v1.26.0",
|
"version": "v1.27.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||||
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace"
|
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace",
|
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
|
||||||
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace",
|
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -1680,7 +1726,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.26-dev"
|
"dev-main": "1.27-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
@@ -1725,7 +1771,7 @@
|
|||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0"
|
"source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1741,7 +1787,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-05-10T07:21:04+00:00"
|
"time": "2022-11-03T14:55:06+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php81",
|
"name": "symfony/polyfill-php81",
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<phpunit
|
<phpunit
|
||||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||||
beStrictAboutChangesToGlobalState="true"
|
beStrictAboutChangesToGlobalState="true"
|
||||||
beStrictAboutOutputDuringTests="true"
|
beStrictAboutOutputDuringTests="true"
|
||||||
|
@@ -3,14 +3,18 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Ely\CS;
|
namespace Ely\CS;
|
||||||
|
|
||||||
|
use Ely\CS\Fixers as ElyFixers;
|
||||||
use PhpCsFixer\Config as PhpCsFixerConfig;
|
use PhpCsFixer\Config as PhpCsFixerConfig;
|
||||||
|
use PhpCsFixer\ConfigInterface as PhpCsFixerConfigInterface;
|
||||||
|
use PhpCsFixerCustomFixers\Fixers as KubawerlosFixers;
|
||||||
|
|
||||||
class Config {
|
class Config {
|
||||||
|
|
||||||
public static function create(array $overwrittenRules = []): PhpCsFixerConfig {
|
public static function create(array $overwrittenRules = []): PhpCsFixerConfigInterface {
|
||||||
return (new PhpCsFixerConfig())
|
return (new PhpCsFixerConfig())
|
||||||
->setRiskyAllowed(true)
|
->setRiskyAllowed(true)
|
||||||
->registerCustomFixers(new Fixers())
|
->registerCustomFixers(new ElyFixers())
|
||||||
|
->registerCustomFixers(new KubawerlosFixers())
|
||||||
->setRules(Rules::create($overwrittenRules));
|
->setRules(Rules::create($overwrittenRules));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
210
src/Fixer/FunctionNotation/AlignMultilineParametersFixer.php
Normal file
210
src/Fixer/FunctionNotation/AlignMultilineParametersFixer.php
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
<?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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -16,14 +16,26 @@ use PhpCsFixer\Preg;
|
|||||||
use PhpCsFixer\Tokenizer\Token;
|
use PhpCsFixer\Tokenizer\Token;
|
||||||
use PhpCsFixer\Tokenizer\Tokens;
|
use PhpCsFixer\Tokenizer\Tokens;
|
||||||
use PhpCsFixer\Tokenizer\TokensAnalyzer;
|
use PhpCsFixer\Tokenizer\TokensAnalyzer;
|
||||||
|
use SplFileInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is copy of the PR https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/3688
|
* 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.
|
||||||
*
|
*
|
||||||
* @author ErickSkrauch <erickskrauch@ely.by>
|
* 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 {
|
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 {
|
public function getDefinition(): FixerDefinitionInterface {
|
||||||
return new FixerDefinition(
|
return new FixerDefinition(
|
||||||
'Ensure that class body contains one blank line after class definition and before its end.',
|
'Ensure that class body contains one blank line after class definition and before its end.',
|
||||||
@@ -48,7 +60,7 @@ new class extends Foo {
|
|||||||
|
|
||||||
};
|
};
|
||||||
',
|
',
|
||||||
['apply_to_anonymous_classes' => false],
|
[self::C_APPLY_TO_ANONYMOUS_CLASSES => false],
|
||||||
),
|
),
|
||||||
new CodeSample(
|
new CodeSample(
|
||||||
'<?php
|
'<?php
|
||||||
@@ -58,74 +70,75 @@ new class extends Foo {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
',
|
',
|
||||||
['apply_to_anonymous_classes' => true],
|
[self::C_APPLY_TO_ANONYMOUS_CLASSES => true],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPriority(): int {
|
|
||||||
// should be run after the BracesFixer
|
|
||||||
return -26;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isCandidate(Tokens $tokens): bool {
|
public function isCandidate(Tokens $tokens): bool {
|
||||||
return $tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds());
|
return $tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function applyFix(\SplFileInfo $file, Tokens $tokens): void {
|
protected function applyFix(SplFileInfo $file, Tokens $tokens): void {
|
||||||
$analyzer = new TokensAnalyzer($tokens);
|
$analyzer = new TokensAnalyzer($tokens);
|
||||||
|
/** @var Token $token */
|
||||||
foreach ($tokens as $index => $token) {
|
foreach ($tokens as $index => $token) {
|
||||||
if (!$token->isClassy()) {
|
if (!$token->isClassy()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$countLines = $this->configuration['blank_lines_count'];
|
$countLines = $this->configuration[self::C_BLANK_LINES_COUNT];
|
||||||
if (!$this->configuration['apply_to_anonymous_classes'] && $analyzer->isAnonymousClass($index)) {
|
if (!$this->configuration[self::C_APPLY_TO_ANONYMOUS_CLASSES] && $analyzer->isAnonymousClass($index)) {
|
||||||
$countLines = 0;
|
$countLines = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$startBraceIndex = $tokens->getNextTokenOfKind($index, ['{']);
|
$startBraceIndex = $tokens->getNextTokenOfKind($index, ['{']);
|
||||||
if ($tokens[$startBraceIndex + 1]->isWhitespace()) {
|
/** @var Token $nextAfterBraceToken */
|
||||||
|
$nextAfterBraceToken = $tokens[$startBraceIndex + 1];
|
||||||
|
if ($nextAfterBraceToken->isWhitespace()) {
|
||||||
$nextStatementIndex = $tokens->getNextMeaningfulToken($startBraceIndex);
|
$nextStatementIndex = $tokens->getNextMeaningfulToken($startBraceIndex);
|
||||||
// Traits should be placed right after a class opening brace,
|
/** @var Token $nextStatementToken */
|
||||||
if ($tokens[$nextStatementIndex]->getContent() !== 'use') {
|
$nextStatementToken = $tokens[$nextStatementIndex];
|
||||||
$this->fixBlankLines($tokens, $startBraceIndex + 1, $countLines);
|
// 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);
|
$endBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $startBraceIndex);
|
||||||
if ($tokens[$endBraceIndex - 1]->isWhitespace()) {
|
if ($tokens[$endBraceIndex - 1]->isWhitespace()) {
|
||||||
$this->fixBlankLines($tokens, $endBraceIndex - 1, $countLines);
|
$this->ensureBlankLines($tokens, $endBraceIndex - 1, $countLines);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface {
|
protected function createConfigurationDefinition(): FixerConfigurationResolverInterface {
|
||||||
return new FixerConfigurationResolver([
|
return new FixerConfigurationResolver([
|
||||||
(new FixerOptionBuilder('blank_lines_count', 'adjusts an amount of the blank lines.'))
|
(new FixerOptionBuilder(self::C_BLANK_LINES_COUNT, 'adjusts the number of blank lines.'))
|
||||||
->setAllowedTypes(['int'])
|
->setAllowedTypes(['int'])
|
||||||
->setDefault(1)
|
->setDefault(1)
|
||||||
->getOption(),
|
->getOption(),
|
||||||
(new FixerOptionBuilder('apply_to_anonymous_classes', 'whether this fixer should be applied to anonymous classes.'))
|
(new FixerOptionBuilder(self::C_APPLY_TO_ANONYMOUS_CLASSES, 'whether this fixer should be applied to anonymous classes.'))
|
||||||
->setAllowedTypes(['bool'])
|
->setAllowedTypes(['bool'])
|
||||||
->setDefault(true)
|
->setDefault(true)
|
||||||
->getOption(),
|
->getOption(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function fixBlankLines(Tokens $tokens, int $index, int $countLines): void {
|
private function ensureBlankLines(Tokens $tokens, int $index, int $countLines): void {
|
||||||
$content = $tokens[$index]->getContent();
|
$content = $tokens[$index]->getContent();
|
||||||
// Apply fix only in the case when the count lines do not equals to expected
|
// 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) {
|
if (substr_count($content, "\n") === $countLines + 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The final bit of the whitespace must be the next statement's indentation
|
// Use regexp to extract contents between line breaks
|
||||||
Preg::matchAll('/[^\n\r]+[\r\n]*/', $content, $matches);
|
Preg::matchAll('/[^\n\r]+[\r\n]*/', $content, $matches);
|
||||||
$lines = $matches[0];
|
$lines = $matches[0];
|
||||||
$eol = $this->whitespacesConfig->getLineEnding();
|
$eol = $this->whitespacesConfig->getLineEnding();
|
||||||
$tokens[$index] = new Token([T_WHITESPACE, str_repeat($eol, $countLines + 1) . end($lines)]);
|
$tokens->ensureWhitespaceAtIndex($index, 0, str_repeat($eol, $countLines + 1) . end($lines));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -67,8 +67,12 @@ final class BlankLineBeforeReturnFixer extends AbstractFixer implements Whitespa
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @var Token $backwardToken */
|
||||||
$backwardToken = $tokens[$backwardIndex];
|
$backwardToken = $tokens[$backwardIndex];
|
||||||
if ($backwardToken->getContent() === '{') {
|
/** @var Token $nextToken */
|
||||||
|
$nextToken = $tokens[$backwardIndex + 1];
|
||||||
|
// Exclude string interpolation: "str {$var}"
|
||||||
|
if ($backwardToken->getContent() === '{' && !$nextToken->isGivenKind(T_VARIABLE)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -26,7 +26,7 @@ final class LineBreakAfterStatementsFixer extends AbstractFixer implements White
|
|||||||
/**
|
/**
|
||||||
* There is no 'do', 'cause the processing of the 'while' also includes do {} while (); construction
|
* There is no 'do', 'cause the processing of the 'while' also includes do {} while (); construction
|
||||||
*/
|
*/
|
||||||
public const STATEMENTS = [
|
private const STATEMENTS = [
|
||||||
T_IF,
|
T_IF,
|
||||||
T_SWITCH,
|
T_SWITCH,
|
||||||
T_FOR,
|
T_FOR,
|
||||||
@@ -93,7 +93,7 @@ class Foo
|
|||||||
}
|
}
|
||||||
|
|
||||||
$endStatementIndex = $this->findStatementEnd($tokens, $index);
|
$endStatementIndex = $this->findStatementEnd($tokens, $index);
|
||||||
$nextStatementIndex = $tokens->getNextNonWhitespace($endStatementIndex);
|
$nextStatementIndex = $tokens->getNextMeaningfulToken($endStatementIndex);
|
||||||
if ($nextStatementIndex === null) {
|
if ($nextStatementIndex === null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
92
src/Fixer/Whitespace/MultilineIfStatementBracesFixer.php
Normal file
92
src/Fixer/Whitespace/MultilineIfStatementBracesFixer.php
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<?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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -213,12 +213,22 @@ class Rules {
|
|||||||
'space_multiple_catch' => 'none',
|
'space_multiple_catch' => 'none',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// kubawerlos fixers
|
||||||
|
'PhpCsFixerCustomFixers/multiline_promoted_properties' => [
|
||||||
|
'minimum_number_of_parameters' => 2,
|
||||||
|
],
|
||||||
|
|
||||||
// Our custom or extended fixers
|
// Our custom or extended fixers
|
||||||
|
'Ely/align_multiline_parameters' => [
|
||||||
|
'variables' => false,
|
||||||
|
'defaults' => false,
|
||||||
|
],
|
||||||
'Ely/blank_line_around_class_body' => [
|
'Ely/blank_line_around_class_body' => [
|
||||||
'apply_to_anonymous_classes' => false,
|
'apply_to_anonymous_classes' => false,
|
||||||
],
|
],
|
||||||
'Ely/blank_line_before_return' => true,
|
'Ely/blank_line_before_return' => true,
|
||||||
'Ely/line_break_after_statements' => true,
|
'Ely/line_break_after_statements' => true,
|
||||||
|
'Ely/multiline_if_statement_braces' => true,
|
||||||
'Ely/remove_class_name_method_usages' => true,
|
'Ely/remove_class_name_method_usages' => true,
|
||||||
], $overwrittenRules);
|
], $overwrittenRules);
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,351 @@
|
|||||||
|
<?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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -180,6 +180,20 @@ return $c;',
|
|||||||
}
|
}
|
||||||
',
|
',
|
||||||
];
|
];
|
||||||
|
yield [
|
||||||
|
'<?php
|
||||||
|
if ($condition) {
|
||||||
|
$a = "Interpolation {$var}.";
|
||||||
|
return true;
|
||||||
|
}',
|
||||||
|
];
|
||||||
|
yield [
|
||||||
|
'<?php
|
||||||
|
if ($condition) {
|
||||||
|
$a = "Deprecated interpolation ${var}.";
|
||||||
|
return true;
|
||||||
|
}',
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -575,6 +575,14 @@ class Foo
|
|||||||
}
|
}
|
||||||
}',
|
}',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
yield [
|
||||||
|
'<?php
|
||||||
|
do {
|
||||||
|
$a = 123;
|
||||||
|
} while ($value > 10); // comment here
|
||||||
|
',
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createFixer(): AbstractFixer {
|
protected function createFixer(): AbstractFixer {
|
||||||
|
@@ -0,0 +1,68 @@
|
|||||||
|
<?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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user