Completed
Push — master ( 389cae...29b40e )
by Richard
06:27
created

Indexer::enterNode()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 74
Code Lines 38

Duplication

Lines 36
Ratio 48.65 %

Code Coverage

Tests 36
CRAP Score 6.3698

Importance

Changes 5
Bugs 0 Features 0
Metric Value
c 5
b 0
f 0
dl 36
loc 74
ccs 36
cts 46
cp 0.7826
rs 8.4878
nc 8
cc 6
eloc 38
nop 1
crap 6.3698

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
     * @inheritdoc
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
        $this->reference_cache = array();
0 ignored issues
show
Bug introduced by
The property reference_cache does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
109 21
        $traverser->traverse($stmts);
110 21
        $this->entity_stack = null; 
111 21
        $this->file_path = null;
112 21
        $this->file_content = null;
113 21
        $this->reference_cache = null;
114 21
    }
115
116
    // helper
117
118 21
    private function lines_from_to($start, $end) {
119 21
        assert('is_int($start)');
120 21
        assert('is_int($end)');
121 21
        return implode("\n", array_slice($this->file_content, $start-1, $end-$start+1));
122
    }
123
124
   // from ListenerRegistry 
125
126 22
    public function on_enter_misc(\Closure $listener) {
127 22
        $this->listeners["misc"][] = $listener;
128 22
    }
129
130
131
   // from Location
132
133
    /**
134
     * @inheritdoc
135
     */
136 16
    public function file_path() {
137 16
        return $this->file_path;
138
    }
139
140
    /**
141
     * @inheritdoc
142
     */
143 16
    public function file_content($from_line = null, $to_line = null) {
144 16
        if ($from_line !== null) {
145 16
            assert('$to_line !== null');
146 16
            return $this->lines_from_to($from_line, $to_line);
147
        }
148
        else {
149
            assert('$to_line === null');
150
            return $this->implode("\n", $this->file_content);
0 ignored issues
show
Bug introduced by
The method implode() does not seem to exist on object<Lechimp\Dicto\Indexer\Indexer>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
151
        }
152
    }
153
154
    /**
155
     * @inheritdoc
156
     */
157 16
    public function in_entities() {
158 16
        return $this->entity_stack;
159
    }
160
161
    // from \PhpParser\NodeVisitor
162
163
    /**
164
     * @inheritdoc
165
     */
166 21
    public function beforeTraverse(array $nodes) {
167
        // for sure found a file
168 21
        $id = $this->insert->entity
169 21
            ( Variable::FILE_TYPE
170 21
            , $this->file_path
171 21
            , $this->file_path
172 21
            , 1
173 21
            , count($this->file_content)
174 21
            , implode("\n", $this->file_content)
175 21
            );
176
177 21
        $this->entity_stack[] = array(Variable::FILE_TYPE, $id);
178
179
        // TODO: reimplement this in some other way.
180
        /*
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...
181
        foreach ($this->listeners as $listener) {
182
            $listener->on_enter_file($id, $this->file_path, implode("\n", $this->file_content));
183
        }
184
        */
185
186 21
        return null;
187
    }
188
189
    /**
190
     * @inheritdoc
191
     */
192 21
    public function afterTraverse(array $nodes) {
193 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...
194
195
        // TODO: reimplement this in some other way.
196
        /*
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...
197
        foreach ($this->listeners as $listener) {
198
            $listener->on_leave_file($type_and_id[1]);
199
        }
200
        */
201
202 21
        return null;
203
    }
204
205
    /**
206
     * @inheritdoc
207
     */
208 21
    public function enterNode(\PhpParser\Node $node) {
209 21
        $start_line = $node->getAttribute("startLine");
210 21
        $end_line = $node->getAttribute("endLine");
211 21
        $source = $this->lines_from_to($start_line, $end_line);
212
213 21
        $id = null;
214 21
        $type = null;
215
216
        // Class
217 21
        if ($node instanceof N\Stmt\Class_) {
218 20
            $type = Variable::CLASS_TYPE;
219 20
            $id = $this->insert->entity
220 20
                ( $type
221 20
                , $node->name
222 20
                , $this->file_path
223 20
                , $start_line
224 20
                , $end_line
225 20
                , $source
226 20
                );
227
228
            // TODO: reimplement this in some other way.
229
            /*
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...
230
            foreach ($this->listeners as $listener) {
231
                $listener->on_enter_class($id, $node);
232
            }
233
            */
234 20
        }
235
        // Method or Function
236 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...
237 20
            $type = Variable::METHOD_TYPE;
238 20
            $id = $this->insert->entity
239 20
                ( $type
240 20
                , $node->name
241 20
                , $this->file_path
242 20
                , $start_line
243 20
                , $end_line
244 20
                , $source
245 20
                );
246
247
            // TODO: reimplement this in some other way.
248
            /*
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...
249
            foreach ($this->listeners as $listener) {
250
                $listener->on_enter_method($id, $node);
251
            }
252
            */
253 20
        }
254 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...
255
            $type = Variable::FUNCTION_TYPE;
256
            $id = $this->insert->entity
257
                ( $type 
258
                , $node->name
259
                , $this->file_path
260
                , $start_line
261
                , $end_line
262
                , $source
263
                );
264
265
            // TODO: reimplement this in some other way.
266
            /*
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...
267
            foreach ($this->listeners as $listener) {
268
                $listener->on_enter_function($id, $node);
269
            }
270
            */
271
        }
272
        else {
273 21
            foreach ($this->listeners["misc"] as $listener) {
274 21
                $listener($this->insert, $this, $node);
275 21
            }
276
        }
277
278 21
        if ($id !== null) {
279 20
            $this->entity_stack[] = array($type, $id);
280 20
        }
281 21
    }
282
283
    /**
284
     * @inheritdoc
285
     */
286 21
    public function leaveNode(\PhpParser\Node $node) {
287
        // Class
288 21
        if ($node instanceof N\Stmt\Class_) {
289
            // We pushed it, we need to pop it now, as we are leaving the class.
290 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...
291
292
            // TODO: reimplement this in some other way.
293
            /*
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...
294
            foreach ($this->listeners as $listener) {
295
                $listener->on_leave_class($type_and_id[1]);
296
            }
297
            */
298 20
        }
299
        // Method or Function
300 21
        elseif ($node instanceof N\Stmt\ClassMethod) {
301
            // We pushed it, we need to pop it now, as we are leaving the method
302
            // or function.
303 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...
304
305
            // TODO: reimplement this in some other way.
306
            /*
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...
307
            foreach ($this->listeners as $listener) {
308
                $listener->on_leave_method($type_and_id[1]);
309
            }
310
            */
311 20
        }
312 21
        elseif ($node instanceof N\Stmt\Function_) {
313
            // We pushed it, we need to pop it now, as we are leaving the method
314
            // or function.
315
            $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...
316
317
            // TODO: reimplement this in some other way.
318
            /*
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...
319
            foreach ($this->listeners as $listener) {
320
                $listener->on_leave_function($type_and_id[1]);
321
            }
322
            */
323
        }
324
        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...
325
            // TODO: reimplement this in some other way.
326
            /*
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...
327
            foreach ($this->listeners as $listener) {
328
                $listener->on_leave_misc($this->insert, $this, $node);
329
            }
330
            */
331
        }
332 21
    }
333
}
334