33 * Copyright © Magento. All rights reserved.
44 * See COPYING.txt for license details.
55 */
6+
67namespace Magento2 \Sniffs \Exceptions ;
78
89use function array_slice ;
@@ -33,7 +34,7 @@ class ThrowCatchSniff implements Sniff
3334 */
3435 public function register ()
3536 {
36- return [T_FUNCTION , T_CLOSURE ];
37+ return [T_TRY ];
3738 }
3839
3940 /**
@@ -42,53 +43,78 @@ public function register()
4243 public function process (File $ phpcsFile , $ stackPtr )
4344 {
4445 $ tokens = $ phpcsFile ->getTokens ();
45- if (!isset ($ tokens [$ stackPtr ]['scope_closer ' ])) {
46- // Probably an interface method no check
47- return ;
48- }
46+ $ endOfStatement = $ phpcsFile ->findEndOfStatement ($ stackPtr );
4947
50- $ closeBrace = $ tokens [$ stackPtr ]['scope_closer ' ];
51- $ throwTags = [];
52- $ catchTags = [];
48+ $ throwClassNames = [];
49+ $ searchForNextThrow = $ stackPtr ;
5350
54- for ($ i = $ stackPtr ; $ i < $ closeBrace ; $ i ++) {
55- $ token = $ tokens [$ i ];
56- if ($ token ['code ' ] === T_CATCH ) {
57- $ catchTags [] = $ token ;
58- }
59- if ($ token ['code ' ] === T_THROW ) {
60- $ throwTags [] = $ i ;
51+ // search for all throws
52+ do {
53+ $ throwTag = $ phpcsFile ->findNext (T_THROW , $ searchForNextThrow , $ endOfStatement );
54+
55+ if ($ throwTag === false ) {
56+ break ;
6157 }
62- }
6358
64- if (count ($ catchTags ) === 0 || count ($ throwTags ) === 0 ) {
65- // No catch or throw found no check
66- return ;
59+ $ throwClassNames [] = $ this ->getFullClassName ($ tokens , $ throwTag + 1 );
60+
61+ $ searchForNextThrow = $ throwTag + 1 ;
62+ } while ($ throwTag !== false );
63+
64+ if (empty ($ throwClassNames )) {
65+ return ; // is not relevant not throw in try found.
6766 }
6867
6968 $ catchClassNames = [];
70- $ throwClassNames = [];
71-
72- // find all relevant classes in catch
73- foreach ($ catchTags as $ catchTag ) {
74- $ start = $ catchTag ['parenthesis_opener ' ];
75- $ end = $ catchTag ['parenthesis_closer ' ];
7669
77- $ match = $ phpcsFile ->findNext (T_STRING , $ start , $ end );
78- $ catchClassNames [$ match ] = $ tokens [$ match ]['content ' ];
79- }
70+ // TRY statements need to check until the end of all CATCH statements.
71+ do {
72+ $ nextToken = $ phpcsFile ->findNext (T_WHITESPACE , ($ endOfStatement + 1 ), null , true );
73+ if ($ tokens [$ nextToken ]['code ' ] === T_CATCH ) {
74+ $ endOfStatement = $ tokens [$ nextToken ]['scope_closer ' ];
75+ $ catchClassNames [$ nextToken ] = $ this ->getFullClassName ($ tokens , $ nextToken + 1 );
76+ } else {
77+ break ;
78+ }
79+ } while (isset ($ tokens [$ nextToken ]['scope_closer ' ]) === true );
8080
81- // find all relevant classes in throws
82- foreach ($ throwTags as $ throwTag ) {
83- $ match = $ phpcsFile ->findNext (T_STRING , $ throwTag );
84- $ throwClassNames [] = $ tokens [$ match ]['content ' ];
81+ if (empty ($ catchClassNames )) {
82+ return ; // is not relevant no catch found
8583 }
8684
8785 $ throwClassNames = array_flip ($ throwClassNames );
8886 foreach ($ catchClassNames as $ match => $ catchClassName ) {
89- if (array_key_exists ( $ catchClassName , $ throwClassNames )) {
87+ if (isset ( $ throwClassNames[ $ catchClassName ] )) {
9088 $ phpcsFile ->addWarning ($ this ->warningMessage , $ match , $ this ->warningCode );
9189 }
9290 }
9391 }
92+
93+ /**
94+ * Get the full class name with namespace.
95+ *
96+ * @param array $tokens
97+ * @param int $startPos
98+ * @return string
99+ */
100+ private function getFullClassName (array $ tokens , $ startPos )
101+ {
102+ $ fullName = "" ;
103+ $ endOfClassName = [T_SEMICOLON => 0 , T_CLOSE_PARENTHESIS => 0 ];
104+
105+ $ tokenCount = count ($ tokens );
106+ for ($ i = $ startPos ; $ i <= $ tokenCount ; $ i ++) {
107+ $ type = $ tokens [$ i ]['code ' ];
108+
109+ if ($ type === T_STRING || $ type === T_NS_SEPARATOR ) {
110+ $ fullName .= $ tokens [$ i ]['content ' ];
111+ }
112+
113+ if (array_key_exists ($ type , $ endOfClassName )) {
114+ break ; // line end each
115+ }
116+ }
117+
118+ return $ fullName ;
119+ }
94120}
0 commit comments