Completed
Push — master ( 1f981d...cc2765 )
by Richard
06:42
created

ResultDB::begin_rule()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 31
Code Lines 26

Duplication

Lines 9
Ratio 29.03 %

Code Coverage

Tests 28
CRAP Score 3

Importance

Changes 4
Bugs 0 Features 2
Metric Value
c 4
b 0
f 2
dl 9
loc 31
ccs 28
cts 28
cp 1
rs 8.8571
cc 3
eloc 26
nc 4
nop 1
crap 3
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 license along with the code.
9
 */
10
11
namespace Lechimp\Dicto\App;
12
13
use Lechimp\Dicto\Analysis\ReportGenerator;
14
use Lechimp\Dicto\Analysis\Violation;
15
use Lechimp\Dicto\Rules\Ruleset;
16
use Lechimp\Dicto\Rules\Rule;
17
use Lechimp\Dicto\Variables\Variable;
18
use Doctrine\DBAL\Schema;
19
use Doctrine\DBAL\Types\Type;
20
use Doctrine\DBAL\Schema\Synchronizer\SingleDatabaseSynchronizer;
21
22
class ResultDB extends DB implements ReportGenerator {
23
    /**
24
     * @var int|null
25
     */
26
    private $current_run_id = null;
27
28
    /**
29
     * @var int|null
30
     */
31
    private $current_rule_id = null;
32
33
    // ReportGenerator implementation
34
35
    /**
36
     * Announce to start a new run of the analysis now.
37
     *
38
     * @param   string  $commit_hash
39
     * @return  null
40
     */
41 18
    public function begin_new_run($commit_hash) {
42 18
        assert('is_string($commit_hash)');
43 18
        $this->builder()
44 18
            ->insert($this->run_table())
45 18
            ->values(array
46
                ( "commit_hash" => "?"
47 18
                ))
48 18
            ->setParameter(0, $commit_hash)
49 18
            ->execute();
50 18
        $this->current_run_id = (int)$this->connection->lastInsertId();
51 18
    }
52
53
    /**
54
     * @inheritdoc
55
     */
56
    public function begin_ruleset(Ruleset $rule) {
57
        // Nothing to do here...
58
    }
59
60
    /**
61
     * @inheritdoc
62
     */
63
    public function end_ruleset(Ruleset $rule) {
64
        // Nothing to do here...
65
    }
66
67
    /**
68
     * @inheritdoc
69
     */
70 12
    public function begin_rule(Rule $rule) {
71 12
        assert('$this->current_run_id !== null');
72 12
        $rule_id = $this->rule_id($rule);
73 12
        if ($rule_id === null) {
74 12
            $this->builder()
75 12
                ->insert($this->rule_table())
76 12
                ->values(array
77
                    ( "rule" => "?"
78 12
                    , "first_seen" => "?"
79 12
                    , "last_seen" => "?"
80 12
                    ))
81 12
                ->setParameter(0, $rule->pprint())
82 12
                ->setParameter(1, $this->current_run_id)
83 12
                ->setParameter(2, $this->current_run_id)
84 12
                ->execute();
85 12
            $rule_id = (int)$this->connection->lastInsertId();
86 12
        }
87 View Code Duplication
        else {
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...
88 7
            $this->builder()
89 7
                ->update($this->rule_table())
90 7
                ->set("last_seen", "?")
91 7
                ->where("id = ?")
92 7
                ->setParameter(0, $this->current_run_id)
93 7
                ->setParameter(1, $rule_id)
94 7
                ->execute();
95
        }
96 12
        foreach ($rule->variables() as $variable) {
97 12
            $this->upsert_variable($variable);
98 12
        } 
99 12
        $this->current_rule_id = $rule_id;
100 12
    }
101
102
    /**
103
     * @inheritdoc
104
     */
105
    public function end_rule(Rule $rule) {
106
        $this->current_rule_id = null;
107
    }
108
109
    /**
110
     * @inheritdoc
111
     */
112 7
    public function report_violation(Violation $violation) {
113 7
        assert('$this->current_run_id !== null');
114 7
        assert('$this->current_rule_id !== null');
115 7
        $violation_id = $this->violation_id($violation);
116 7
        if ($violation_id === null) {
117 7
            $this->builder()
118 7
                ->insert($this->violation_table())
119 7
                ->values(array
120
                    ( "rule_id" => "?"
121 7
                    , "file" => "?"
122 7
                    , "line" => "?"
123 7
                    , "first_seen" => "?"
124 7
                    , "last_seen" => "?"
125 7
                    ))
126 7
                ->setParameter(0, $this->current_rule_id)
127 7
                ->setParameter(1, $violation->filename())
128 7
                ->setParameter(2, $violation->line())
129 7
                ->setParameter(3, $this->current_run_id)
130 7
                ->setParameter(4, $this->current_run_id)
131 7
                ->execute();
132 7
            $violation_id = (int)$this->connection->lastInsertId();
133 7
        }
134 View Code Duplication
        else {
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...
135 3
            $this->builder()
136 3
                ->update($this->violation_table())
137 3
                ->set("last_seen", "?")
138 3
                ->where("id = ?")
139 3
                ->setParameter(0, $this->current_run_id)
140 3
                ->setParameter(1, $violation_id)
141 3
                ->execute();
142
        }
143
144 7
        $this->builder()
145 7
            ->insert($this->violation_location_table())
146 7
            ->values(array
147
                ( "violation_id" => "?"
148 7
                , "run_id"  => "?"
149 7
                , "line_no" => "?"
150 7
                ))
151 7
            ->setParameter(0, $violation_id)
152 7
            ->setParameter(1, $this->current_run_id)
153 7
            ->setParameter(2, $violation->line_no())
154 7
            ->execute();
155 7
    }
156
157
    // Helpers
158
159
160
    /**
161
     * @param   Rule    $rule
162
     * @return  int|null
163
     */
164 12
    protected function rule_id(Rule $rule) {
165 12
        $res = $this->builder()
166 12
            ->select("id")
167 12
            ->from($this->rule_table())
168 12
            ->where("rule = ?")
169 12
            ->setParameter(0, $rule->pprint())
170 12
            ->execute()
171 12
            ->fetch();
172 12
        if ($res) {
173 7
            return (int)$res["id"];
174
        }
175
        else {
176 12
            return null;
177
        }
178
    }
179
180
    /**
181
     * @param   Variable $var
182
     * @return  int|null
183
     */
184 12 View Code Duplication
    protected function variable_id(Variable $var) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
185 12
        $res = $this->builder()
186 12
            ->select("id")
187 12
            ->from($this->variable_table())
188 12
            ->where($this->builder()->expr()->andX
189 12
                ( "name = ?"
190 12
                , "meaning = ?"
191 12
                ))
192 12
            ->setParameter(0, $var->name())
193 12
            ->setParameter(1, $var->meaning())
194 12
            ->execute()
195 12
            ->fetch();
196 12
        if ($res) {
197 7
            return (int)$res["id"];
198
        }
199
        else {
200 12
            return null;
201
        }
202
    }
