Skip to content

Commit

Permalink
Implement ExceptionsPunctuation (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
PedroTroller authored Aug 10, 2018
1 parent 58f5ee6 commit 37ad12b
Show file tree
Hide file tree
Showing 5 changed files with 361 additions and 1 deletion.
65 changes: 65 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,71 @@ return $config;
//
```

## PedroTroller/exceptions_punctuation

Exception messages MUST ends by ".", "…", "?" or "!".<br /><br /><i>Risky: will change the exception message.</i>

### Configuration

```php
// .php_cs.dist
<?php

$config = PhpCsFixer\Config::create()
// ...
->setRules([
// ...
'PedroTroller/exceptions_punctuation' => true,
// ...
])
// ...
->registerCustomFixers(new PedroTroller\CS\Fixer\Fixers())
;

return $config;
```

**OR** using my [rule list builder](doc/rule-set-factory.md).

```php
// .php_cs.dist
<?php

$config = PhpCsFixer\Config::create()
// ...
->setRules(PedroTroller\CS\Fixer\RuleSetFactory::create()
->enable('PedroTroller/exceptions_punctuation')
->getRules()
])
// ...
->registerCustomFixers(new PedroTroller\CS\Fixer\Fixers())
;

return $config;
```

### Fixes

```diff
--- Original // 80 chars
+++ New //
@@ @@ //
class MyClass { //
public function fun1() //
{ //
- throw new \Exception('This is the message'); //
+ throw new \Exception('This is the message.'); //
} //
//
public function fun2($data) //
{ //
- throw new LogicException(sprintf('This is the %s', 'message')); //
+ throw new LogicException(sprintf('This is the %s.', 'message')); //
} //
} //
//
```

## PedroTroller/forbidden_functions

Forbidden functions MUST BE commented
Expand Down
2 changes: 1 addition & 1 deletion bin/doc.twig
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ return $config;
## {{ fixer.name }}
{{ fixer.doc.summary}}
{{ fixer.doc.summary|raw }}
{% if fixer.deprecated %}
**DEPRECATED**
Expand Down
172 changes: 172 additions & 0 deletions src/PedroTroller/CS/Fixer/CodingStyle/ExceptionsPunctuationFixer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
<?php

declare(strict_types=1);

namespace PedroTroller\CS\Fixer\CodingStyle;

use PedroTroller\CS\Fixer\AbstractFixer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
use SplFileInfo;

final class ExceptionsPunctuationFixer extends AbstractFixer
{
/**
* {@inheritdoc}
*/
public function isCandidate(Tokens $tokens)
{
return $tokens->isTokenKindFound(T_THROW);
}

/**
* {@inheritdoc}
*/
public function isRisky()
{
return true;
}

/**
* {@inheritdoc}
*/
public function getSampleCode()
{
return <<<'PHP'
<?php
use LogicException;
class MyClass {
public function fun1()
{
throw new \Exception('This is the message');
}
public function fun2($data)
{
throw new LogicException(sprintf('This is the %s', 'message'));
}
}
PHP;
}

/**
* {@inheritdoc}
*/
public function getDocumentation()
{
return 'Exception messages MUST ends by ".", "…", "?" or "!".<br /><br /><i>Risky: will change the exception message.</i>';
}

protected function applyFix(SplFileInfo $file, Tokens $tokens)
{
$cases = $this->analyze($tokens)->findAllSequences([
[
[T_THROW],
[T_NEW],
[T_STRING],
'(',
[T_CONSTANT_ENCAPSED_STRING],
',',
],
[
[T_THROW],
[T_NEW],
[T_NS_SEPARATOR],
[T_STRING],
'(',
[T_CONSTANT_ENCAPSED_STRING],
',',
],
[
[T_THROW],
[T_NEW],
[T_STRING],
'(',
[T_STRING, 'sprintf'],
'(',
[T_CONSTANT_ENCAPSED_STRING],
',',
],
[
[T_THROW],
[T_NEW],
[T_NS_SEPARATOR],
[T_STRING],
'(',
[T_STRING, 'sprintf'],
'(',
[T_CONSTANT_ENCAPSED_STRING],
',',
],
[
[T_THROW],
[T_NEW],
[T_STRING],
'(',
[T_CONSTANT_ENCAPSED_STRING],
')',
],
[
[T_THROW],
[T_NEW],
[T_NS_SEPARATOR],
[T_STRING],
'(',
[T_CONSTANT_ENCAPSED_STRING],
')',
],
[
[T_THROW],
[T_NEW],
[T_STRING],
'(',
[T_STRING, 'sprintf'],
'(',
[T_CONSTANT_ENCAPSED_STRING],
')',
],
[
[T_THROW],
[T_NEW],
[T_NS_SEPARATOR],
[T_STRING],
'(',
[T_STRING, 'sprintf'],
'(',
[T_CONSTANT_ENCAPSED_STRING],
')',
],
]);

foreach ($cases as $case) {
$keys = array_keys($case);
array_pop($keys);
array_pop($case);
$tokens[end($keys)] = $this->cleanupMessage(end($case));
}
}

/**
* @return Token
*/
private function cleanupMessage(Token $token)
{
$content = $token->getContent();
$chars = str_split($content);
$quotes = array_shift($chars);
$quotes = array_pop($chars);
$ponctuation = end($chars);

switch ($ponctuation) {
case '.':
case '':
case '?':
case '!':
return $token;
}

return new Token([T_CONSTANT_ENCAPSED_STRING, sprintf('%s%s.%s', $quotes, implode($chars), $quotes)]);
}
}
26 changes: 26 additions & 0 deletions src/PedroTroller/CS/Fixer/TokensAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -406,4 +406,30 @@ public function getLineIndentation($index)

return end($parts);
}

/**
* @return array
*/
public function findAllSequences(array $seqs)
{
$sequences = [];

foreach ($seqs as $seq) {
$index = 0;

do {
$extract = $this->tokens->findSequence($seq, (int) $index);

if (null !== $extract) {
$keys = array_keys($extract);
$index = end($keys) + 1;
$sequences[reset($keys)] = $extract;
}
} while (null !== $extract);
}

ksort($sequences);

return $sequences;
}
}
97 changes: 97 additions & 0 deletions tests/UseCase/ExceptionsPunctuation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

declare(strict_types=1);

namespace tests\UseCase;

use PedroTroller\CS\Fixer\CodingStyle\ExceptionsPunctuationFixer;
use tests\UseCase;

final class ExceptionsPunctuation implements UseCase
{
/**
* {@inheritdoc}
*/
public function getFixer()
{
return new ExceptionsPunctuationFixer();
}

/**
* {@inheritdoc}
*/
public function getRawScript()
{
return <<<'PHP'
<?php
use LogicException;
use RuntimeException;
class MyClass {
public function fun1()
{
throw new \Exception('This is the message');
}
public function fun2($data)
{
throw new LogicException(sprintf('This is the %s', 'message'));
}
public function fun3($data)
{
throw new RuntimeException('This is the '.message);
}
public function fun4($data)
{
throw new RuntimeException('Are you sure ?');
}
}
PHP;
}

/**
* {@inheritdoc}
*/
public function getExpectation()
{
return <<<'PHP'
<?php
use LogicException;
use RuntimeException;
class MyClass {
public function fun1()
{
throw new \Exception('This is the message.');
}
public function fun2($data)
{
throw new LogicException(sprintf('This is the %s.', 'message'));
}
public function fun3($data)
{
throw new RuntimeException('This is the '.message);
}
public function fun4($data)
{
throw new RuntimeException('Are you sure ?');
}
}
PHP;
}

/**
* {@inheritdoc}
*/
public function getMinSupportedPhpVersion()
{
return 0;
}
}

0 comments on commit 37ad12b

Please sign in to comment.