Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ Slevomat Coding Standard for [PHP_CodeSniffer](https://github.com/PHPCSStandards
- [SlevomatCodingStandard.ControlStructures.DisallowEmpty](doc/control-structures.md#slevomatcodingstandardcontrolstructuresdisallowempty)
- [SlevomatCodingStandard.ControlStructures.DisallowNullSafeObjectOperator](doc/control-structures.md#slevomatcodingstandardcontrolstructuresdisallownullsafeobjectoperator)
- [SlevomatCodingStandard.ControlStructures.DisallowShortTernaryOperator](doc/control-structures.md#slevomatcodingstandardcontrolstructuresdisallowshortternaryoperator-) 🔧
- [SlevomatCodingStandard.ControlStructures.DisallowTrailingCommaInMatchExpression](doc/control-structures.md#slevomatcodingstandardcontrolstructuresdisallowtrailingcommainmatchexpression-) 🔧
- [SlevomatCodingStandard.ControlStructures.DisallowTrailingMultiLineTernaryOperatorSniff](doc/control-structures.md#slevomatcodingstandardcontrolstructuresdisallowtrailingmultilineternaryoperator-) 🔧
- [SlevomatCodingStandard.ControlStructures.DisallowYodaComparison](doc/control-structures.md#slevomatcodingstandardcontrolstructuresdisallowyodacomparison-) 🔧
- [SlevomatCodingStandard.ControlStructures.EarlyExit](doc/control-structures.md#slevomatcodingstandardcontrolstructuresearlyexit-) 🔧
Expand All @@ -110,6 +111,7 @@ Slevomat Coding Standard for [PHP_CodeSniffer](https://github.com/PHPCSStandards
- [SlevomatCodingStandard.ControlStructures.RequireShortTernaryOperator](doc/control-structures.md#slevomatcodingstandardcontrolstructuresrequireshortternaryoperator-) 🔧
- [SlevomatCodingStandard.ControlStructures.RequireSingleLineCondition](doc/control-structures.md#slevomatcodingstandardcontrolstructuresrequiresinglelinecondition-) 🔧
- [SlevomatCodingStandard.ControlStructures.RequireTernaryOperator](doc/control-structures.md#slevomatcodingstandardcontrolstructuresrequireternaryoperator-) 🔧
- [SlevomatCodingStandard.ControlStructures.RequireTrailingCommaInMatchExpression](doc/control-structures.md#slevomatcodingstandardcontrolstructuresrequiretrailingcommainmatchexpression-) 🔧
- [SlevomatCodingStandard.ControlStructures.RequireYodaComparison](doc/control-structures.md#slevomatcodingstandardcontrolstructuresrequireyodacomparison-) 🔧
- [SlevomatCodingStandard.ControlStructures.UselessIfConditionWithReturn](doc/control-structures.md#slevomatcodingstandardcontrolstructuresuselessifconditionwithreturn-) 🔧
- [SlevomatCodingStandard.ControlStructures.UselessTernaryOperator](doc/control-structures.md#slevomatcodingstandardcontrolstructuresuselessternaryoperator-) 🔧
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php declare(strict_types = 1);

namespace SlevomatCodingStandard\Sniffs\ControlStructures;

use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use SlevomatCodingStandard\Helpers\FixerHelper;
use SlevomatCodingStandard\Helpers\TokenHelper;
use const T_COMMA;
use const T_MATCH;

class DisallowTrailingCommaInMatchExpressionSniff implements Sniff
{

public const CODE_DISALLOWED_TRAILING_COMMA = 'DisallowedTrailingComma';

public bool $onlySingleLine = false;

/**
* @return array<int, (int|string)>
*/
public function register(): array
{
return [
T_MATCH,
];
}

public function process(File $phpcsFile, int $stackPtr): void
{
$tokens = $phpcsFile->getTokens();

$scopeCloser = $tokens[$stackPtr]['scope_closer'];
$pointerBeforeScopeCloser = TokenHelper::findPreviousEffective($phpcsFile, $scopeCloser - 1);

if ($tokens[$pointerBeforeScopeCloser]['code'] !== T_COMMA) {
return;
}

if ($this->onlySingleLine && $tokens[$pointerBeforeScopeCloser]['line'] !== $tokens[$scopeCloser]['line']) {
return;
}

$fix = $phpcsFile->addFixableError(
'Trailing comma after the last branch of a match expression is disallowed.',
$pointerBeforeScopeCloser,
self::CODE_DISALLOWED_TRAILING_COMMA,
);

if (!$fix) {
return;
}

$phpcsFile->fixer->beginChangeset();
FixerHelper::replace($phpcsFile, $pointerBeforeScopeCloser, '');

if ($tokens[$pointerBeforeScopeCloser]['line'] === $tokens[$scopeCloser]['line']) {
FixerHelper::removeBetween($phpcsFile, $pointerBeforeScopeCloser, $scopeCloser);
}

$phpcsFile->fixer->endChangeset();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php declare(strict_types = 1);

namespace SlevomatCodingStandard\Sniffs\ControlStructures;

use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use SlevomatCodingStandard\Helpers\FixerHelper;
use SlevomatCodingStandard\Helpers\SniffSettingsHelper;
use SlevomatCodingStandard\Helpers\TokenHelper;
use const T_COMMA;
use const T_MATCH;

class RequireTrailingCommaInMatchExpressionSniff implements Sniff
{

public const CODE_MISSING_TRAILING_COMMA = 'MissingTrailingComma';

public ?bool $enable = null;

/**
* @return array<int, (int|string)>
*/
public function register(): array
{
return [
T_MATCH,
];
}

public function process(File $phpcsFile, int $stackPtr): void
{
$this->enable = SniffSettingsHelper::isEnabledByPhpVersion($this->enable, 80800);
if (!$this->enable) {
return;
}

$tokens = $phpcsFile->getTokens();
$scopeCloser = $tokens[$stackPtr]['scope_closer'];
$pointerBeforeScopeCloser = TokenHelper::findPreviousEffective($phpcsFile, $scopeCloser - 1);

if ($tokens[$scopeCloser]['line'] === $tokens[$pointerBeforeScopeCloser]['line']) {
return;
}

if ($tokens[$pointerBeforeScopeCloser]['code'] === T_COMMA) {
return;
}

$fix = $phpcsFile->addFixableError(
'Multi-line match expressions must have a trailing comma after the last branch.',
$pointerBeforeScopeCloser,
self::CODE_MISSING_TRAILING_COMMA,
);

if (!$fix) {
return;
}

$phpcsFile->fixer->beginChangeset();
FixerHelper::add($phpcsFile, $pointerBeforeScopeCloser, ',');
$phpcsFile->fixer->endChangeset();
}

}
18 changes: 18 additions & 0 deletions doc/control-structures.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,24 @@ $t = $someCondition
: $otherwiseThis;
```

#### SlevomatCodingStandard.ControlStructures.RequireTrailingCommaInMatchExpression 🔧

Commas after the last branch in match expression make adding a new parameter easier and result in a cleaner versioning diff.

This sniff enforces trailing commas in multi-line match expressions.

This sniff provides the following setting:

* `enable`: either to enable or not this sniff. By default, it is enabled for PHP versions 8.0 or higher.

#### SlevomatCodingStandard.ControlStructures.DisallowTrailingCommaInMatchExpression 🔧

This sniff disallows trailing commas on the last branch of a match expression.

This sniff provides the following setting:

* `onlySingleLine`: to enable checks only for single-line match expressions.

#### SlevomatCodingStandard.ControlStructures.JumpStatementsSpacing 🔧

Enforces configurable number of lines around jump statements (continue, return, ...).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php declare(strict_types = 1);

namespace SlevomatCodingStandard\Sniffs\ControlStructures;

use SlevomatCodingStandard\Sniffs\TestCase;

class DisallowTrailingCommaInMatchExpressionSniffTest extends TestCase
{

public function testNoErrors(): void
{
$report = self::checkFile(__DIR__ . '/data/disallowTrailingCommaInMatchExpressionNoErrors.php', [
'onlySingleLine' => false,
]);

self::assertNoSniffErrorInFile($report);
}

public function testErrors(): void
{
$report = self::checkFile(__DIR__ . '/data/disallowTrailingCommaInMatchExpressionErrors.php', [
'onlySingleLine' => false,
]);

self::assertSame(7, $report->getErrorCount());

self::assertSniffError($report, 3, DisallowTrailingCommaInMatchExpressionSniff::CODE_DISALLOWED_TRAILING_COMMA);
self::assertSniffError($report, 7, DisallowTrailingCommaInMatchExpressionSniff::CODE_DISALLOWED_TRAILING_COMMA);
self::assertSniffError($report, 14, DisallowTrailingCommaInMatchExpressionSniff::CODE_DISALLOWED_TRAILING_COMMA);
self::assertSniffError($report, 15, DisallowTrailingCommaInMatchExpressionSniff::CODE_DISALLOWED_TRAILING_COMMA);
self::assertSniffError($report, 22, DisallowTrailingCommaInMatchExpressionSniff::CODE_DISALLOWED_TRAILING_COMMA);
self::assertSniffError($report, 32, DisallowTrailingCommaInMatchExpressionSniff::CODE_DISALLOWED_TRAILING_COMMA);
self::assertSniffError($report, 33, DisallowTrailingCommaInMatchExpressionSniff::CODE_DISALLOWED_TRAILING_COMMA);

self::assertAllFixedInFile($report);
}

public function testWithOnlySingleLineEnabledErrors(): void
{
$report = self::checkFile(__DIR__ . '/data/disallowTrailingCommaInMatchExpressionWithOnlySingleLineEnabledErrors.php', [
'onlySingleLine' => true,
]);

self::assertSame(1, $report->getErrorCount());
self::assertSniffError($report, 3, DisallowTrailingCommaInMatchExpressionSniff::CODE_DISALLOWED_TRAILING_COMMA);
self::assertAllFixedInFile($report);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php declare(strict_types = 1);

namespace SlevomatCodingStandard\Sniffs\ControlStructures;

use SlevomatCodingStandard\Sniffs\TestCase;

class RequireTrailingCommaInMatchExpressionSniffTest extends TestCase
{

public function testNoErrors(): void
{
$report = self::checkFile(__DIR__ . '/data/requireTrailingCommaInMatchExpressionNoErrors.php', [
'enable' => true,
]);

self::assertNoSniffErrorInFile($report);
}

public function testErrors(): void
{
$report = self::checkFile(__DIR__ . '/data/requireTrailingCommaInMatchExpressionErrors.php', [
'enable' => true,
]);

self::assertSame(6, $report->getErrorCount());

self::assertSniffError($report, 5, RequireTrailingCommaInMatchExpressionSniff::CODE_MISSING_TRAILING_COMMA);
self::assertSniffError($report, 12, RequireTrailingCommaInMatchExpressionSniff::CODE_MISSING_TRAILING_COMMA);
self::assertSniffError($report, 13, RequireTrailingCommaInMatchExpressionSniff::CODE_MISSING_TRAILING_COMMA);
self::assertSniffError($report, 20, RequireTrailingCommaInMatchExpressionSniff::CODE_MISSING_TRAILING_COMMA);
self::assertSniffError($report, 30, RequireTrailingCommaInMatchExpressionSniff::CODE_MISSING_TRAILING_COMMA);
self::assertSniffError($report, 31, RequireTrailingCommaInMatchExpressionSniff::CODE_MISSING_TRAILING_COMMA);

self::assertAllFixedInFile($report);
}

public function testShouldNotReportIfSniffIsDisabled(): void
{
$report = self::checkFile(__DIR__ . '/data/requireTrailingCommaInMatchExpressionErrors.php', [
'enable' => false,
]);

self::assertNoSniffErrorInFile($report);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php // lint >= 8.0

$foo = match(rand(0, 1)) {0 => false, 1 => true};

$bar = match (rand(0, 1)) {
0 => false,
1 => true
};

$baz = match (rand(0, 1)) {
0 => false,
1 => match ($foo) {
false => 'foobar',
true => 'foobaz'
}
};

function foo(): bool
{
return match (rand(0, 1)) {
0 => false,
1 => true
};
}

function bar(): string
{
return match (rand(0, 1)) {
0 => false,
1 => match (foo()) {
false => 'foobar',
true => 'foobaz'
}
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php // lint >= 8.0

$foo = match(rand(0, 1)) {0 => false, 1 => true,};

$bar = match (rand(0, 1)) {
0 => false,
1 => true,
};

$baz = match (rand(0, 1)) {
0 => false,
1 => match ($foo) {
false => 'foobar',
true => 'foobaz',
},
};

function foo(): bool
{
return match (rand(0, 1)) {
0 => false,
1 => true,
};
}

function bar(): string
{
return match (rand(0, 1)) {
0 => false,
1 => match (foo()) {
false => 'foobar',
true => 'foobaz',
},
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php // lint >= 8.0

$foo = match (rand(0, 1)) {
0 => false,
1 => true
};

$bar = match (rand(0, 1)) {
0 => false,
1 => match($foo) {
false => 'foobar',
true => 'foobaz'
}
};

function foo(): bool
{
return match (rand(0, 1)) {
0 => false,
1 => true
};
}

function bar(): string
{
return match (rand(0, 1)) {
0 => false,
1 => match (foo()) {
false => 'foobar',
true => 'foobaz'
}
};
}
Loading