Completed
Push — master ( f1c7a7...774eb2 )
by Richard
07:53 queued 02:10
created

Indexer   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 312
Duplicated Lines 11.54 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 84.68%

Importance

Changes 10
Bugs 0 Features 0
Metric Value
wmc 25
c 10
b 0
f 0
lcom 1
cbo 8
dl 36
loc 312
ccs 105
cts 124
cp 0.8468
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A register_listeners() 0 6 1
B index_file() 0 31 5
A lines_from_to() 0 5 1
A on_enter_misc() 0 3 1
A file_path() 0 3 1
A file_content() 0 10 2
A in_entities() 0 3 1
A beforeTraverse() 0 22 1
A afterTraverse() 0 12 1
B enterNode() 36 74 6
B leaveNode() 0 47 4

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/******************************************************************************
3
 * An implementation of dicto (scg.unibe.ch/dicto) in and for PHP.
4
 *
5
 * Copyright (c) 2016, 2015 Richard Klees <[email protected]>
6
 *
7
 * This software is licensed under The MIT License. You should have received
8
 * a copy of the licence along with the code.
9
 */
10
11
namespace Lechimp\Dicto\Indexer;
12
13
use Lechimp\Dicto\Indexer as I;
14
use Lechimp\Dicto\Variables\Variable;
15
use PhpParser\Node as N;
16
17
/**
18
 * Implementation of Indexer with PhpParser.
19
 */
20
class Indexer implements Location, ListenerRegistry, \PhpParser\NodeVisitor {
21
    /**
22
     * @var string
23
     */
24
    protected $project_root_path;
25
26
    /**
27
     * @var I\Insert|null
28
     */
29
    protected $insert;
30
31
    /**
32
     * @var \PhpParser\Parser
33
     */
34
    protected $parser;
35
36
    /**
37
     * @var array   string => array()
38
     */
39
    protected $listeners;
40
41
    // state for parsing a file
42
43
    /**
44
     * @var string|null
45
     */
46
    protected $file_path = null;
47
48
    /**
49
     * @var string[]|null
50
     */
51
    protected $file_content = null;
52
53
    /**
54
     * This contains the stack of ids were currently in, i.e. the nesting of
55
     * known code blocks we are in.
56
     *
57
     * @var array|null  contain ($entity_type, $entity_id)
58
     */
59
    protected $entity_stack = null;
60
61 22
    public function __construct(\PhpParser\Parser $parser, $project_root_path, Insert $insert) {
62 22
        $this->parser = $parser;
63 22
        assert('is_string($project_root_path)');
64 22
        $this->project_root_path = $project_root_path;
65 22
        $this->insert = $insert;
66
        // TODO: This could contain class names from PhpParser as optimisation.
67 22
        $this->listeners = array("misc" => array());
68
        // TODO: This should be more dynamic.
69 22
        $this->register_listeners();
70 22
    }
71
72 22
    protected function register_listeners() {
73 22
        $d = new \Lechimp\Dicto\Rules\DependOn();
74 22
        $d->register_listeners($this);
75 22
        $i = new \Lechimp\Dicto\Rules\Invoke();
76 22
        $i->register_listeners($this);
77 22
    }
78
79
    /**
80
     * @param   string  $path
81
     */
82 21
    public function index_file($path) {
83 21
        if ($this->insert === null) {
84
            throw new \RuntimeException(
85
                "Set an inserter to be used before starting to index files.");
86
        }
87
88 21
        $content = file_get_contents($this->project_root_path."/$path");
89 21
        if ($content === false) {
90
            throw \InvalidArgumentException("Can't read file $path.");
91
        }
92
93 21
        $stmts = $this->parser->parse($content);
94 21
        if ($stmts === null) {
95
            throw new \RuntimeException("Can't parse file $path.");
96
        }
97
98 21
        if ($stmts === null) {
99
            throw new \RuntimeException("Could not parse file '$path'.");
100
        }
101
102 21
        $traverser = new \PhpParser\NodeTraverser;
103 21
        $traverser->addVisitor($this);
104
105 21
        $this->entity_stack = array();
106 21
        $this->file_path = $path;
107 21
        $this->file_content = explode("\n", $content);
108 21
        $traverser->traverse($stmts);
109 21
        $this->entity_stack = null; 
110 21
        $this->file_path = null;
111 21
        $this->file_content = null;
112 21
    }
113
114
    // helper
115
116 21
    private function lines_from_to($start, $end) {
117 21
        assert('is_int($start)');
118 21
        assert('is_int($end)');
119 21
        return implode("\n", array_slice($this->file_content, $start-1, $end-$start+1));
120
    }
121
122
   // from ListenerRegistry 
123
124 22
    public function on_enter_misc(\Closure $listener) {
125 22
        $this->listeners["misc"][] = $listener;
126 22
    }
127
128
129
   // from Location
130
131
    /**
132
     * @inheritdoc
133
     */
134 16
    public function file_path() {
135 16
        return $this->file_path;
136
    }
137
138
    /**
139
     * @inheritdoc
140
     */
141 16
    public function file_content($from_line = null, $to_line = null) {
142 16
        if ($from_line !== null) {
143 16
            assert('$to_line !== null');
144 16
            return $this->lines_from_to($from_line, $to_line);
145
        }
146
        else {
147
            assert('$to_line === null');
148
            return implode("\n", $this->file_content);
149
        }
150
    }
151
152
    /**
153
     * @inheritdoc
154
     */
155 16
    public function in_entities() {
156 16
        return $this->entity_stack;
157
    }
158
159
    // from \PhpParser\NodeVisitor
160
161
    /**
162
     * @inheritdoc
163
     */
164 21
    public function beforeTraverse(array $nodes) {
165
        // for sure found a file
166 21
        $id = $this->insert->entity
167 21
            ( Variable::FILE_TYPE
168 21
            , $this->file_path
169 21
            , $this->file_path
170 21
            , 1
171 21
            , count($this->file_content)
172 21
            , implode("\n", $this->file_content)
173 21
            );
174
175 21
        $this->entity_stack[] = array(Variable::FILE_TYPE, $id);
176
177
        // TODO: reimplement this in some other way.
178
        /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
179
        foreach ($this->listeners as $listener) {
180
            $listener->on_enter_file($id, $this->file_path, implode("\n", $this->file_content));
181
        }
182
        */
183
184 21
        return null;
185
    }
186
187
    /**
188
     * @inheritdoc
189
     */
190 21
    public function afterTraverse(array $nodes) {
191 21
        $type_and_id = array_pop($this->entity_stack);
0 ignored issues
show
Unused Code introduced by
$type_and_id 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...
192
193
        // TODO: reimplement this in some other way.
194
        /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
195
        foreach ($this->listeners as $listener) {
196
            $listener->on_leave_file($type_and_id[1]);
197
        }
198
        */
199
200 21
        return null;
201
    }
202
203
    /**
204
     * @inheritdoc
205
     */
206 21
    public function enterNode(\PhpParser\Node $node) {
207 21
        $start_line = $node->getAttribute("startLine");
208 21
        $end_line = $node->getAttribute("endLine");
209 21
        $source = $this->lines_from_to($start_line, $end_line);
210
211 21
        $id = null;
212 21
        $type = null;
213
214
        // Class
215 21
        if ($node instanceof N\Stmt\Class_) {
216 20
            $type = Variable::CLASS_TYPE;
217 20
            $id = $this->insert->entity
218 20
                ( $type
219 20
                , $node->name
220 20
                , $this->file_path
221 20
                , $start_line
222 20
                , $end_line
223 20
                , $source
224 20
                );
225
226
            // TODO: reimplement this in some other way.
227
            /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
228
            foreach ($this->listeners as $listener) {
229
                $listener->on_enter_class($id, $node);
230
            }
231
            */
232 20
        }
233
        // Method or Function
234 21 View Code Duplication
        elseif ($node instanceof N\Stmt\ClassMethod) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
235 20
            $type = Variable::METHOD_TYPE;
236 20
            $id = $this->insert->entity
237 20
                ( $type
238 20
                , $node->name
239 20
                , $this->file_path
240 20
                , $start_line
241 20
                , $end_line
242 20
                , $source
243 20
                );
244
245
            // TODO: reimplement this in some other way.
246
            /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
247
            foreach ($this->listeners as $listener) {
248
                $listener->on_enter_method($id, $node);
249
            }
250
            */
251 20
        }
252 21 View Code Duplication
        elseif ($node instanceof N\Stmt\Function_) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
253
            $type = Variable::FUNCTION_TYPE;
254
            $id = $this->insert->entity
255
                ( $type 
256
                , $node->name
257
                , $this->file_path
258
                , $start_line
259
                , $end_line
260
                , $source
261
                );
262
263
            // TODO: reimplement this in some other way.
264
            /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
265
            foreach ($this->listeners as $listener) {
266
                $listener->on_enter_function($id, $node);
267
            }
268
            */
269
        }
