Skip to content
Draft
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
6 changes: 4 additions & 2 deletions library.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@
<exclude name="PSR12.Files.FileHeader"/>
<exclude name="PSR12.Files.OpenTag.NotAlone"/>
<exclude name="PSR12.ControlStructures.ControlStructureSpacing"/>
<exclude name="PSR2.Classes.PropertyDeclaration"/>
</rule>

<!-- 2.3 Lines -->
<!-- 2.4 Indenting -->

<!-- Code MUST use an indent of 4 spaces, and MUST NOT use tabs for indenting. -->
<rule ref="Generic.WhiteSpace.ScopeIndent">
<!-- Code MUST use an indent of 1 tab, and MUST NOT use spaces for indenting. -->
<rule ref="Stefna.WhiteSpace.ScopeIndent">
<properties>
<property name="ignoreIndentationTokens" type="array">
<element value="T_COMMENT"/>
Expand Down Expand Up @@ -78,6 +79,7 @@
<!---->
<!-- <rule ref="Stefna.Functions.ArgumentTrailingComma"/> -->
<!-- <rule ref="Stefna.Functions.MultiLineFunctionDeclaration"/> -->
<rule ref="Stefna.Classes.PropertyDeclaration"/>
<rule ref="Stefna.Functions.MultiLineFunctionDeclaration"/>
<rule ref="Stefna.Functions.BlankLines"/>
<rule ref="Stefna.Files.DeclareStrict"/>
Expand Down
2 changes: 2 additions & 0 deletions moya.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<rule ref="PSR2">
<exclude name="Generic.WhiteSpace.DisallowTabIndent"/>
<exclude name="Squiz.ControlStructures.ControlSignature.SpaceAfterCloseBrace"/>
<exclude name="PSR2.Classes.PropertyDeclaration"/>
</rule>
<rule ref="PSR1">
<exclude name="PSR1.Classes.ClassDeclaration.MissingNamespace"/>
Expand Down Expand Up @@ -63,6 +64,7 @@
<rule ref="Stefna.ControlStructures.ElseCatch"/>
<!-- needed special control structure check to not collide with ElseCatch -->
<rule ref="Stefna.ControlStructures.ControlSignature"/>
<rule ref="Stefna.Classes.PropertyDeclaration"/>

<config name="installed_paths" value="vendor/slevomat/coding-standard"/>
<rule ref="SlevomatCodingStandard.Namespaces.UnusedUses">
Expand Down
112 changes: 112 additions & 0 deletions src/Stefna/Sniffs/Classes/PropertyDeclarationSniff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php declare(strict_types=1);

namespace Stefna\Sniffs\Classes;

use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Standards\PSR2\Sniffs\Classes\PropertyDeclarationSniff as PropertyDeclarationSniffBase;
use Stefna\Utils\TokenCollection;

final class PropertyDeclarationSniff extends PropertyDeclarationSniffBase
{
protected function processMemberVar(File $phpcsFile, int $stackPtr): int|null
{
parent::processMemberVar($phpcsFile, $stackPtr);

$tokens = new TokenCollection($phpcsFile->getTokens());

$lastToken = $this->lastSymbolOnLine($phpcsFile, $stackPtr, $tokens);

if ($tokens->content($lastToken) === '{') {
$this->removeInvalidErrors($phpcsFile, $stackPtr, $tokens);
return $this->processHookVariable($phpcsFile, $stackPtr, $tokens);
}

return null;
}

private function lastSymbolOnLine(File $phpcsFile, int $stackPtr, TokenCollection $tokens): int
{
$currentLine = $tokens->line($stackPtr);
while ($tokens->line($stackPtr) == $currentLine) {
$stackPtr++;
}

return $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, exclude: true);
}

private function removeInvalidErrors(File $phpcsFile, int $stackPtr, TokenCollection $tokens): void
{
$removeError = function (int $line, int $column): void {
if ($this->errors[$line][$column][0]['source'] === 'Stefna.Classes.PropertyDeclaration.Multiple') {
unset($this->errors[$line][$column]);
if (empty($this->errors[$line])) {
unset($this->errors[$line]);
}

$this->errorCount -= 1;
}
};

$boundClosure = \Closure::bind($removeError, $phpcsFile, File::class);
$boundClosure($tokens->line($stackPtr), $tokens->column($stackPtr));
}

private function processHookVariable(File $phpcsFile, int $stackPtr, TokenCollection $tokens): int
{
$stackPtr = $this->lastSymbolOnLine($phpcsFile, $stackPtr, $tokens);
$hookScopeEnd = $tokens->bracketCloser($stackPtr);

while ($stackPtr < $hookScopeEnd) {
$stackPtr = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, exclude: true);

if ($tokens->code($stackPtr) !== T_STRING) {
if ($tokens->content($stackPtr) === '}') {
continue;
}

$error = 'Expected hook get/set; found %s';
$data = [
$tokens->content($stackPtr),
];
$phpcsFile->addError($error, $stackPtr, 'InvalidHook', $data);
}

$isSetHook = $tokens->content($stackPtr) === 'set';

$nextToken = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, exclude: true);

// Skip over parameter list
if ($isSetHook && $tokens->content($nextToken) === '(') {
$nextToken = $phpcsFile->findNext(T_WHITESPACE, $tokens->parenthesisCloser($nextToken) + 1, exclude: true);
}

if ($tokens->content($nextToken) === '{') {
if ($stackPtr === $nextToken - 1) {
$error = 'Expected 1 space between hook and "{"; found 0';
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterHook');
if ($fix) {
$phpcsFile->fixer->addContent($stackPtr, ' ');
}
}
elseif ($stackPtr === $nextToken - 2 && $tokens->code($stackPtr + 1) === T_WHITESPACE) {
$contentLength = strlen($tokens->content($stackPtr + 1));

if ($contentLength !== 1) {
$error = 'Expected 1 space between hook and "{"; found %d';
$data = [
$contentLength,
];
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterHook', $data);
if ($fix) {
$phpcsFile->fixer->replaceToken($stackPtr + 1, ' ');
}
}
}

$stackPtr = $phpcsFile->findNext(T_WHITESPACE, $tokens->bracketCloser($nextToken), exclude: true);
}
}

return $hookScopeEnd;
}
}
Loading