1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Automattic\Jetpack\Analyzer; |
4
|
|
|
|
5
|
|
|
use PhpParser\ParserFactory; |
6
|
|
|
use PhpParser\NodeTraverser; |
7
|
|
|
use PhpParser\NodeDumper; |
8
|
|
|
use PhpParser\NodeVisitor\NameResolver; |
9
|
|
|
|
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* TODO: share this file loading code w/ Declarations |
13
|
|
|
*/ |
14
|
|
|
class Invocations extends PersistentList { |
15
|
|
|
private $parser; |
16
|
|
|
|
17
|
|
|
function __construct() { |
18
|
|
|
$this->parser = ( new ParserFactory() )->create( ParserFactory::PREFER_PHP7 ); |
19
|
|
|
parent::__construct(); |
20
|
|
|
} |
21
|
|
|
|
22
|
|
|
private function slashit( $path ) { |
23
|
|
|
$path .= ( substr( $path, -1 ) == '/' ? '' : '/' ); |
24
|
|
|
return $path; |
25
|
|
|
} |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Scan every PHP in the root |
29
|
|
|
*/ |
30
|
|
View Code Duplication |
public function scan( $root, $exclude = array() ) { |
31
|
|
|
if ( is_dir( $root ) ) { |
32
|
|
|
return $this->scan_dir( $this->slashit( $root ), $exclude ); |
33
|
|
|
} elseif ( is_file( $root ) ) { |
34
|
|
|
return $this->scan_file( $this->slashit( dirname( $root ) ), $root ); |
35
|
|
|
} else { |
36
|
|
|
throw new \Exception( 'input_error', "Expected $root to be a file or directory" ); |
37
|
|
|
} |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
public function scan_dir( $root, $exclude = array() ) { |
41
|
|
View Code Duplication |
$filter = function ( $file, $key, $iterator ) use ( $exclude ) { |
42
|
|
|
if ( $iterator->hasChildren() && ! in_array( $file->getFilename(), $exclude ) ) { |
43
|
|
|
return true; |
44
|
|
|
} |
45
|
|
|
return $file->isFile(); |
46
|
|
|
}; |
47
|
|
|
|
48
|
|
|
$inner_iterator = new \RecursiveDirectoryIterator( $root, \RecursiveDirectoryIterator::SKIP_DOTS ); |
49
|
|
|
|
50
|
|
|
$iterator = new \RecursiveIteratorIterator( |
51
|
|
|
new \RecursiveCallbackFilterIterator( $inner_iterator, $filter ) |
52
|
|
|
); |
53
|
|
|
|
54
|
|
|
$valid_extensions = array( 'php' ); |
55
|
|
View Code Duplication |
foreach ( $iterator as $file ) { |
56
|
|
|
$parts = explode( '.', $file ); |
57
|
|
|
$current_extension = strtolower( array_pop( $parts ) ); |
58
|
|
|
|
59
|
|
|
if ( in_array( $current_extension, $valid_extensions ) ) { |
60
|
|
|
$this->scan_file( $root, $file ); |
61
|
|
|
} |
62
|
|
|
} |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Scans the file for any invocations that depend on missing or different classes, methods, properties and functions |
67
|
|
|
*/ |
68
|
|
View Code Duplication |
public function scan_file( $root, $file_path ) { |
69
|
|
|
$file_path_relative = str_replace( $root, '', $file_path ); |
70
|
|
|
|
71
|
|
|
$source = file_get_contents( $file_path ); |
72
|
|
|
try { |
73
|
|
|
$ast = $this->parser->parse( $source ); |
74
|
|
|
} catch ( \Error $error ) { |
|
|
|
|
75
|
|
|
echo "Parse error: {$error->getMessage()}\n"; |
76
|
|
|
return; |
77
|
|
|
} catch ( \RuntimeException $error ) { |
78
|
|
|
echo "Parse error: {$error->getMessage()}\n"; |
79
|
|
|
return; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
// $dumper = new NodeDumper; |
83
|
|
|
// echo $dumper->dump($ast) . "\n"; |
84
|
|
|
|
85
|
|
|
// before parsing, make sure we try to resolve class names |
86
|
|
|
$traverser = new NodeTraverser(); |
87
|
|
|
$nameResolver = new NameResolver(); |
88
|
|
|
$traverser->addVisitor( $nameResolver ); |
89
|
|
|
|
90
|
|
|
// Resolve names |
91
|
|
|
$ast = $traverser->traverse( $ast ); |
92
|
|
|
|
93
|
|
|
$traverser = new NodeTraverser(); |
94
|
|
|
$invocations = new Invocations(); |
|
|
|
|
95
|
|
|
$invocation_finder = new Invocations\Visitor( $file_path_relative, $this ); |
96
|
|
|
$traverser->addVisitor( $invocation_finder ); |
97
|
|
|
$ast = $traverser->traverse( $ast ); |
|
|
|
|
98
|
|
|
} |
99
|
|
|
} |
100
|
|
|
|
Scrutinizer analyzes your
composer.json
/composer.lock
file if available to determine the classes, and functions that are defined by your dependencies.It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.