270
        else {
271 21
            foreach ($this->listeners["misc"] as $listener) {
272 21
                $listener($this->insert, $this, $node);
273 21
            }
274
        }
275
276 21
        if ($id !== null) {
277 20
            $this->entity_stack[] = array($type, $id);
278 20
        }
279 21
    }
280
281
    /**
282
     * @inheritdoc
283
     */
284 21
    public function leaveNode(\PhpParser\Node $node) {
285
        // Class
286 21
        if ($node instanceof N\Stmt\Class_) {
287
            // We pushed it, we need to pop it now, as we are leaving the class.
288 20
            $type_and_id = array_pop($this->entity_stack);
0 ignored issues
show
Unused Code introduced by
$type_and_id 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...
289
290
            // TODO: reimplement this in some other way.
291
            /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
292
            foreach ($this->listeners as $listener) {
293
                $listener->on_leave_class($type_and_id[1]);
294
            }
295
            */
296 20
        }
297
        // Method or Function
298 21
        elseif ($node instanceof N\Stmt\ClassMethod) {
299
            // We pushed it, we need to pop it now, as we are leaving the method
300
            // or function.
301 20
            $type_and_id = array_pop($this->entity_stack);
0 ignored issues
show
Unused Code introduced by
$type_and_id 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...
302
303
            // TODO: reimplement this in some other way.
304
            /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
305
            foreach ($this->listeners as $listener) {
306
                $listener->on_leave_method($type_and_id[1]);
307
            }
308
            */
309 20
        }
310 21
        elseif ($node instanceof N\Stmt\Function_) {
311
            // We pushed it, we need to pop it now, as we are leaving the method
312
            // or function.
313
            $type_and_id = array_pop($this->entity_stack);
0 ignored issues
show
Unused Code introduced by
$type_and_id 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...
314
315
            // TODO: reimplement this in some other way.
316
            /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
317
            foreach ($this->listeners as $listener) {
318
                $listener->on_leave_function($type_and_id[1]);
319
            }
320
            */
321
        }
322
        else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
323
            // TODO: reimplement this in some other way.
324
            /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
325
            foreach ($this->listeners as $listener) {
326
                $listener->on_leave_misc($this->insert, $this, $node);
327
            }
328
            */
329
        }
330 21
    }
331
}
332