203
204 12
    protected function upsert_variable(Variable $var) {
205 12
        $var_id = $this->variable_id($var);
206 12
        if ($var_id === null) {
207 12
            $var_id = $this->insert_variable($var);            
208 12
        }
209
        else {
210 7
            $this->update_variable($var, $var_id);
211
        }
212 12
        return $var_id;
213
    }
214
215 12
    protected function insert_variable(Variable $var) {
216 12
        assert('$this->current_run_id !== null');
217 12
        $this->builder()
218 12
            ->insert($this->variable_table())
219 12
            ->values(array
220
                ( "name" => "?"
221 12
                , "meaning" => "?"
222 12
                , "first_seen" => "?"
223 12
                , "last_seen" => "?"
224 12
                ))
225 12
            ->setParameter(0, $var->name())
226 12
            ->setParameter(1, $var->meaning())
227 12
            ->setParameter(2, $this->current_run_id)
228 12
            ->setParameter(3, $this->current_run_id)
229 12
            ->execute();
230 12
        return (int)$this->connection->lastInsertId();
231
    }
232
233 7
    protected function update_variable(Variable $var, $var_id) {
0 ignored issues
show
Unused Code introduced by
The parameter $var is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
234 7
        assert('is_integer($var_id)');
235 7
        assert('$this->current_run_id !== null');
236 7
        $this->builder()
237 7
            ->update($this->variable_table())
238 7
            ->set("last_seen", "?")
239 7
            ->where("id = ?")
240 7
            ->setParameter(0, $this->current_run_id)
241 7
            ->setParameter(1, $var_id)
242 7
            ->execute();
243 7
    }
