Skip to content
Merged
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
5 changes: 5 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
"email": "milan.pala@peckadesign.cz"
}
],
"autoload": {
"psr-4": {
"Pd\\CodingStandard\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"PdTests\\": "tests/"
Expand Down
61 changes: 52 additions & 9 deletions src/PeckaCodingStandardPSR12Based/ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

<!-- Arrays -->
<rule ref="SlevomatCodingStandard.Arrays.TrailingArrayComma"/>
<rule ref="SlevomatCodingStandard.Arrays.ArrayAccess"/>

<!-- Classes -->
<rule ref="Generic.Classes.DuplicateClassName"/>
Expand All @@ -48,19 +49,35 @@
<property name="linesCountBetweenMembers" value="2"/>
</properties>
</rule>

<!-- Namespaces -->
<rule ref="SlevomatCodingStandard.Namespaces.NamespaceSpacing">
<rule ref="SlevomatCodingStandard.Classes.BackedEnumTypeSpacing"/>
<rule ref="SlevomatCodingStandard.Classes.DisallowMultiConstantDefinition"/>
<rule ref="SlevomatCodingStandard.Classes.DisallowMultiPropertyDefinition"/>
<rule ref="SlevomatCodingStandard.Classes.EnumCaseSpacing">
<properties>
<property name="linesCountBeforeNamespace" value="1"/>
<property name="linesCountAfterNamespace" value="1"/>
<property name="minLinesCountBeforeWithComment" value="1"/>
<property name="maxLinesCountBeforeWithComment" value="1"/>
<property name="minLinesCountBeforeWithoutComment" value="0"/>
<property name="maxLinesCountBeforeWithoutComment" value="0"/>
</properties>
</rule>
<rule ref="SlevomatCodingStandard.Namespaces.UseSpacing">
<rule ref="SlevomatCodingStandard.Classes.ClassStructure">
<properties>
<property name="linesCountBeforeFirstUse" value="1"/>
<property name="linesCountBetweenUseTypes" value="1"/>
<property name="linesCountAfterLastUse" value="1"/>
<property name="groups" type="array">
<element value="uses"/>

<element value="enum cases"/>

<!-- Public constants are first, but you don't care about the order of protected or private constants -->
<element value="public constants"/>
<element value="constants"/>

<!-- You don't care about the order among the properties -->
<element value="properties"/>

<!-- Constructor is first, then methods -->
<element value="constructor"/>
<element value="methods"/>
</property>
</properties>
</rule>

Expand Down Expand Up @@ -89,20 +106,45 @@
<property name="allowMultiLine" value="true"/>
</properties>
</rule>
<rule ref="SlevomatCodingStandard.Functions.RequireTrailingCommaInDeclaration" />
<rule ref="SlevomatCodingStandard.Functions.RequireTrailingCommaInClosureUse"/>

<!-- Namespaces -->
<rule ref="SlevomatCodingStandard.Namespaces.NamespaceSpacing">
<properties>
<property name="linesCountBeforeNamespace" value="1"/>
<property name="linesCountAfterNamespace" value="1"/>
</properties>
</rule>
<rule ref="SlevomatCodingStandard.Namespaces.UseSpacing">
<properties>
<property name="linesCountBeforeFirstUse" value="1"/>
<property name="linesCountBetweenUseTypes" value="1"/>
<property name="linesCountAfterLastUse" value="1"/>
</properties>
</rule>
<rule ref="SlevomatCodingStandard.Namespaces.UnusedUses">
<properties>
<property name="searchAnnotations" value="1"/>
</properties>
</rule>
<rule ref="SlevomatCodingStandard.Namespaces.MultipleUsesPerLine"/>
<rule ref="SlevomatCodingStandard.Namespaces.UseDoesNotStartWithBackslash"/>
<rule ref="SlevomatCodingStandard.Namespaces.DisallowGroupUse"/>
<rule ref="SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly">
<properties>
<property name="allowFullyQualifiedNameForCollidingClasses" value="true"/>
<property name="allowFullyQualifiedNameForCollidingFunctions" value="true"/>
<property name="allowFullyQualifiedNameForCollidingConstants" value="true"/>
</properties>
</rule>
<rule ref="SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses"/>

<!-- PHP -->
<rule ref="Generic.PHP.DeprecatedFunctions"/>
<rule ref="Generic.PHP.ForbiddenFunctions"/>
<rule ref="Generic.PHP.CharacterBeforePHPOpeningTag"/>
<rule ref="SlevomatCodingStandard.PHP.TypeCast"/>

<!-- WhiteSpace -->
<rule ref="Generic.WhiteSpace.DisallowSpaceIndent"/>
Expand All @@ -114,6 +156,7 @@
<property name="spacing" value="1" />
</properties>
</rule>
<rule ref="../Sniffs/Formatting/SpaceBeforeNotSniff.php"/>

<!-- Variables -->
<rule ref="SlevomatCodingStandard.Variables.UnusedVariable">
Expand Down
125 changes: 125 additions & 0 deletions src/Sniffs/Formatting/SpaceBeforeNotSniff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?php declare(strict_types = 1);

namespace Pd\CodingStandard\Sniffs\Formatting;

use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;

use const T_BOOLEAN_NOT;
use const T_WHITESPACE;

class SpaceBeforeNotSniff implements Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array<string>
*/
public array $supportedTokenizers = [
'PHP',
'JS',
];

/**
* The number of spaces desired after the NOT operator.
*
*/
public int $spacing = 1;

/**
* Allow newlines instead of spaces.
*
*/
public bool $ignoreNewlines = false;


/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [T_BOOLEAN_NOT];
}


/**
* Processes this test, when one of its tokens is encountered.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$this->spacing = (int) $this->spacing;

$nextNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($nextNonEmpty === false) {
return;
}

if (
$this->ignoreNewlines === true
&& $tokens[$stackPtr]['line'] !== $tokens[$nextNonEmpty]['line']
) {
return;
}

if ($this->spacing === 0 && $nextNonEmpty === ($stackPtr + 1)) {
return;
}

$previousNonWhitespace = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
if ($nextNonEmpty !== $previousNonWhitespace) {
$error = 'Expected %s space(s) before NOT operator';
$data = [$this->spacing];
$phpcsFile->addError($error, $stackPtr, 'CommentFound', $data);

return;
}

$found = 0;
if ($tokens[$stackPtr]['line'] !== $tokens[$nextNonEmpty]['line']) {
$found = 'newline';
} elseif ($tokens[($stackPtr - 1)]['code'] === T_WHITESPACE) {
$found = $tokens[($stackPtr - 1)]['length'];
}

if ($found === $this->spacing) {
return;
}

$error = 'Expected %s space(s) before NOT operator; %s found';
$data = [
$this->spacing,
$found,
];

$fix = $phpcsFile->addFixableError($error, $stackPtr, 'Incorrect', $data);
if ($fix === true) {
$padding = str_repeat(' ', $this->spacing);
if ($found === 0) {
$phpcsFile->fixer->addContentBefore($stackPtr, $padding);
} else {
$phpcsFile->fixer->beginChangeset();
$start = ($stackPtr - 1);

if ($this->spacing > 0) {
$phpcsFile->fixer->replaceToken($start, $padding);
--$start;
}

for ($i = $start; $i < $previousNonWhitespace; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}

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