Completed
Push — master ( cce771...f52bd9 )
by Richard
05:07
created

IndexDB::init_table()   C

Complexity

Conditions 19
Paths 114

Size

Total Lines 57
Code Lines 46

Duplication

Lines 12
Ratio 21.05 %

Code Coverage

Tests 52
CRAP Score 19.0584

Importance

Changes 0
Metric Value
dl 12
loc 57
ccs 52
cts 55
cp 0.9455
rs 6.0221
c 0
b 0
f 0
cc 19
eloc 46
nc 114
nop 4
crap 19.0584

How to fix   Long Method    Complexity   

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 license along with the code.
9
 */
10
11
namespace Lechimp\Dicto\App;
12
13
use Lechimp\Dicto\Graph;
14
use Lechimp\Dicto\Indexer\Insert;
15
use Doctrine\DBAL\Schema;
16
use Doctrine\DBAL\Types\Type;
17
use Doctrine\DBAL\Schema\Synchronizer\SingleDatabaseSynchronizer;
18
use Doctrine\DBAL\Statement;
19
20
class IndexDB extends DB implements Insert {
21
    protected $nodes_per_insert = 200;
22
23
    /**
24
     * @var array
25
     */
26
    protected $tables =
27
        [ "files" => ["_file",
28
            ["id", "path", "source"]]
29
        , "namespaces" => ["_namespace",
30
            ["id", "name"]]
31
        , "classes" => ["_class",
32
            ["id", "name", "file_id", "start_line", "end_line", "namespace_id"]]
33
        , "interfaces" => ["_interface",
34
            ["id", "name", "file_id", "start_line", "end_line", "namespace_id"]]
35
        , "traits" => ["_trait",
36
            ["id", "name", "file_id", "start_line", "end_line", "namespace_id"]]
37
        , "methods" => ["_method",
38
            ["id", "name", "class_id", "file_id", "start_line", "end_line"]]
39
        , "functions" => ["_function",
40
            ["id", "name", "file_id", "start_line", "end_line", "namespace_id"]]
41
        , "globals" => ["_global",
42
            ["id", "name"]]
43
        , "language_constructs" => ["_language_construct",
44
            ["id", "name"]]
45
        , "method_references" => ["_method_reference",
46
            ["id", "name", "file_id", "line", "column"]]
47
        , "function_references" => ["_function_reference",
48
            ["id", "name", "file_id", "line", "column"]]
49
        , "relations" => ["_relation",
50
            ["left_id", "relation", "right_id", "file_id", "line"]]
51
        ];
52
53
    /**
54
     * Lists of fields that contain an id.
55
     *
56
     * @var array<string,int>
57
     */
58
    protected $id_fields =
59
        [ "file_id" => 0
60
        , "namespace_id" => 0
61
        , "class_id" => 0
62
        , "left_id" => 0
63
        , "right_id" => 0
64
        ];
65
66
    /**
67
     * List of all fields that contain an integer.
68
     *
69
     * @var array<string,0>
70
     */
71
    protected $int_fields =
72
        [ "start_line" => 0
73
        , "end_line" => 0
74
        , "line" => 0
75
        , "column" => 0
76
        ];
77
78
    /**
79
     * @var array[]
80
     */
81
    protected $caches = [];
82
83
    /**
84
     * @var integer
85
     */
86
    protected $id_counter = 0;
87
88 1
    public function __construct($connection) {
89 1
        parent::__construct($connection);
90 1
        foreach ($this->tables as $table => $_) {
91 1
            $this->caches[$table] = [];
92 1
        }
93 1
    }
94
95
    /**
96
     * @inheritdocs
97
     */
98 1
    public function _file($path, $source) {
99 1
        return $this->append_and_maybe_flush("files",
100 1
            [null, $this->esc_str($path), $this->esc_str($source)]);
101
    }
102
103
    /**
104
     * @inheritdocs
105
     */
106 1
    public function _namespace($name) {
107 1
        return $this->append_and_maybe_flush("namespaces",
108 1
            [null, $this->esc_str($name)]);
109
    }
110
111
    /**
112
     * @inheritdocs
113
     */
114 1
    public function _class($name, $file, $start_line, $end_line, $namespace = null) {
115 1
        return $this->append_and_maybe_flush("classes",
116 1
            [null, $this->esc_str($name), $file, $start_line, $end_line, $this->esc_maybe_null($namespace)]);
117
    }
118
119
    /**
120
     * @inheritdocs
121
     */
122
    public function _interface($name, $file, $start_line, $end_line, $namespace = null) {
123
        return $this->append_and_maybe_flush("interfaces",
124
            [null, $this->esc_str($name), $file, $start_line, $end_line, $this->esc_maybe_null($namespace)]);
125
    }
126
127
    /**
128
     * @inheritdocs
129
     */
130
    public function _trait($name, $file, $start_line, $end_line, $namespace = null) {
131
        return $this->append_and_maybe_flush("traits",
132
            [null, $this->esc_str($name), $file, $start_line, $end_line, $this->esc_maybe_null($namespace)]);
133
    }
134
135
    /**
136
     * @inheritdocs
137
     */
138 1
    public function _method($name, $class, $file, $start_line, $end_line) {
139 1
        return $this->append_and_maybe_flush("methods",
140 1
            [null, $this->esc_str($name), $class, $file, $start_line, $end_line]);
141
    }
142
143
    /**
144
     * @inheritdocs
145
     */
146 1
    public function _function($name, $file, $start_line, $end_line, $namespace = null) {
147 1
        return $this->append_and_maybe_flush("functions",
148 1
            [null, $this->esc_str($name), $file, $start_line, $end_line, $this->esc_maybe_null($namespace)]);
149
    }
150
151
    /**
152
     * @inheritdocs
153
     */
154 1
    public function _global($name) {
155 1
        return $this->append_and_maybe_flush("globals",
156 1
            [null, $this->esc_str($name)]);
157
    }
158
159
    /**
160
     * @inheritdocs
161
     */
162 1
    public function _language_construct($name) {
163 1
        return $this->append_and_maybe_flush("language_constructs",
164 1
            [null, $this->esc_str($name)]);
165
    }
166
167
    /**
168
     * @inheritdocs
169
     */
170 1
    public function _method_reference($name, $file, $line, $column) {
171 1
        return $this->append_and_maybe_flush("method_references",
172 1
            [null, $this->esc_str($name), $file, $line, $column]);
173
    }
174
175
    /**
176
     * @inheritdocs
177
     */
178 1
    public function _function_reference($name, $file, $line, $column) {
179 1
        return $this->append_and_maybe_flush("function_references",
180 1
            [null, $this->esc_str($name), $file, $line, $column]);
181
    }
182
183
    /**
184
     * @inheritdocs
185
     */
186 1
    public function _relation($left_entity, $relation, $right_entity, $file, $line) {
187 1
        $this->append_and_maybe_flush("relations",
188 1
            [$left_entity, $this->esc_str($relation), $right_entity, $file, $line]);
189 1
    }
190
191 1
    protected function insert_cache($table) {
192 1
        assert('array_key_exists($table, $this->tables)');
193 1
        $fields = $this->tables[$table][1];
194 1
        $which = &$this->caches[$table];
195 1
        if (count($which) == 0) {
196 1
            return;
197
        }
198 1
        $stmt = "INSERT INTO $table (".implode(", ", $fields).") VALUES\n";
199 1
        $values = [];
200 1
        foreach ($which as $v) {
201 1
            $values[] = "(".implode(", ", $v).")";
202 1
        }
203 1
        $stmt .= implode(",\n", $values).";";
204 1
        $this->connection->exec($stmt);
205 1
        $which = [];
206 1
    }
207
208 1
    protected function append_and_maybe_flush($table, $values) {
209 1
        if ($values[0] === null) {
210 1
            $id = $this->id_counter++;
211 1
            $values[0] = $id;
212 1
        }
213
        else {
214 1
            $id = null;
215
        }
216
217 1
        $which = &$this->caches[$table];
218 1
        $which[] = $values;
219 1
        if (count($which) > $this->nodes_per_insert) {
220
            $this->insert_cache($table);
221
        }
222
223 1
        return $id;
224
    }
225 1
    protected function esc_str($str) {
226 1
        assert('is_string($str)');
227 1
        return '"'.str_replace('"', '""', $str).'"';
228
    }
229
230 1
    protected function esc_maybe_null($val) {
231 1
        assert('!is_string($val)');
232 1
        if ($val === null) {
233 1
            return "NULL";
234
        }
235 1
        return $val;
236
    }
237
238
    /**
239
     * Write everything that currently is in cache to the database.
240
     *
241
     * @return null
242
     */
243 1
    public function write_cached_inserts() {
244 1
        foreach ($this->tables as $table => $_) {
245 1
            $this->insert_cache($table);
246 1
        }
247 1
    }
248
249
    /**
250
     * Read the index from the database.
251
     *
252
     * @return  Graph\IndexDB   $index
253
     */
254 1
    public function to_graph_index() {
255 1
        $index = $this->build_graph_index_db();
256
        $reader = new IndexDBReader
257 1
            ( $this->tables
258 1
            , $this->id_fields
259 1
            , $this->int_fields
260
            , function () { return $this->builder(); }
261 1
            , $index
262 1
            );
263 1
        $reader->run();
264 1
        return $index;
265
    }
266
267
    protected function build_graph_index_db() {
268
        return new Graph\IndexDB();
269
    }
270
271
    // INIT DATABASE
272
273 1
    public function init_table($name, Schema\Schema $schema, Schema\Table $file_table = null, Schema\Table $namespace_table = null) {
274 1
        assert('array_key_exists($name, $this->tables)');
275 1
        $table = $schema->createTable($name);
276 1
        foreach ($this->tables[$name][1] as $field) {
277
            switch ($field) {
278 1
                case "id":
279 1
                case "file_id":
280 1
                case "class_id":
281 1
                case "left_id":
282 1
                case "right_id":
283 1
                case "start_line":
284 1
                case "end_line":
285 1
                case "line":
286 1 View Code Duplication
                case "column":
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...
287 1
                    $table->addColumn
288 1
                        ($field, "integer"
289 1
                        , ["notnull" => true, "unsigned" => true]
290 1
                        );
291 1
                    break;
292 1 View Code Duplication
                case "namespace_id":
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...
293 1
                    $table->addColumn
294 1
                        ($field, "integer"
295 1
                        , ["notnull" => false, "unsigned" => true]
296 1
                        );
297 1
                   break;
298 1
                case "path":
299 1
                case "source":
300 1
                case "name":
301 1
                case "relation":
302 1
                    $table->addColumn
303 1
                        ($field, "string"
304 1
                        , ["notnull" => true]
305 1
                        );
306 1
                    break;
307
                default:
308
                    throw new \LogicException("Unknown field '$field'");
309
            }
310 1
            if ($field == "id") {
311 1
                $table->setPrimaryKey(["id"]);
312 1
            }
313 1
            if ($field == "file_id") {
314 1
                $table->addForeignKeyConstraint
315 1
                    ( $file_table
0 ignored issues
show
Bug introduced by
It seems like $file_table defined by parameter $file_table on line 273 can be null; however, Doctrine\DBAL\Schema\Tab...dForeignKeyConstraint() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
316 1
                    , array("file_id")
317 1
                    , array("id")
318 1
                    );
319 1
            }
320 1
            if ($field == "namespace_id") {
321 1
                $table->addForeignKeyConstraint
322 1
                    ( $namespace_table
0 ignored issues
show
Bug introduced by
It seems like $namespace_table defined by parameter $namespace_table on line 273 can be null; however, Doctrine\DBAL\Schema\Tab...dForeignKeyConstraint() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
323 1
                    , array("namespace_id")
324 1
                    , array("id")
325 1
                    );
326 1
            }
327 1
        }
328 1
        return $table;
329
    }
330
331 1
    public function init_database_schema() {
332 1
        $schema = new Schema\Schema();
333
334 1
        $file_table = $this->init_table("files", $schema);
335 1
        $namespace_table = $this->init_table("namespaces", $schema);
336 1
        $this->init_table("classes", $schema, $file_table, $namespace_table);
337 1
        $this->init_table("interfaces", $schema, $file_table, $namespace_table);
338 1
        $this->init_table("traits", $schema, $file_table, $namespace_table);
339 1
        $this->init_table("methods", $schema, $file_table);
340 1
        $this->init_table("functions", $schema, $file_table, $namespace_table);
341 1
        $this->init_table("globals", $schema);
342 1
        $this->init_table("language_constructs", $schema);
343 1
        $this->init_table("method_references", $schema, $file_table);
344 1
        $this->init_table("function_references", $schema, $file_table);
345 1
        $relation_table = $this->init_table("relations", $schema, $file_table);
346 1
        $relation_table->setPrimaryKey(["left_id", "relation", "right_id"]);
347
348 1
        $sync = new SingleDatabaseSynchronizer($this->connection);
349 1
        $sync->createSchema($schema);
350 1
    }
351
}
352