MediaWiki  master
findDeprecated.php
Go to the documentation of this file.
1 <?php
26 require_once __DIR__ . '/Maintenance.php';
27 require_once __DIR__ . '/../vendor/autoload.php';
28 
32 class FileAwareNodeVisitor extends PhpParser\NodeVisitorAbstract {
33  private $currentFile = null;
34 
35  public function enterNode( PhpParser\Node $node ) {
36  $retVal = parent::enterNode( $node );
37  $node->filename = $this->currentFile;
38  return $retVal;
39  }
40 
41  public function setCurrentFile( $filename ) {
42  $this->currentFile = $filename;
43  }
44 
45  public function getCurrentFile() {
46  return $this->currentFile;
47  }
48 }
49 
54 
55  private $currentClass = null;
56 
57  private $foundNodes = [];
58 
59  public function getFoundNodes() {
60  // Sort results by version, then by filename, then by name.
61  foreach ( $this->foundNodes as $version => &$nodes ) {
62  uasort( $nodes, function ( $a, $b ) {
63  return ( $a['filename'] . $a['name'] ) <=> ( $b['filename'] . $b['name'] );
64  } );
65  }
66  ksort( $this->foundNodes );
67  return $this->foundNodes;
68  }
69 
76  public function isHardDeprecated( PhpParser\Node $node ) {
77  if ( !$node->stmts ) {
78  return false;
79  }
80  foreach ( $node->stmts as $stmt ) {
81  if (
82  $stmt instanceof PhpParser\Node\Expr\FuncCall
83  && $stmt->name->toString() === 'wfDeprecated'
84  ) {
85  return true;
86  }
87  return false;
88  }
89  }
90 
91  public function enterNode( PhpParser\Node $node ) {
92  $retVal = parent::enterNode( $node );
93 
94  if ( $node instanceof PhpParser\Node\Stmt\ClassLike ) {
95  $this->currentClass = $node->name;
96  }
97 
98  if ( $node instanceof PhpParser\Node\FunctionLike ) {
99  $docComment = $node->getDocComment();
100  if ( !$docComment ) {
101  return;
102  }
103  if ( !preg_match( '/@deprecated.*(\d+\.\d+)/', $docComment->getText(), $matches ) ) {
104  return;
105  }
106  $version = $matches[1];
107 
108  if ( $node instanceof PhpParser\Node\Stmt\ClassMethod ) {
109  $name = $this->currentClass . '::' . $node->name;
110  } else {
111  $name = $node->name;
112  }
113 
114  $this->foundNodes[ $version ][] = [
115  'filename' => $node->filename,
116  'line' => $node->getLine(),
117  'name' => $name,
118  'hard' => $this->isHardDeprecated( $node ),
119  ];
120  }
121 
122  return $retVal;
123  }
124 }
125 
131  public function __construct() {
132  parent::__construct();
133  $this->addDescription( 'Find deprecated interfaces' );
134  }
135 
139  public function getFiles() {
140  global $IP;
141 
142  $files = new RecursiveDirectoryIterator( $IP . '/includes' );
143  $files = new RecursiveIteratorIterator( $files );
144  $files = new RegexIterator( $files, '/\.php$/' );
145  return iterator_to_array( $files, false );
146  }
147 
148  public function execute() {
149  global $IP;
150 
151  $files = $this->getFiles();
152  $chunkSize = ceil( count( $files ) / 72 );
153 
154  $parser = ( new PhpParser\ParserFactory )->create( PhpParser\ParserFactory::PREFER_PHP7 );
155  $traverser = new PhpParser\NodeTraverser;
156  $finder = new DeprecatedInterfaceFinder;
157  $traverser->addVisitor( $finder );
158 
159  $fileCount = count( $files );
160 
161  for ( $i = 0; $i < $fileCount; $i++ ) {
162  $file = $files[$i];
163  $code = file_get_contents( $file );
164 
165  if ( strpos( $code, '@deprecated' ) === -1 ) {
166  continue;
167  }
168 
169  $finder->setCurrentFile( substr( $file->getPathname(), strlen( $IP ) + 1 ) );
170  $nodes = $parser->parse( $code );
171  $traverser->traverse( $nodes );
172 
173  if ( $i % $chunkSize === 0 ) {
174  $percentDone = 100 * $i / $fileCount;
175  fprintf( STDERR, "\r[%-72s] %d%%", str_repeat( '#', $i / $chunkSize ), $percentDone );
176  }
177  }
178 
179  fprintf( STDERR, "\r[%'#-72s] 100%%\n", '' );
180 
181  // Colorize output if STDOUT is an interactive terminal.
182  if ( posix_isatty( STDOUT ) ) {
183  $versionFmt = "\n* Deprecated since \033[37;1m%s\033[0m:\n";
184  $entryFmt = " %s \033[33;1m%s\033[0m (%s:%d)\n";
185  } else {
186  $versionFmt = "\n* Deprecated since %s:\n";
187  $entryFmt = " %s %s (%s:%d)\n";
188  }
189 
190  foreach ( $finder->getFoundNodes() as $version => $nodes ) {
191  printf( $versionFmt, $version );
192  foreach ( $nodes as $node ) {
193  printf(
194  $entryFmt,
195  $node['hard'] ? '+' : '-',
196  $node['name'],
197  $node['filename'],
198  $node['line']
199  );
200  }
201  }
202  printf( "\nlegend:\n -: soft-deprecated\n +: hard-deprecated (via wfDeprecated())\n" );
203  }
204 }
205 
206 $maintClass = FindDeprecated::class;
207 require_once RUN_MAINTENANCE_IF_MAIN;
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42
const RUN_MAINTENANCE_IF_MAIN
Definition: Maintenance.php:39
$IP
Definition: WebStart.php:41
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
Definition: Maintenance.php:86
enterNode(PhpParser\Node $node)
Maintenance task that recursively scans MediaWiki PHP files for deprecated functions and interfaces a...
isHardDeprecated(PhpParser\Node $node)
Check whether a function or method includes a call to wfDeprecated(), indicating that it is a hard-de...
A PHPParser node visitor that finds deprecated functions and methods.
A PHPParser node visitor that associates each node with its file name.
setCurrentFile( $filename)
enterNode(PhpParser\Node $node)
$maintClass
$matches