Chris@0
|
1 <?php
|
Chris@0
|
2 /**
|
Chris@17
|
3 * \Drupal\Sniffs\Semantics\FunctionCall.
|
Chris@0
|
4 *
|
Chris@0
|
5 * @category PHP
|
Chris@0
|
6 * @package PHP_CodeSniffer
|
Chris@0
|
7 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
8 */
|
Chris@0
|
9
|
Chris@17
|
10 namespace Drupal\Sniffs\Semantics;
|
Chris@17
|
11
|
Chris@17
|
12 use PHP_CodeSniffer\Files\File;
|
Chris@17
|
13 use PHP_CodeSniffer\Sniffs\Sniff;
|
Chris@17
|
14 use PHP_CodeSniffer\Util\Tokens;
|
Chris@17
|
15
|
Chris@0
|
16 /**
|
Chris@0
|
17 * Helper class to sniff for specific function calls.
|
Chris@0
|
18 *
|
Chris@0
|
19 * @category PHP
|
Chris@0
|
20 * @package PHP_CodeSniffer
|
Chris@0
|
21 * @link http://pear.php.net/package/PHP_CodeSniffer
|
Chris@0
|
22 */
|
Chris@17
|
23 abstract class FunctionCall implements Sniff
|
Chris@0
|
24 {
|
Chris@0
|
25
|
Chris@0
|
26 /**
|
Chris@0
|
27 * The currently processed file.
|
Chris@0
|
28 *
|
Chris@17
|
29 * @var \PHP_CodeSniffer\Files\File
|
Chris@0
|
30 */
|
Chris@0
|
31 protected $phpcsFile;
|
Chris@0
|
32
|
Chris@0
|
33 /**
|
Chris@0
|
34 * The token position of the function call.
|
Chris@0
|
35 *
|
Chris@0
|
36 * @var int
|
Chris@0
|
37 */
|
Chris@0
|
38 protected $functionCall;
|
Chris@0
|
39
|
Chris@0
|
40 /**
|
Chris@0
|
41 * The token position of the opening bracket of the function call.
|
Chris@0
|
42 *
|
Chris@0
|
43 * @var int
|
Chris@0
|
44 */
|
Chris@0
|
45 protected $openBracket;
|
Chris@0
|
46
|
Chris@0
|
47 /**
|
Chris@0
|
48 * The token position of the closing bracket of the function call.
|
Chris@0
|
49 *
|
Chris@0
|
50 * @var int
|
Chris@0
|
51 */
|
Chris@0
|
52 protected $closeBracket;
|
Chris@0
|
53
|
Chris@0
|
54 /**
|
Chris@0
|
55 * Internal cache to save the calculated arguments of the function call.
|
Chris@0
|
56 *
|
Chris@0
|
57 * @var array
|
Chris@0
|
58 */
|
Chris@0
|
59 protected $arguments;
|
Chris@0
|
60
|
Chris@0
|
61 /**
|
Chris@0
|
62 * Whether method invocations with the same function name should be processed,
|
Chris@0
|
63 * too.
|
Chris@0
|
64 *
|
Chris@0
|
65 * @var bool
|
Chris@0
|
66 */
|
Chris@0
|
67 protected $includeMethodCalls = false;
|
Chris@0
|
68
|
Chris@0
|
69
|
Chris@0
|
70 /**
|
Chris@0
|
71 * Returns an array of tokens this test wants to listen for.
|
Chris@0
|
72 *
|
Chris@0
|
73 * @return array
|
Chris@0
|
74 */
|
Chris@0
|
75 public function register()
|
Chris@0
|
76 {
|
Chris@0
|
77 return array(T_STRING);
|
Chris@0
|
78
|
Chris@0
|
79 }//end register()
|
Chris@0
|
80
|
Chris@0
|
81
|
Chris@0
|
82 /**
|
Chris@0
|
83 * Processes this test, when one of its tokens is encountered.
|
Chris@0
|
84 *
|
Chris@17
|
85 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
|
Chris@17
|
86 * @param int $stackPtr The position of the current token
|
Chris@17
|
87 * in the stack passed in $tokens.
|
Chris@0
|
88 *
|
Chris@0
|
89 * @return void
|
Chris@0
|
90 */
|
Chris@17
|
91 public function process(File $phpcsFile, $stackPtr)
|
Chris@0
|
92 {
|
Chris@0
|
93 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
94 $functionName = $tokens[$stackPtr]['content'];
|
Chris@0
|
95 if (in_array($functionName, $this->registerFunctionNames()) === false) {
|
Chris@0
|
96 // Not interested in this function.
|
Chris@0
|
97 return;
|
Chris@0
|
98 }
|
Chris@0
|
99
|
Chris@0
|
100 if ($this->isFunctionCall($phpcsFile, $stackPtr) === false) {
|
Chris@0
|
101 return;
|
Chris@0
|
102 }
|
Chris@0
|
103
|
Chris@0
|
104 // Find the next non-empty token.
|
Chris@17
|
105 $openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
Chris@0
|
106
|
Chris@0
|
107 $this->phpcsFile = $phpcsFile;
|
Chris@0
|
108 $this->functionCall = $stackPtr;
|
Chris@0
|
109 $this->openBracket = $openBracket;
|
Chris@0
|
110 $this->closeBracket = $tokens[$openBracket]['parenthesis_closer'];
|
Chris@0
|
111 $this->arguments = array();
|
Chris@0
|
112
|
Chris@0
|
113 $this->processFunctionCall($phpcsFile, $stackPtr, $openBracket, $this->closeBracket);
|
Chris@0
|
114
|
Chris@0
|
115 }//end process()
|
Chris@0
|
116
|
Chris@0
|
117
|
Chris@0
|
118 /**
|
Chris@0
|
119 * Checks if this is a function call.
|
Chris@0
|
120 *
|
Chris@17
|
121 * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
|
Chris@17
|
122 * @param int $stackPtr The position of the current token
|
Chris@17
|
123 * in the stack passed in $tokens.
|
Chris@0
|
124 *
|
Chris@0
|
125 * @return bool
|
Chris@0
|
126 */
|
Chris@17
|
127 protected function isFunctionCall(File $phpcsFile, $stackPtr)
|
Chris@0
|
128 {
|
Chris@0
|
129 $tokens = $phpcsFile->getTokens();
|
Chris@0
|
130 // Find the next non-empty token.
|
Chris@17
|
131 $openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
|
Chris@0
|
132
|
Chris@0
|
133 if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
|
Chris@0
|
134 // Not a function call.
|
Chris@0
|
135 return false;
|
Chris@0
|
136 }
|
Chris@0
|
137
|
Chris@0
|
138 if (isset($tokens[$openBracket]['parenthesis_closer']) === false) {
|
Chris@0
|
139 // Not a function call.
|
Chris@0
|
140 return false;
|
Chris@0
|
141 }
|
Chris@0
|
142
|
Chris@0
|
143 // Find the previous non-empty token.
|
Chris@17
|
144 $search = Tokens::$emptyTokens;
|
Chris@0
|
145 $search[] = T_BITWISE_AND;
|
Chris@0
|
146 $previous = $phpcsFile->findPrevious($search, ($stackPtr - 1), null, true);
|
Chris@0
|
147 if ($tokens[$previous]['code'] === T_FUNCTION) {
|
Chris@0
|
148 // It's a function definition, not a function call.
|
Chris@0
|
149 return false;
|
Chris@0
|
150 }
|
Chris@0
|
151
|
Chris@0
|
152 if ($tokens[$previous]['code'] === T_OBJECT_OPERATOR && $this->includeMethodCalls === false) {
|
Chris@0
|
153 // It's a method invocation, not a function call.
|
Chris@0
|
154 return false;
|
Chris@0
|
155 }
|
Chris@0
|
156
|
Chris@0
|
157 if ($tokens[$previous]['code'] === T_DOUBLE_COLON && $this->includeMethodCalls === false) {
|
Chris@0
|
158 // It's a static method invocation, not a function call.
|
Chris@0
|
159 return false;
|
Chris@0
|
160 }
|
Chris@0
|
161
|
Chris@0
|
162 return true;
|
Chris@0
|
163
|
Chris@0
|
164 }//end isFunctionCall()
|
Chris@0
|
165
|
Chris@0
|
166
|
Chris@0
|
167 /**
|
Chris@0
|
168 * Returns start and end token for a given argument number.
|
Chris@0
|
169 *
|
Chris@0
|
170 * @param int $number Indicates which argument should be examined, starting with
|
Chris@0
|
171 * 1 for the first argument.
|
Chris@0
|
172 *
|
Chris@0
|
173 * @return array(string => int)
|
Chris@0
|
174 */
|
Chris@0
|
175 public function getArgument($number)
|
Chris@0
|
176 {
|
Chris@0
|
177 // Check if we already calculated the tokens for this argument.
|
Chris@0
|
178 if (isset($this->arguments[$number]) === true) {
|
Chris@0
|
179 return $this->arguments[$number];
|
Chris@0
|
180 }
|
Chris@0
|
181
|
Chris@0
|
182 $tokens = $this->phpcsFile->getTokens();
|
Chris@0
|
183 // Start token of the first argument.
|
Chris@17
|
184 $start = $this->phpcsFile->findNext(Tokens::$emptyTokens, ($this->openBracket + 1), null, true);
|
Chris@0
|
185 if ($start === $this->closeBracket) {
|
Chris@0
|
186 // Function call has no arguments, so return false.
|
Chris@0
|
187 return false;
|
Chris@0
|
188 }
|
Chris@0
|
189
|
Chris@0
|
190 // End token of the last argument.
|
Chris@17
|
191 $end = $this->phpcsFile->findPrevious(Tokens::$emptyTokens, ($this->closeBracket - 1), null, true);
|
Chris@0
|
192 $lastArgEnd = $end;
|
Chris@0
|
193 $nextSeperator = $this->openBracket;
|
Chris@0
|
194 $counter = 1;
|
Chris@0
|
195 while (($nextSeperator = $this->phpcsFile->findNext(T_COMMA, ($nextSeperator + 1), $this->closeBracket)) !== false) {
|
Chris@0
|
196 // Make sure the comma belongs directly to this function call,
|
Chris@0
|
197 // and is not inside a nested function call or array.
|
Chris@0
|
198 $brackets = $tokens[$nextSeperator]['nested_parenthesis'];
|
Chris@0
|
199 $lastBracket = array_pop($brackets);
|
Chris@0
|
200 if ($lastBracket !== $this->closeBracket) {
|
Chris@0
|
201 continue;
|
Chris@0
|
202 }
|
Chris@0
|
203
|
Chris@0
|
204 // Update the end token of the current argument.
|
Chris@17
|
205 $end = $this->phpcsFile->findPrevious(Tokens::$emptyTokens, ($nextSeperator - 1), null, true);
|
Chris@0
|
206 // Save the calculated findings for the current argument.
|
Chris@0
|
207 $this->arguments[$counter] = array(
|
Chris@0
|
208 'start' => $start,
|
Chris@0
|
209 'end' => $end,
|
Chris@0
|
210 );
|
Chris@0
|
211 if ($counter === $number) {
|
Chris@0
|
212 break;
|
Chris@0
|
213 }
|
Chris@0
|
214
|
Chris@0
|
215 $counter++;
|
Chris@17
|
216 $start = $this->phpcsFile->findNext(Tokens::$emptyTokens, ($nextSeperator + 1), null, true);
|
Chris@0
|
217 $end = $lastArgEnd;
|
Chris@0
|
218 }//end while
|
Chris@0
|
219
|
Chris@0
|
220 // If the counter did not reach the passed number something is wrong.
|
Chris@0
|
221 if ($counter !== $number) {
|
Chris@0
|
222 return false;
|
Chris@0
|
223 }
|
Chris@0
|
224
|
Chris@0
|
225 $this->arguments[$counter] = array(
|
Chris@0
|
226 'start' => $start,
|
Chris@0
|
227 'end' => $end,
|
Chris@0
|
228 );
|
Chris@0
|
229 return $this->arguments[$counter];
|
Chris@0
|
230
|
Chris@0
|
231 }//end getArgument()
|
Chris@0
|
232
|
Chris@0
|
233
|
Chris@0
|
234 }//end class
|