244
245
    /**
246
     * @param   Violation   $violation
247
     * @return  int|null
248
     */
249 7 View Code Duplication
    protected function violation_id(Violation $violation) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
250 7
        $res = $this->builder()
251 7
            ->select("id")
252 7
            ->from($this->violation_table())
253 7
            ->where($this->builder()->expr()->andX
254 7
                ( "rule_id = ?"
255 7
                , "file = ?"
256 7
                , "line = ?"
257 7
                ))
258 7
            ->setParameter(0, $this->current_rule_id)
259 7
            ->setParameter(1, $violation->filename())
260 7
            ->setParameter(2, $violation->line())
261 7
            ->execute()
262 7
            ->fetch();
263 7
        if ($res) {
264 3
            return (int)$res["id"];
265
        }
266
        else {
267 7
            return null;
268
        }
269
    }
270
271
    // Names
272
273 20
    public function run_table() {
274 20
        return "runs";    
275
    }
276
277 20
    public function variable_table() {
278 20
        return "variables";
279
    }
280
281 20
    public function rule_table() {
282 20
        return "rules";
283
    }
284
285 20
    public function violation_table() {
286 20
        return "violations";
287
    }
288
289 20
    public function violation_location_table() {
290 20
        return "violation_locations";
291
    }
292
293
    // Creation of database.
