Skip to content

Commit 37ad12b

Browse files
authored
Implement ExceptionsPunctuation (#41)
1 parent 58f5ee6 commit 37ad12b

File tree

5 files changed

+361
-1
lines changed

5 files changed

+361
-1
lines changed

README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,71 @@ return $config;
126126
//
127127
```
128128

129+
## PedroTroller/exceptions_punctuation
130+
131+
Exception messages MUST ends by ".", "…", "?" or "!".<br /><br /><i>Risky: will change the exception message.</i>
132+
133+
### Configuration
134+
135+
```php
136+
// .php_cs.dist
137+
<?php
138+
139+
$config = PhpCsFixer\Config::create()
140+
// ...
141+
->setRules([
142+
// ...
143+
'PedroTroller/exceptions_punctuation' => true,
144+
// ...
145+
])
146+
// ...
147+
->registerCustomFixers(new PedroTroller\CS\Fixer\Fixers())
148+
;
149+
150+
return $config;
151+
```
152+
153+
**OR** using my [rule list builder](doc/rule-set-factory.md).
154+
155+
```php
156+
// .php_cs.dist
157+
<?php
158+
159+
$config = PhpCsFixer\Config::create()
160+
// ...
161+
->setRules(PedroTroller\CS\Fixer\RuleSetFactory::create()
162+
->enable('PedroTroller/exceptions_punctuation')
163+
->getRules()
164+
])
165+
// ...
166+
->registerCustomFixers(new PedroTroller\CS\Fixer\Fixers())
167+
;
168+
169+
return $config;
170+
```
171+
172+
### Fixes
173+
174+
```diff
175+
--- Original // 80 chars
176+
+++ New //
177+
@@ @@ //
178+
class MyClass { //
179+
public function fun1() //
180+
{ //
181+
- throw new \Exception('This is the message'); //
182+
+ throw new \Exception('This is the message.'); //
183+
} //
184+
//
185+
public function fun2($data) //
186+
{ //
187+
- throw new LogicException(sprintf('This is the %s', 'message')); //
188+
+ throw new LogicException(sprintf('This is the %s.', 'message')); //
189+
} //
190+
} //
191+
//
192+
```
193+
129194
## PedroTroller/forbidden_functions
130195

131196
Forbidden functions MUST BE commented

bin/doc.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ return $config;
3131
3232
## {{ fixer.name }}
3333
34-
{{ fixer.doc.summary}}
34+
{{ fixer.doc.summary|raw }}
3535
3636
{% if fixer.deprecated %}
3737
**DEPRECATED**
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PedroTroller\CS\Fixer\CodingStyle;
6+
7+
use PedroTroller\CS\Fixer\AbstractFixer;
8+
use PhpCsFixer\Tokenizer\Token;
9+
use PhpCsFixer\Tokenizer\Tokens;
10+
use SplFileInfo;
11+
12+
final class ExceptionsPunctuationFixer extends AbstractFixer
13+
{
14+
/**
15+
* {@inheritdoc}
16+
*/
17+
public function isCandidate(Tokens $tokens)
18+
{
19+
return $tokens->isTokenKindFound(T_THROW);
20+
}
21+
22+
/**
23+
* {@inheritdoc}
24+
*/
25+
public function isRisky()
26+
{
27+
return true;
28+
}
29+
30+
/**
31+
* {@inheritdoc}
32+
*/
33+
public function getSampleCode()
34+
{
35+
return <<<'PHP'
36+
<?php
37+
38+
use LogicException;
39+
40+
class MyClass {
41+
public function fun1()
42+
{
43+
throw new \Exception('This is the message');
44+
}
45+
46+
public function fun2($data)
47+
{
48+
throw new LogicException(sprintf('This is the %s', 'message'));
49+
}
50+
}
51+
PHP;
52+
}
53+
54+
/**
55+
* {@inheritdoc}
56+
*/
57+
public function getDocumentation()
58+
{
59+
return 'Exception messages MUST ends by ".", "…", "?" or "!".<br /><br /><i>Risky: will change the exception message.</i>';
60+
}
61+
62+
protected function applyFix(SplFileInfo $file, Tokens $tokens)
63+
{
64+
$cases = $this->analyze($tokens)->findAllSequences([
65+
[
66+
[T_THROW],
67+
[T_NEW],
68+
[T_STRING],
69+
'(',
70+
[T_CONSTANT_ENCAPSED_STRING],
71+
',',
72+
],
73+
[
74+
[T_THROW],
75+
[T_NEW],
76+
[T_NS_SEPARATOR],
77+
[T_STRING],
78+
'(',
79+
[T_CONSTANT_ENCAPSED_STRING],
80+
',',
81+
],
82+
[
83+
[T_THROW],
84+
[T_NEW],
85+
[T_STRING],
86+
'(',
87+
[T_STRING, 'sprintf'],
88+
'(',
89+
[T_CONSTANT_ENCAPSED_STRING],
90+
',',
91+
],
92+
[
93+
[T_THROW],
94+
[T_NEW],
95+
[T_NS_SEPARATOR],
96+
[T_STRING],
97+
'(',
98+
[T_STRING, 'sprintf'],
99+
'(',
100+
[T_CONSTANT_ENCAPSED_STRING],
101+
',',
102+
],
103+
[
104+
[T_THROW],
105+
[T_NEW],
106+
[T_STRING],
107+
'(',
108+
[T_CONSTANT_ENCAPSED_STRING],
109+
')',
110+
],
111+
[
112+
[T_THROW],
113+
[T_NEW],
114+
[T_NS_SEPARATOR],
115+
[T_STRING],
116+
'(',
117+
[T_CONSTANT_ENCAPSED_STRING],
118+
')',
119+
],
120+
[
121+
[T_THROW],
122+
[T_NEW],
123+
[T_STRING],
124+
'(',
125+
[T_STRING, 'sprintf'],
126+
'(',
127+
[T_CONSTANT_ENCAPSED_STRING],
128+
')',
129+
],
130+
[
131+
[T_THROW],
132+
[T_NEW],
133+
[T_NS_SEPARATOR],
134+
[T_STRING],
135+
'(',
136+
[T_STRING, 'sprintf'],
137+
'(',
138+
[T_CONSTANT_ENCAPSED_STRING],
139+
')',
140+
],
141+
]);
142+
143+
foreach ($cases as $case) {
144+
$keys = array_keys($case);
145+
array_pop($keys);
146+
array_pop($case);
147+
$tokens[end($keys)] = $this->cleanupMessage(end($case));
148+
}
149+
}
150+
151+
/**
152+
* @return Token
153+
*/
154+
private function cleanupMessage(Token $token)
155+
{
156+
$content = $token->getContent();
157+
$chars = str_split($content);
158+
$quotes = array_shift($chars);
159+
$quotes = array_pop($chars);
160+
$ponctuation = end($chars);
161+
162+
switch ($ponctuation) {
163+
case '.':
164+
case '':
165+
case '?':
166+
case '!':
167+
return $token;
168+
}
169+
170+
return new Token([T_CONSTANT_ENCAPSED_STRING, sprintf('%s%s.%s', $quotes, implode($chars), $quotes)]);
171+
}
172+
}

src/PedroTroller/CS/Fixer/TokensAnalyzer.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,4 +406,30 @@ public function getLineIndentation($index)
406406

407407
return end($parts);
408408
}
409+
410+
/**
411+
* @return array
412+
*/
413+
public function findAllSequences(array $seqs)
414+
{
415+
$sequences = [];
416+
417+
foreach ($seqs as $seq) {
418+
$index = 0;
419+
420+
do {
421+
$extract = $this->tokens->findSequence($seq, (int) $index);
422+
423+
if (null !== $extract) {
424+
$keys = array_keys($extract);
425+
$index = end($keys) + 1;
426+
$sequences[reset($keys)] = $extract;
427+
}
428+
} while (null !== $extract);
429+
}
430+
431+
ksort($sequences);
432+
433+
return $sequences;
434+
}
409435
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace tests\UseCase;
6+
7+
use PedroTroller\CS\Fixer\CodingStyle\ExceptionsPunctuationFixer;
8+
use tests\UseCase;
9+
10+
final class ExceptionsPunctuation implements UseCase
11+
{
12+
/**
13+
* {@inheritdoc}
14+
*/
15+
public function getFixer()
16+
{
17+
return new ExceptionsPunctuationFixer();
18+
}
19+
20+
/**
21+
* {@inheritdoc}
22+
*/
23+
public function getRawScript()
24+
{
25+
return <<<'PHP'
26+
<?php
27+
28+
use LogicException;
29+
use RuntimeException;
30+
31+
class MyClass {
32+
public function fun1()
33+
{
34+
throw new \Exception('This is the message');
35+
}
36+
37+
public function fun2($data)
38+
{
39+
throw new LogicException(sprintf('This is the %s', 'message'));
40+
}
41+
42+
public function fun3($data)
43+
{
44+
throw new RuntimeException('This is the '.message);
45+
}
46+
47+
public function fun4($data)
48+
{
49+
throw new RuntimeException('Are you sure ?');
50+
}
51+
}
52+
PHP;
53+
}
54+
55+
/**
56+
* {@inheritdoc}
57+
*/
58+
public function getExpectation()
59+
{
60+
return <<<'PHP'
61+
<?php
62+
63+
use LogicException;
64+
use RuntimeException;
65+
66+
class MyClass {
67+
public function fun1()
68+
{
69+
throw new \Exception('This is the message.');
70+
}
71+
72+
public function fun2($data)
73+
{
74+
throw new LogicException(sprintf('This is the %s.', 'message'));
75+
}
76+
77+
public function fun3($data)
78+
{
79+
throw new RuntimeException('This is the '.message);
80+
}
81+
82+
public function fun4($data)
83+
{
84+
throw new RuntimeException('Are you sure ?');
85+
}
86+
}
87+
PHP;
88+
}
89+
90+
/**
91+
* {@inheritdoc}
92+
*/
93+
public function getMinSupportedPhpVersion()
94+
{
95+
return 0;
96+
}
97+
}

0 commit comments

Comments
 (0)