Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
MethodArgumentsSniff.php
Go to the documentation of this file.
1 <?php
7 declare(strict_types=1);
8 
10 
11 use PHP_CodeSniffer\Sniffs\Sniff;
12 use PHP_CodeSniffer\Files\File;
13 
17 class MethodArgumentsSniff implements Sniff
18 {
22  private $validTokensBeforeClosingCommentTag = [
23  'T_WHITESPACE',
24  'T_PUBLIC',
25  'T_PRIVATE',
26  'T_PROTECTED',
27  'T_STATIC',
28  'T_ABSTRACT',
29  'T_FINAL'
30  ];
31 
35  private $invalidTypes = [
36  'null',
37  'false',
38  'true',
39  'self'
40  ];
41 
45  public function register() : array
46  {
47  return [
48  T_FUNCTION
49  ];
50  }
51 
58  private function isTokenBeforeClosingCommentTagValid(string $type) : bool
59  {
60  return in_array($type, $this->validTokensBeforeClosingCommentTag);
61  }
62 
71  private function validateCommentBlockExists(File $phpcsFile, int $previousCommentClosePtr, int $stackPtr) : bool
72  {
73  $tokens = $phpcsFile->getTokens();
74  for ($tempPtr = $previousCommentClosePtr + 1; $tempPtr < $stackPtr; $tempPtr++) {
75  if (!$this->isTokenBeforeClosingCommentTagValid($tokens[$tempPtr]['type'])) {
76  return false;
77  }
78  }
79  return true;
80  }
81 
88  private function isInvalidType(string $type) : bool
89  {
90  return in_array(strtolower($type), $this->invalidTypes);
91  }
92 
101  private function getMethodArguments(File $phpcsFile, int $openParenthesisPtr, int $closedParenthesisPtr) : array
102  {
103  $tokens = $phpcsFile->getTokens();
104  $methodArguments = [];
105  for ($i = $openParenthesisPtr; $i < $closedParenthesisPtr; $i++) {
106  $argumentsPtr = $phpcsFile->findNext(T_VARIABLE, $i + 1, $closedParenthesisPtr);
107  if ($argumentsPtr === false) {
108  break;
109  } elseif ($argumentsPtr < $closedParenthesisPtr) {
110  $arguments = $tokens[$argumentsPtr]['content'];
111  $methodArguments[] = $arguments;
112  $i = $argumentsPtr - 1;
113  }
114  }
115  return $methodArguments;
116  }
117 
124  private function getMethodParameters(array $paramDefinitions) : array
125  {
126  $paramName = [];
127  for ($i = 0; $i < count($paramDefinitions); $i++) {
128  if (isset($paramDefinitions[$i]['paramName'])) {
129  $paramName[] = $paramDefinitions[$i]['paramName'];
130  }
131  }
132  return $paramName;
133  }
134 
142  private function validateInheritdocAnnotationWithoutBracesExists(
143  File $phpcsFile,
144  int $previousCommentOpenPtr,
145  int $previousCommentClosePtr
146  ) : bool {
147  return $this->validateInheritdocAnnotationExists(
148  $phpcsFile,
149  $previousCommentOpenPtr,
150  $previousCommentClosePtr,
151  '@inheritdoc'
152  );
153  }
154 
162  private function validateInheritdocAnnotationWithBracesExists(
163  File $phpcsFile,
164  int $previousCommentOpenPtr,
165  int $previousCommentClosePtr
166  ) : bool {
167  return $this->validateInheritdocAnnotationExists(
168  $phpcsFile,
169  $previousCommentOpenPtr,
170  $previousCommentClosePtr,
171  '{@inheritdoc}'
172  );
173  }
174 
184  private function validateInheritdocAnnotationExists(
185  File $phpcsFile,
186  int $previousCommentOpenPtr,
187  int $previousCommentClosePtr,
188  string $inheritdocAnnotation
189  ) : bool {
190  $tokens = $phpcsFile->getTokens();
191  for ($ptr = $previousCommentOpenPtr; $ptr < $previousCommentClosePtr; $ptr++) {
192  if (strtolower($tokens[$ptr]['content']) === $inheritdocAnnotation) {
193  return true;
194  }
195  }
196  return false;
197  }
198 
209  private function validateParameterAnnotationForArgumentExists(
210  File $phpcsFile,
211  int $argumentsCount,
212  int $parametersCount,
213  int $previousCommentOpenPtr,
214  int $previousCommentClosePtr,
215  int $stackPtr
216  ) : void {
217  if ($argumentsCount > 0 && $parametersCount === 0) {
218  $inheritdocAnnotationWithoutBracesExists = $this->validateInheritdocAnnotationWithoutBracesExists(
219  $phpcsFile,
220  $previousCommentOpenPtr,
221  $previousCommentClosePtr
222  );
223  $inheritdocAnnotationWithBracesExists = $this->validateInheritdocAnnotationWithBracesExists(
224  $phpcsFile,
225  $previousCommentOpenPtr,
226  $previousCommentClosePtr
227  );
228  if ($inheritdocAnnotationWithBracesExists) {
229  $phpcsFile->addFixableError(
230  '{@inheritdoc} does not import parameter annotation',
231  $stackPtr,
232  'MethodArguments'
233  );
234  } elseif ($this->validateCommentBlockExists($phpcsFile, $previousCommentClosePtr, $stackPtr)
235  && !$inheritdocAnnotationWithoutBracesExists
236  ) {
237  $phpcsFile->addFixableError(
238  'Missing @param for argument in method annotation',
239  $stackPtr,
240  'MethodArguments'
241  );
242  }
243  }
244  }
245 
254  private function validateCommentBlockDoesnotHaveExtraParameterAnnotation(
255  File $phpcsFile,
256  int $argumentsCount,
257  int $parametersCount,
258  int $stackPtr
259  ) : void {
260  if ($argumentsCount < $parametersCount && $argumentsCount > 0) {
261  $phpcsFile->addFixableError(
262  'Extra @param found in method annotation',
263  $stackPtr,
264  'MethodArguments'
265  );
266  } elseif ($argumentsCount > 0 && $argumentsCount != $parametersCount && $parametersCount != 0) {
267  $phpcsFile->addFixableError(
268  '@param is not found for one or more params in method annotation',
269  $stackPtr,
270  'MethodArguments'
271  );
272  }
273  }
274 
284  private function validateArgumentNameInParameterAnnotationExists(
285  int $stackPtr,
286  int $ptr,
287  File $phpcsFile,
288  array $methodArguments,
289  array $paramDefinitions
290  ) : void {
291  $parameterNames = $this->getMethodParameters($paramDefinitions);
292  if (!in_array($methodArguments[$ptr], $parameterNames)) {
293  $error = $methodArguments[$ptr]. ' parameter is missing in method annotation';
294  $phpcsFile->addFixableError($error, $stackPtr, 'MethodArguments');
295  }
296  }
297 
307  private function validateParameterPresentInMethodSignature(
308  int $ptr,
309  string $paramDefinitionsArguments,
310  array $methodArguments,
311  File $phpcsFile,
312  array $paramPointers
313  ) : void {
314  if (!in_array($paramDefinitionsArguments, $methodArguments)) {
315  $phpcsFile->addFixableError(
316  $paramDefinitionsArguments . ' parameter is missing in method arguments signature',
317  $paramPointers[$ptr],
318  'MethodArguments'
319  );
320  }
321  }
322 
331  private function validateParameterOrderIsCorrect(
332  array $paramDefinitions,
333  array $methodArguments,
334  File $phpcsFile,
335  array $paramPointers
336  ) : void {
337  $parameterNames = $this->getMethodParameters($paramDefinitions);
338  $paramDefinitionsCount = count($paramDefinitions);
339  for ($ptr = 0; $ptr < $paramDefinitionsCount; $ptr++) {
340  if (isset($methodArguments[$ptr]) && isset($parameterNames[$ptr])
341  && in_array($methodArguments[$ptr], $parameterNames)
342  ) {
343  if ($methodArguments[$ptr] != $parameterNames[$ptr]) {
344  $phpcsFile->addFixableError(
345  $methodArguments[$ptr].' parameter is not in order',
346  $paramPointers[$ptr],
347  'MethodArguments'
348  );
349  }
350  }
351  }
352  }
353 
363  private function validateDuplicateAnnotationDoesnotExists(
364  int $stackPtr,
365  array $paramDefinitions,
366  array $paramPointers,
367  File $phpcsFile,
368  array $methodArguments
369  ) : void {
370  $argumentsCount = count($methodArguments);
371  $parametersCount = count($paramPointers);
372  if ($argumentsCount <= $parametersCount && $argumentsCount > 0) {
373  $duplicateParameters = [];
374  for ($i = 0; $i < sizeof($paramDefinitions); $i++) {
375  if (isset($paramDefinitions[$i]['paramName'])) {
376  $parameterContent = $paramDefinitions[$i]['paramName'];
377  for ($j = $i + 1; $j < count($paramDefinitions); $j++) {
378  if (isset($paramDefinitions[$j]['paramName'])
379  && $parameterContent === $paramDefinitions[$j]['paramName']
380  ) {
381  $duplicateParameters[] = $parameterContent;
382  }
383  }
384  }
385  }
386  foreach ($duplicateParameters as $value) {
387  $phpcsFile->addFixableError(
388  $value . ' duplicate found in method annotation',
389  $stackPtr,
390  'MethodArguments'
391  );
392  }
393  }
394  }
395 
405  private function validateParameterAnnotationFormatIsCorrect(
406  int $ptr,
407  File $phpcsFile,
408  array $methodArguments,
409  array $paramDefinitions,
410  array $paramPointers
411  ) : void {
412  switch (count($paramDefinitions)) {
413  case 0:
414  $phpcsFile->addFixableError(
415  'Missing both type and parameter',
416  $paramPointers[$ptr],
417  'MethodArguments'
418  );
419  break;
420  case 1:
421  if (preg_match('/^\$.*/', $paramDefinitions[0])) {
422  $phpcsFile->addError(
423  'Type is not specified',
424  $paramPointers[$ptr],
425  'MethodArguments'
426  );
427  }
428  break;
429  case 2:
430  if ($this->isInvalidType($paramDefinitions[0])) {
431  $phpcsFile->addFixableError(
432  $paramDefinitions[0].' is not a valid PHP type',
433  $paramPointers[$ptr],
434  'MethodArguments'
435  );
436  }
437  $this->validateParameterPresentInMethodSignature(
438  $ptr,
439  ltrim($paramDefinitions[1], '&'),
440  $methodArguments,
441  $phpcsFile,
442  $paramPointers
443  );
444  break;
445  default:
446  if (preg_match('/^\$.*/', $paramDefinitions[0])) {
447  $phpcsFile->addError(
448  'Type is not specified',
449  $paramPointers[$ptr],
450  'MethodArguments'
451  );
452  if ($this->isInvalidType($paramDefinitions[0])) {
453  $phpcsFile->addFixableError(
454  $paramDefinitions[0].' is not a valid PHP type',
455  $paramPointers[$ptr],
456  'MethodArguments'
457  );
458  }
459  }
460  break;
461  }
462  }
463 
475  private function validateMethodParameterAnnotations(
476  int $stackPtr,
477  array $paramDefinitions,
478  array $paramPointers,
479  File $phpcsFile,
480  array $methodArguments,
481  int $previousCommentOpenPtr,
482  int $previousCommentClosePtr
483  ) : void {
484  $argumentCount = count($methodArguments);
485  $paramCount = count($paramPointers);
486  $this->validateParameterAnnotationForArgumentExists(
487  $phpcsFile,
488  $argumentCount,
489  $paramCount,
490  $previousCommentOpenPtr,
491  $previousCommentClosePtr,
492  $stackPtr
493  );
494  $this->validateCommentBlockDoesnotHaveExtraParameterAnnotation(
495  $phpcsFile,
496  $argumentCount,
497  $paramCount,
498  $stackPtr
499  );
500  $this->validateDuplicateAnnotationDoesnotExists(
501  $stackPtr,
502  $paramDefinitions,
503  $paramPointers,
504  $phpcsFile,
505  $methodArguments
506  );
507  $this->validateParameterOrderIsCorrect(
508  $paramDefinitions,
509  $methodArguments,
510  $phpcsFile,
511  $paramPointers
512  );
513  for ($ptr = 0; $ptr < count($methodArguments); $ptr++) {
514  $tokens = $phpcsFile->getTokens();
515  if (isset($paramPointers[$ptr])) {
516  $this->validateArgumentNameInParameterAnnotationExists(
517  $stackPtr,
518  $ptr,
519  $phpcsFile,
520  $methodArguments,
521  $paramDefinitions
522  );
523  $paramContent = $tokens[$paramPointers[$ptr]+2]['content'];
524  $paramContentExplode = explode(' ', $paramContent);
525  $this->validateParameterAnnotationFormatIsCorrect(
526  $ptr,
527  $phpcsFile,
528  $methodArguments,
529  $paramContentExplode,
530  $paramPointers
531  );
532  }
533  }
534  }
535 
539  public function process(File $phpcsFile, $stackPtr)
540  {
541  $tokens = $phpcsFile->getTokens();
542  $numTokens = count($tokens);
543  $previousCommentOpenPtr = $phpcsFile->findPrevious(T_DOC_COMMENT_OPEN_TAG, $stackPtr-1, 0);
544  $previousCommentClosePtr = $phpcsFile->findPrevious(T_DOC_COMMENT_CLOSE_TAG, $stackPtr-1, 0);
545  if (!$this->validateCommentBlockExists($phpcsFile, $previousCommentClosePtr, $stackPtr)) {
546  $phpcsFile->addError('Comment block is missing', $stackPtr, 'MethodArguments');
547  return;
548  }
549  $openParenthesisPtr = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $stackPtr+1, $numTokens);
550  $closedParenthesisPtr = $phpcsFile->findNext(T_CLOSE_PARENTHESIS, $stackPtr+1, $numTokens);
551  $methodArguments = $this->getMethodArguments($phpcsFile, $openParenthesisPtr, $closedParenthesisPtr);
552  $paramPointers = $paramDefinitions = [];
553  for ($tempPtr = $previousCommentOpenPtr; $tempPtr < $previousCommentClosePtr; $tempPtr++) {
554  if (strtolower($tokens[$tempPtr]['content']) === '@param') {
555  $paramPointers[] = $tempPtr;
556  $paramAnnotationParts = explode(' ', $tokens[$tempPtr+2]['content']);
557  if (count($paramAnnotationParts) === 1) {
558  if ((preg_match('/^\$.*/', $paramAnnotationParts[0]))) {
559  $paramDefinitions[] = [
560  'type' => null,
561  'paramName' => rtrim(ltrim($tokens[$tempPtr+2]['content'], '&'), ',')
562  ];
563  } else {
564  $paramDefinitions[] = [
565  'type' => $tokens[$tempPtr+2]['content'],
566  'paramName' => null
567  ];
568  }
569  } else {
570  $paramDefinitions[] = [
571  'type' => $paramAnnotationParts[0],
572  'paramName' => rtrim(ltrim($paramAnnotationParts[1], '&'), ',')
573  ];
574  }
575  }
576  }
577  $this->validateMethodParameterAnnotations(
578  $stackPtr,
579  $paramDefinitions,
580  $paramPointers,
581  $phpcsFile,
582  $methodArguments,
583  $previousCommentOpenPtr,
584  $previousCommentClosePtr
585  );
586  }
587 }
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
$type
Definition: item.phtml:13
$value
Definition: gender.phtml:16
$arguments
$i
Definition: gallery.phtml:31
$tokens
Definition: cards_list.phtml:9