294
295 20
    public function init_database_schema() {
296 20
        $schema = new Schema\Schema();
297
298 20
        $run_table = $schema->createTable($this->run_table());
299 20
        $run_table->addColumn
300 20
            ("id", "integer"
301 20
            , array("notnull" => true, "unsigned" => true, "autoincrement" => true)
302 20
            );
303 20
        $run_table->addColumn
304 20
            ( "commit_hash", "string"
305 20
            , array("notnull" => true)
306 20
            );
307
        // TODO: maybe add time
308
        // TODO: do we need some other meta information per run of the analysis? 
309 20
        $run_table->setPrimaryKey(array("id"));
310
311
312 20
        $rule_table = $schema->createTable($this->rule_table());
313 20
        $rule_table->addColumn
314 20
            ( "id", "integer"
315 20
            , array("notnull" => true)
316 20
            , array("notnull" => true, "unsigned" => true, "autoincrement" => true)
0 ignored issues
show
Unused Code introduced by
The call to Table::addColumn() has too many arguments starting with array('notnull' => true,...autoincrement' => true).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
317 20
            );
318 20
        $rule_table->addColumn
319 20
            ( "rule", "string"
320 20
            , array("notnull" => true)
321 20
            );
322 20
        $rule_table->addColumn
323 20
            ( "first_seen", "integer"
324 20
            , array("notnull" => true)
325 20
            );
326 20
        $rule_table->addColumn
327 20
            ( "last_seen", "integer"
328 20
            , array("notnull" => true)
329 20
            );
330 20
        $rule_table->setPrimaryKey(array("id"));
331 20
        $rule_table->addUniqueIndex(array("rule"));
332 20
        $rule_table->addForeignKeyConstraint
333 20
            ( $run_table
334 20
            , array("first_seen")
335 20
            , array("id")
336 20
            );
337 20
        $rule_table->addForeignKeyConstraint
338 20
            ( $run_table
339 20
            , array("last_seen")
340 20
            , array("id")
341 20
            );
342
343
        
344 20
        $variable_table = $schema->createTable($this->variable_table());
345 20
        $variable_table->addColumn
346 20
            ( "id", "integer"
347 20
            , array("notnull" => true)
348 20
            );
349 20
        $variable_table->addColumn
350 20
            ( "name", "string"
351 20
            , array("notnull" => true)
352 20
            );
353 20
        $variable_table->addColumn
354 20
            ( "meaning", "string"
355 20
            , array("notnull" => true)
356 20
            );
357 20
        $variable_table->addColumn
358 20
            ( "first_seen", "integer"
359 20
            , array("notnull" => true)
360 20
            );
361 20
        $variable_table->addColumn
362 20
            ( "last_seen", "integer"
363 20
            , array("notnull" => true)
364 20
            );
365 20
        $variable_table->setPrimaryKey(array("id"));
366
367
368 20
        $violation_table = $schema->createTable($this->violation_table());
369 20
        $violation_table->addColumn
370 20
            ( "id", "integer"
371 20
            , array("notnull" => true, "unsigned" => true, "autoincrement" => true)
372 20
            );
373 20
        $violation_table->addColumn
374 20
            ( "rule_id", "integer"
375 20
            , array("notnull" => true)
376 20
            );
377 20
        $violation_table->addColumn
378 20
            ( "file", "string"
379 20
            , array("notnull" => true)
380 20
            );
381 20
        $violation_table->addColumn
382 20
            ( "line", "string"
383 20
            , array("notnull" => true)
384 20
            );
385 20
        $violation_table->addColumn
386 20
            ( "first_seen", "integer"
387 20
            , array("notnull" => true)
388 20
            );
389 20
        $violation_table->addColumn
390 20
            ( "last_seen", "integer"
391 20
            , array("notnull" => true)
392 20
            );
393 20
        $violation_table->setPrimaryKey(array("id"));
394 20
        $violation_table->addUniqueIndex(array("rule_id", "file", "line"));
395 20
        $violation_table->addForeignKeyConstraint
396 20
            ( $rule_table
397 20
            , array("rule_id")
398 20
            , array("id")
399 20
            );
400 20
        $violation_table->addForeignKeyConstraint
401 20
            ( $run_table
402 20
            , array("first_seen")
403 20
            , array("id")
404 20
            );
405 20
        $violation_table->addForeignKeyConstraint
406 20
            ( $run_table
407 20
            , array("last_seen")
408 20
            , array("id")
409 20
            );
410
411
412 20
        $violation_location_table = $schema->createTable($this->violation_location_table());
413
414 20
        $violation_location_table->addColumn
415 20
            ( "id", "integer"
416 20
            , array("notnull" => true, "unsigned" => true, "autoincrement" => true)
417 20
            );
418 20
        $violation_location_table->addColumn
419 20
            ( "violation_id", "integer"
420 20
            , array("notnull" => true)
421 20
            );
422 20
        $violation_location_table->addColumn
423 20
            ( "run_id", "integer"
424 20
            , array("notnull" => true)
425 20
            );
426 20
        $violation_location_table->addColumn
427 20
            ( "line_no", "integer"
428 20
            , array("notnull" => true)
429 20
            );
430 20
        $violation_location_table->setPrimaryKey(array("id"));
431 20
        $violation_location_table->addForeignKeyConstraint
432 20
            ( $violation_table
433 20
            , array("violation_id")
434 20
            , array("id")
435 20
            );
436 20
        $violation_location_table->addForeignKeyConstraint
437 20
            ( $run_table
438 20
            , array("run_id")
439 20
            , array("id")
440 20
            );
441
442 20
        $sync = new SingleDatabaseSynchronizer($this->connection);
443 20
        $sync->createSchema($schema);
444 20
    }
445
}
446