Skip to content

Commit

Permalink
Exceptions must be placed in configured directory
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin Hujer authored and VasekPurchart committed Sep 21, 2017
1 parent f7afec4 commit da42ccd
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 16 deletions.
28 changes: 28 additions & 0 deletions Consistence/Sniffs/Exceptions/ExceptionDeclarationSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ class ExceptionDeclarationSniff implements \PHP_CodeSniffer\Sniffs\Sniff

const CODE_NOT_ENDING_WITH_EXCEPTION = 'NotEndingWithException';
const CODE_NOT_CHAINABLE = 'NotChainable';
const CODE_INCORRECT_EXCEPTION_DIRECTORY = 'IncorrectExceptionDirectory';

/** @var string */
public $exceptionsDirectoryName = 'exceptions';

/**
* @return int[]
Expand Down Expand Up @@ -44,6 +48,8 @@ public function process(PhpCsFile $phpcsFile, $classPointer)

$this->checkExceptionName($phpcsFile, $classPointer);

$this->checkExceptionDirectoryName($phpcsFile, $classPointer);

$this->checkThatExceptionIsChainable($phpcsFile, $classPointer);
}

Expand All @@ -58,6 +64,28 @@ private function checkExceptionName(PhpCsFile $phpcsFile, int $classPointer)
}
}

private function checkExceptionDirectoryName(PhpCsFile $phpcsFile, int $classPointer)
{
$filename = $phpcsFile->getFilename();

// normalize path for Windows (PHP_CodeSniffer detects it with backslashes on Windows)
$filename = str_replace('\\', '/', $filename);

$pathInfo = pathinfo($filename);
$pathSegments = explode('/', $pathInfo['dirname']);

$exceptionDirectoryName = array_pop($pathSegments);

if ($exceptionDirectoryName !== $this->exceptionsDirectoryName) {
$phpcsFile->addError(sprintf(
'Exception file "%s" must be placed in "%s" directory (is in "%s").',
$pathInfo['basename'],
$this->exceptionsDirectoryName,
$exceptionDirectoryName
), $classPointer, self::CODE_INCORRECT_EXCEPTION_DIRECTORY);
}
}

private function checkThatExceptionIsChainable(PhpCsFile $phpcsFile, int $classPointer)
{
$constructorPointer = $this->findConstructorMethodPointer($phpcsFile, $classPointer);
Expand Down
110 changes: 95 additions & 15 deletions tests/Sniffs/Exceptions/ExceptionDeclarationSniffTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ class ExceptionDeclarationSniffTest extends \Consistence\Sniffs\TestCase

public function testInvalidExceptionName()
{
$resultFile = $this->checkFile(__DIR__ . '/data/InvalidExceptionName.php');
$resultFile = $this->checkFile(__DIR__ . '/data/InvalidExceptionName.php', [
'exceptionsDirectoryName' => 'data',
]);

$this->assertSniffError(
$resultFile,
Expand All @@ -21,28 +23,36 @@ public function testInvalidExceptionName()

public function testValidClassName()
{
$resultFile = $this->checkFile(__DIR__ . '/data/ValidNameException.php');
$resultFile = $this->checkFile(__DIR__ . '/data/ValidNameException.php', [
'exceptionsDirectoryName' => 'data',
]);

$this->assertNoSniffError($resultFile, 7);
}

public function testValidClassNameThatExtendsCustomException()
{
$resultFile = $this->checkFile(__DIR__ . '/data/ValidClassNameThatExtendsCustomException.php');
$resultFile = $this->checkFile(__DIR__ . '/data/ValidClassNameThatExtendsCustomException.php', [
'exceptionsDirectoryName' => 'data',
]);

$this->assertNoSniffError($resultFile, 7);
}

public function testAbstractExceptionWithValidNameException()
{
$resultFile = $this->checkFile(__DIR__ . '/data/AbstractExceptionWithValidNameException.php');
$resultFile = $this->checkFile(__DIR__ . '/data/AbstractExceptionWithValidNameException.php', [
'exceptionsDirectoryName' => 'data',
]);

$this->assertNoSniffError($resultFile, 7);
}

public function testAbstractClassWithInvalidExceptionName()
{
$resultFile = $this->checkFile(__DIR__ . '/data/AbstractExceptionWithInvalidName.php');
$resultFile = $this->checkFile(__DIR__ . '/data/AbstractExceptionWithInvalidName.php', [
'exceptionsDirectoryName' => 'data',
]);

$this->assertSniffError(
$resultFile,
Expand All @@ -54,42 +64,54 @@ public function testAbstractClassWithInvalidExceptionName()

public function testClassThatDoesNotExtendAnything()
{
$resultFile = $this->checkFile(__DIR__ . '/data/ClassThatDoesNotExtendAnything.php');
$resultFile = $this->checkFile(__DIR__ . '/data/ClassThatDoesNotExtendAnything.php', [
'exceptionsDirectoryName' => 'data',
]);

$this->assertNoSniffError($resultFile, 7);
}

public function testClassThatExtendsRegularClass()
{
$resultFile = $this->checkFile(__DIR__ . '/data/ClassThatDoesNotExtendException.php');
$resultFile = $this->checkFile(__DIR__ . '/data/ClassThatDoesNotExtendException.php', [
'exceptionsDirectoryName' => 'data',
]);

$this->assertNoSniffError($resultFile, 7);
}

public function testInterfaceThatDoesNotExtendAnything()
{
$resultFile = $this->checkFile(__DIR__ . '/data/InterfaceThatDoesNotExtendAnything.php');
$resultFile = $this->checkFile(__DIR__ . '/data/InterfaceThatDoesNotExtendAnything.php', [
'exceptionsDirectoryName' => 'data',
]);

$this->assertNoSniffError($resultFile, 7);
}

public function testInterfaceThatDoesNotExtendAnythingException()
{
$resultFile = $this->checkFile(__DIR__ . '/data/InterfaceThatDoesNotExtendAnythingException.php');
$resultFile = $this->checkFile(__DIR__ . '/data/InterfaceThatDoesNotExtendAnythingException.php', [
'exceptionsDirectoryName' => 'data',
]);

$this->assertNoSniffError($resultFile, 7);
}

public function testInterfaceThatExtendsException()
{
$resultFile = $this->checkFile(__DIR__ . '/data/InterfaceThatExtendsException.php');
$resultFile = $this->checkFile(__DIR__ . '/data/InterfaceThatExtendsException.php', [
'exceptionsDirectoryName' => 'data',
]);

$this->assertNoSniffError($resultFile, 7);
}

public function testInterfaceThatExtendsExceptionIncorrectName()
{
$resultFile = $this->checkFile(__DIR__ . '/data/InterfaceThatExtendsExceptionIncorrectName.php');
$resultFile = $this->checkFile(__DIR__ . '/data/InterfaceThatExtendsExceptionIncorrectName.php', [
'exceptionsDirectoryName' => 'data',
]);

$this->assertSniffError(
$resultFile,
Expand All @@ -101,7 +123,9 @@ public function testInterfaceThatExtendsExceptionIncorrectName()

public function testExceptionWithConstructorWithoutParametersIsNotChainable()
{
$resultFile = $this->checkFile(__DIR__ . '/data/ConstructWithoutParametersException.php');
$resultFile = $this->checkFile(__DIR__ . '/data/ConstructWithoutParametersException.php', [
'exceptionsDirectoryName' => 'data',
]);

$this->assertSniffError(
$resultFile,
Expand All @@ -113,14 +137,18 @@ public function testExceptionWithConstructorWithoutParametersIsNotChainable()

public function testExceptionWithChainableConstructorIsChainable()
{
$resultFile = $this->checkFile(__DIR__ . '/data/ChainableConstructorException.php');
$resultFile = $this->checkFile(__DIR__ . '/data/ChainableConstructorException.php', [
'exceptionsDirectoryName' => 'data',
]);

$this->assertNoSniffError($resultFile, 10);
}

public function testExceptionWithNonchainableConstructorIsNotChainable()
{
$resultFile = $this->checkFile(__DIR__ . '/data/NonChainableConstructorException.php');
$resultFile = $this->checkFile(__DIR__ . '/data/NonChainableConstructorException.php', [
'exceptionsDirectoryName' => 'data',
]);

$this->assertSniffError(
$resultFile,
Expand All @@ -132,7 +160,9 @@ public function testExceptionWithNonchainableConstructorIsNotChainable()

public function testExceptionWithConstructorWithoutParameterTypeHintIsNotChainable()
{
$resultFile = $this->checkFile(__DIR__ . '/data/NonChainableConstructorWithoutParameterTypehintException.php');
$resultFile = $this->checkFile(__DIR__ . '/data/NonChainableConstructorWithoutParameterTypehintException.php', [
'exceptionsDirectoryName' => 'data',
]);

$this->assertSniffError(
$resultFile,
Expand All @@ -142,4 +172,54 @@ public function testExceptionWithConstructorWithoutParameterTypeHintIsNotChainab
);
}

public function testExceptionIsPlacedInCorrectDirectory()
{
$resultFile = $this->checkFile(__DIR__ . '/data/ValidNameException.php', [
'exceptionsDirectoryName' => 'data',
]);

$this->assertNoSniffError($resultFile, 7);
}

/**
* @requires OS WIN
*/
public function testExceptionIsPlacedInCorrectDirectoryOnWindows()
{
// PHP_CodeSniffer detects the path with backslashes on Windows
$resultFile = $this->checkFile(__DIR__ . '\data\ValidNameException.php', [
'exceptionsDirectoryName' => 'data',
]);

$this->assertNoSniffError($resultFile, 7);
}

