Completed
Push — feature/back-compat-7.5 ( 9186e7 )
by
unknown
23:17 queued 14:42
created

Invocations::scan_file()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 31

Duplication

Lines 31
Ratio 100 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 2
dl 31
loc 31
rs 9.424
c 0
b 0
f 0
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 ) {
0 ignored issues
show
Bug introduced by
The class Error does not exist. Did you forget a USE statement, or did you not list all dependencies?

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.

Loading history...
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();
0 ignored issues
show
Unused Code introduced by
$invocations is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
95
		$invocation_finder = new Invocations\Visitor( $file_path_relative, $this );
96
		$traverser->addVisitor( $invocation_finder );
97
		$ast = $traverser->traverse( $ast );
0 ignored issues
show
Unused Code introduced by
$ast is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
98
	}
99
}
100