Skip to content

Commit

Permalink
Feature: Classes\PropertyBeforeMethod sniff
Browse files Browse the repository at this point in the history
Class properties should be defined before class methods.
  • Loading branch information
michalbundyra committed Oct 27, 2019
1 parent df93d48 commit 956cb53
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

declare(strict_types=1);

namespace WebimpressCodingStandard\Sniffs\Classes;

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

use function array_reverse;
use function key;

use const T_FUNCTION;
use const T_NS_SEPARATOR;
use const T_NULLABLE;
use const T_STATIC;
use const T_STRING;
use const T_VAR;

class PropertyBeforeMethodSniff extends AbstractVariableSniff
{
/**
* @param int $stackPtr
*/
protected function processMemberVar(File $phpcsFile, $stackPtr) : void
{
$tokens = $phpcsFile->getTokens();

$scopePtr = key(array_reverse($tokens[$stackPtr]['conditions'], true));
$scopeOpener = $tokens[$scopePtr]['scope_opener'];

$find = $phpcsFile->findNext(T_FUNCTION, $scopeOpener + 1, $stackPtr);
if ($find) {
$error = 'Method declaration forbidden in line %d before property declaration';

$fix = $phpcsFile->addFixableError($error, $stackPtr, 'BeforeProperty', [$tokens[$find]['line']]);
if ($fix) {
$before = $phpcsFile->findPrevious(
Tokens::$emptyTokens + Tokens::$methodPrefixes,
$find - 1,
null,
true
);

$from = $phpcsFile->findPrevious(
Tokens::$emptyTokens + Tokens::$scopeModifiers + [
T_VAR => T_VAR,
T_STATIC => T_STATIC,
// as of PHP 7.4 we can have defined types with properties
T_NS_SEPARATOR => T_NS_SEPARATOR,
T_STRING => T_STRING,
T_NULLABLE => T_NULLABLE,
],
$stackPtr - 1,
null,
true
);
$eos = $phpcsFile->findEndOfStatement($stackPtr);

$toMove = $phpcsFile->getTokensAsString($from + 1, $eos - $from);

$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->addContent($before, $toMove);
for ($i = $from + 1; $i <= $eos; ++$i) {
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}
}
}

/**
* @codeCoverageIgnore
*
* @param int $stackPtr
*/
protected function processVariable(File $phpcsFile, $stackPtr) : void
{
// Normal variables are not processed in this sniff.
}

/**
* @codeCoverageIgnore
*
* @param int $stackPtr
*/
protected function processVariableInString(File $phpcsFile, $stackPtr) : void
{
// Variables in string are not processed in this sniff.
}
}
59 changes: 59 additions & 0 deletions test/Sniffs/Classes/PropertyBeforeMethodUnitTest.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

class PropertyBeforeMethodClass
{
use MyTrait;
use MyOtherTrait {
func1 as func2;
}

/** method comment */
public function method()
{
}

/** constant comment */
const CONST_1 = 'const';

/** property comment **/
var $property1 = 'val';
}

trait PropertyBeforeMethodTrait
{
/** method1 comment */
public function method1() {}

/** method2 comment */
public function method2() {}

/** property2 comment */
public ?MyName\MyClass $property2;

/** property3 comment */
private int $property3 = 12;
}

$a = new class() {
/** method comment */
public function __construct()
{
}

/** property comment */
protected static $prop;
const CONST_3 = 'const';
};

$b = new class() {
/** method comment */
public function method()
{
}

/** property comment */
public static $prop;

/** const comment */
public const CONST_4 = 'const';
};
59 changes: 59 additions & 0 deletions test/Sniffs/Classes/PropertyBeforeMethodUnitTest.inc.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

class PropertyBeforeMethodClass
{
use MyTrait;
use MyOtherTrait {
func1 as func2;
}

/** property comment **/
var $property1 = 'val';

/** method comment */
public function method()
{
}

/** constant comment */
const CONST_1 = 'const';
}

trait PropertyBeforeMethodTrait
{

/** property2 comment */
public ?MyName\MyClass $property2;

/** property3 comment */
private int $property3 = 12;
/** method1 comment */
public function method1() {}

/** method2 comment */
public function method2() {}
}

$a = new class() {

/** property comment */
protected static $prop;
/** method comment */
public function __construct()
{
}
const CONST_3 = 'const';
};

$b = new class() {

/** property comment */
public static $prop;
/** method comment */
public function method()
{
}

/** const comment */
public const CONST_4 = 'const';
};
26 changes: 26 additions & 0 deletions test/Sniffs/Classes/PropertyBeforeMethodUnitTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace WebimpressCodingStandardTest\Sniffs\Classes;

use WebimpressCodingStandardTest\Sniffs\AbstractTestCase;

class PropertyBeforeMethodUnitTest extends AbstractTestCase
{
protected function getErrorList(string $testFile = '') : array
{
return [
19 => 1,
31 => 1,
34 => 1,
44 => 1,
55 => 1,
];
}

protected function getWarningList(string $testFile = '') : array
{
return [];
}
}

0 comments on commit 956cb53

Please sign in to comment.