public function testExceptionIsPlacedInIncorrectDirectory()
{
$resultFile = $this->checkFile(__DIR__ . '/data/ValidNameException.php', [
'exceptionsDirectoryName' => 'exceptions',
]);

$this->assertSniffError(
$resultFile,
7,
ExceptionDeclarationSniff::CODE_INCORRECT_EXCEPTION_DIRECTORY,
'Exception file "ValidNameException.php" must be placed in "exceptions" directory (is in "data").'
);
}

public function testExceptionIsPlacedInIncorrectDirectoryCaseSensitively()
{
$resultFile = $this->checkFile(__DIR__ . '/data/ValidNameException.php', [
'exceptionsDirectoryName' => 'Data',
]);

$this->assertSniffError(
$resultFile,
7,
ExceptionDeclarationSniff::CODE_INCORRECT_EXCEPTION_DIRECTORY,
'Exception file "ValidNameException.php" must be placed in "Data" directory (is in "data").'
);
}

}
14 changes: 13 additions & 1 deletion tests/Sniffs/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,15 @@ public function __construct(string $name = null, array $data = [], string $dataN
parent::__construct($name, $data, $dataName);
}

protected function checkFile(string $filePath): PhpCsFile
/**
* @param string $filePath
* @param mixed[] $sniffProperties
* @return \PHP_CodeSniffer\Files\File
*/
protected function checkFile(
string $filePath,
array $sniffProperties = []
): PhpCsFile
{
if (!is_readable($filePath)) {
throw new \Exception(sprintf(
Expand All @@ -38,6 +46,10 @@ protected function checkFile(string $filePath): PhpCsFile

$codeSniffer->init();

if (count($sniffProperties) > 0) {
$codeSniffer->ruleset->ruleset[$this->getSniffName()]['properties'] = $sniffProperties;
}

$codeSniffer->ruleset->sniffs = [$this->getSniffClassName() => $this->getSniffClassName()];
$codeSniffer->ruleset->populateTokenListeners();

Expand Down

0 comments on commit da42ccd

Please sign in to comment.