-
Notifications
You must be signed in to change notification settings - Fork 159
/
Copy pathThrowCatchSniff.php
121 lines (100 loc) · 3.18 KB
/
ThrowCatchSniff.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
<?php
/**
* Copyright 2019 Adobe
* All Rights Reserved.
*/
declare(strict_types=1);
namespace Magento2\Sniffs\Exceptions;
use function array_slice;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
/**
* Detects exceptions must not be handled in same function
*/
class ThrowCatchSniff implements Sniff
{
/**
* String representation of warning.
*
* @var string
*/
protected $warningMessage = 'Exceptions must not be handled in the same function where they are thrown.';
/**
* Warning violation code.
*
* @var string
*/
protected $warningCode = 'ThrowCatch';
/**
* @inheritDoc
*/
public function register()
{
return [T_TRY];
}
/**
* @inheritDoc
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$endOfStatement = $phpcsFile->findEndOfStatement($stackPtr);
$throwClassNames = [];
$searchForNextThrow = $stackPtr;
// search for all throws
do {
$throwTag = $phpcsFile->findNext(T_THROW, $searchForNextThrow, $endOfStatement);
if ($throwTag === false) {
break;
}
$throwClassNames[] = $this->getFullClassName($tokens, $throwTag + 1);
$searchForNextThrow = $throwTag + 1;
} while ($throwTag !== false);
if (empty($throwClassNames)) {
return; // is not relevant not throw in try found.
}
$catchClassNames = [];
// TRY statements need to check until the end of all CATCH statements.
do {
$nextToken = $phpcsFile->findNext(T_WHITESPACE, ($endOfStatement + 1), null, true);
if ($tokens[$nextToken]['code'] === T_CATCH) {
$endOfStatement = $tokens[$nextToken]['scope_closer'];
$catchClassNames[$nextToken] = $this->getFullClassName($tokens, $nextToken + 1);
} else {
break;
}
} while (isset($tokens[$nextToken]['scope_closer']) === true);
if (empty($catchClassNames)) {
return; // is not relevant no catch found
}
$throwClassNames = array_flip($throwClassNames);
foreach ($catchClassNames as $match => $catchClassName) {
if (isset($throwClassNames[$catchClassName])) {
$phpcsFile->addWarning($this->warningMessage, $match, $this->warningCode);
}
}
}
/**
* Get the full class name with namespace.
*
* @param array $tokens
* @param int $startPos
* @return string
*/
private function getFullClassName(array $tokens, $startPos)
{
$fullName = "";
$endOfClassName = [T_SEMICOLON => 0, T_CLOSE_PARENTHESIS => 0];
$tokenCount = count($tokens);
for ($i = $startPos; $i <= $tokenCount; $i++) {
$type = $tokens[$i]['code'];
if ($type === T_STRING || $type === T_NS_SEPARATOR) {
$fullName .= $tokens[$i]['content'];
}
if (array_key_exists($type, $endOfClassName)) {
break; // line end each
}
}
return $fullName;
}
}