ClassAnnotationStructureSniff.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. declare(strict_types=1);
  7. namespace Magento\Sniffs\Annotation;
  8. use PHP_CodeSniffer\Sniffs\Sniff;
  9. use PHP_CodeSniffer\Files\File;
  10. /**
  11. * Sniff to validate structure of class, interface annotations
  12. */
  13. class ClassAnnotationStructureSniff implements Sniff
  14. {
  15. /**
  16. * @var AnnotationFormatValidator
  17. */
  18. private $annotationFormatValidator;
  19. /**
  20. * @inheritdoc
  21. */
  22. public function register()
  23. {
  24. return [
  25. T_CLASS
  26. ];
  27. }
  28. /**
  29. * AnnotationStructureSniff constructor.
  30. */
  31. public function __construct()
  32. {
  33. $this->annotationFormatValidator = new AnnotationFormatValidator();
  34. }
  35. /**
  36. * Validates whether annotation block exists for interface, abstract or final classes
  37. *
  38. * @param File $phpcsFile
  39. * @param int $previousCommentClosePtr
  40. * @param int $stackPtr
  41. */
  42. private function validateInterfaceOrAbstractOrFinalClassAnnotationBlockExists(
  43. File $phpcsFile,
  44. int $previousCommentClosePtr,
  45. int $stackPtr
  46. ) : void {
  47. $tokens = $phpcsFile->getTokens();
  48. if ($tokens[$stackPtr]['type'] === 'T_CLASS') {
  49. if ($tokens[$stackPtr - 2]['type'] === 'T_ABSTRACT' &&
  50. $tokens[$stackPtr - 4]['content'] != $tokens[$previousCommentClosePtr]['content']
  51. ) {
  52. $error = 'Interface or abstract class is missing annotation block';
  53. $phpcsFile->addFixableError($error, $stackPtr, 'ClassAnnotation');
  54. }
  55. if ($tokens[$stackPtr - 2]['type'] === 'T_FINAL' &&
  56. $tokens[$stackPtr - 4]['content'] != $tokens[$previousCommentClosePtr]['content']
  57. ) {
  58. $error = 'Final class is missing annotation block';
  59. $phpcsFile->addFixableError($error, $stackPtr, 'ClassAnnotation');
  60. }
  61. }
  62. }
  63. /**
  64. * Validates whether annotation block exists
  65. *
  66. * @param File $phpcsFile
  67. * @param int $previousCommentClosePtr
  68. * @param int $stackPtr
  69. */
  70. private function validateAnnotationBlockExists(File $phpcsFile, int $previousCommentClosePtr, int $stackPtr) : void
  71. {
  72. $tokens = $phpcsFile->getTokens();
  73. $this->validateInterfaceOrAbstractOrFinalClassAnnotationBlockExists(
  74. $phpcsFile,
  75. $previousCommentClosePtr,
  76. $stackPtr
  77. );
  78. if ($tokens[$stackPtr - 2]['content'] != 'class' && $tokens[$stackPtr - 2]['content'] != 'abstract'
  79. && $tokens[$stackPtr - 2]['content'] != 'final'
  80. && $tokens[$stackPtr - 2]['content'] !== $tokens[$previousCommentClosePtr]['content']
  81. ) {
  82. $error = 'Class is missing annotation block';
  83. $phpcsFile->addFixableError($error, $stackPtr, 'ClassAnnotation');
  84. }
  85. }
  86. /**
  87. * @inheritdoc
  88. */
  89. public function process(File $phpcsFile, $stackPtr)
  90. {
  91. $tokens = $phpcsFile->getTokens();
  92. $previousCommentClosePtr = $phpcsFile->findPrevious(T_DOC_COMMENT_CLOSE_TAG, $stackPtr - 1, 0);
  93. $this->validateAnnotationBlockExists($phpcsFile, $previousCommentClosePtr, $stackPtr);
  94. $commentStartPtr = $phpcsFile->findPrevious(T_DOC_COMMENT_OPEN_TAG, $stackPtr - 1, 0);
  95. $commentCloserPtr = $tokens[$commentStartPtr]['comment_closer'];
  96. $emptyTypeTokens = [
  97. T_DOC_COMMENT_WHITESPACE,
  98. T_DOC_COMMENT_STAR
  99. ];
  100. $shortPtr = $phpcsFile->findNext($emptyTypeTokens, $commentStartPtr +1, $commentCloserPtr, true);
  101. if ($shortPtr === false) {
  102. $error = 'Annotation block is empty';
  103. $phpcsFile->addError($error, $commentStartPtr, 'MethodAnnotation');
  104. } else {
  105. $this->annotationFormatValidator->validateDescriptionFormatStructure(
  106. $phpcsFile,
  107. $commentStartPtr,
  108. (int) $shortPtr,
  109. $previousCommentClosePtr,
  110. $emptyTypeTokens
  111. );
  112. }
  113. }
  114. }