‪TYPO3CMS  9.5
annotationChecker.php
Go to the documentation of this file.
1 #!/usr/bin/env php
2 <?php
3 declare(strict_types = 1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
18 use PhpParser\Comment\Doc;
19 use PhpParser\Error;
20 use PhpParser\Node;
21 use PhpParser\NodeTraverser;
22 use PhpParser\NodeVisitorAbstract;
23 use PhpParser\ParserFactory;
24 use Symfony\Component\Console\Output\ConsoleOutput;
25 
26 require_once __DIR__ . '/../../vendor/autoload.php';
27 
31 class NodeVisitor extends NodeVisitorAbstract
32 {
36  public ‪$matches = [];
37 
38  public function ‪enterNode(Node $node)
39  {
40  switch (get_class($node)) {
41  case Node\Stmt\Class_::class:
42  case Node\Stmt\Property::class:
43  case Node\Stmt\ClassMethod::class:
45  if (!($docComment = $node->getDocComment()) instanceof Doc) {
46  return;
47  }
48 
49  // These annotations are OK to have, everything else is denied
50  $negativeLookaheadMatches = [
51  // Annotation tags
52  'Annotation', 'Attribute', 'Attributes', 'Required', 'Target',
53  // Widely used tags (but not existent in phpdoc)
54  'fix', 'fixme', 'override',
55  // PHPDocumentor 1 tags
56  'abstract', 'code', 'deprec', 'endcode', 'exception', 'final', 'ingroup', 'inheritdoc', 'inheritDoc', 'magic', 'name', 'toc', 'tutorial', 'private', 'static', 'staticvar', 'staticVar', 'throw',
57  // PHPDocumentor 2 tags
58  'api', 'author', 'category', 'copyright', 'deprecated', 'example', 'filesource', 'global', 'ignore', 'internal', 'license', 'link', 'method', 'package', 'param', 'property', 'property-read', 'property-write', 'return', 'see', 'since', 'source', 'subpackage', 'throws', 'todo', 'TODO', 'usedby', 'uses', 'var', 'version',
59  // PHPUnit tags
60  'codeCoverageIgnore', 'codeCoverageIgnoreStart', 'codeCoverageIgnoreEnd', 'test', 'covers', 'dataProvider', 'group', 'skip', 'depends', 'expectedException', 'before', 'requires',
61  // codeception tags
62  'env',
63  // PHPCheckStyle
64  'SuppressWarnings', 'noinspection',
65  // Extbase related (deprecated), will be removed in TYPO3 v10.0
66  'transient',
67  'cli', // this is still used in Extbase\Tests\UnitDeprecated
68  'flushesCaches', // still used in MockCCommandController test fixture, will be removed in TYPO3 v10.0
69  // Extbase related
70  'TYPO3\\\\CMS\\\\Extbase\\\\Annotation\\\\IgnoreValidation', 'Extbase\\\\IgnoreValidation', 'IgnoreValidation',
71  'TYPO3\\\\CMS\\\\Extbase\\\\Annotation\\\\Inject', 'Extbase\\\\Inject', 'Inject',
72  'TYPO3\\\\CMS\\\\Extbase\\\\Annotation\\\\Validate', 'Extbase\\\\Validate', 'Validate',
73  'Transient', 'Extbase\\\\ORM\\\\Lazy', 'Enum',
74  'TYPO3\\\\CMS\\\\Extbase\\\\Annotation\\\\ORM\\\\Cascade', 'Extbase\\\\ORM\\\\Cascade', 'Cascade',
75  // Extension scanner
76  'extensionScannerIgnoreFile', 'extensionScannerIgnoreLine',
77  // static code analysis
78  'template', 'implements', 'extends'
79  ];
80 
81  ‪$matches = [];
82  preg_match_all(
83  '/\*\s@(?!' . implode('|', $negativeLookaheadMatches) . ')(?<annotations>[a-zA-Z0-9\\\\]+)/',
84  $docComment->getText(),
86  );
87 
88  if (!empty(‪$matches['annotations'])) {
89  $this->matches[$node->getLine()] = array_map(function ($value) {
90  return '@' . $value;
91  }, ‪$matches['annotations']);
92  }
93 
94  break;
95  default:
96  break;
97  }
98  }
99 }
100 
101 ‪$parser = (new ParserFactory)->create(ParserFactory::ONLY_PHP7);
102 
103 ‪$finder = new Symfony\Component\Finder\Finder();
104 ‪$finder->files()
105  ->in(__DIR__ . '/../../typo3/')
106  ->name('/\.php$/')
107  // black list some deprecated unit test fixture files that test old deprecations
108  ->notPath('/.*sysext\/extbase\/Tests\/UnitDeprecated\/Reflection\/Fixture/')
109  // black list some unit test fixture files from extension scanner that test matchers of old annotations
110  ->notName('MethodAnnotationMatcherFixture.php')
111  ->notName('PropertyAnnotationMatcherFixture.php')
112 ;
113 
114 ‪$output = new ConsoleOutput();
115 
116 ‪$errors = [];
117 foreach (‪$finder as $file) {
118  try {
119  $ast = ‪$parser->parse($file->getContents());
120  } catch (Error $error) {
121  ‪$output->writeln('<error>Parse error: ' . $error->getMessage() . '</error>');
122  exit(1);
123  }
124 
125  $visitor = new NodeVisitor();
126 
127  $traverser = new NodeTraverser();
128  $traverser->addVisitor($visitor);
129 
130  $ast = $traverser->traverse($ast);
131 
132  if (!empty($visitor->matches)) {
133  ‪$errors[$file->getRealPath()] = $visitor->matches;
134  ‪$output->write('<error>F</error>');
135  } else {
136  ‪$output->write('<fg=green>.</>');
137  }
138 }
139 
140 ‪$output->writeln('');
141 
142 if (!empty(‪$errors)) {
143  ‪$output->writeln('');
144 
145  foreach (‪$errors as $file => $matchesPerLine) {
146  ‪$output->writeln('');
147  ‪$output->writeln('<error>' . $file . '</error>');
148 
154  foreach ($matchesPerLine as $line => $matches) {
155  ‪$output->writeln($line . ': ' . implode(', ', $matches));
156  }
157  }
158  exit(1);
159 }
160 
161 exit(0);
‪NodeVisitor\enterNode
‪enterNode(Node $node)
Definition: annotationChecker.php:37
‪$finder
‪$finder
Definition: annotationChecker.php:102
‪$parser
‪$parser
Definition: annotationChecker.php:100
‪$errors
‪$errors
Definition: annotationChecker.php:115
‪$output
‪$output
Definition: annotationChecker.php:113
‪NodeVisitor\$matches
‪array $matches
Definition: annotationChecker.php:35