Completed
Push — master ( 9b2a73...93cace )
by Richard
05:06
created

QueryImpl   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 209
Duplicated Lines 9.57 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 97.2%

Importance

Changes 0
Metric Value
wmc 29
lcom 1
cbo 5
dl 20
loc 209
ccs 104
cts 107
cp 0.972
rs 10
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A predicate_factory() 0 3 1
A expand() 0 6 1
A extract() 0 6 1
A filter() 0 15 2
B run() 0 23 5
A switch_run_command() 0 15 4
A run_expand() 10 10 3
A run_extract() 0 11 3
A run_filter() 10 10 3
A add_result() 0 7 2
A filter_by_types() 0 13 1
A expand_relations() 0 9 1
A expand_target() 0 5 1

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 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\Graph;
12
13
class QueryImpl implements Query {
14
    /**
15
     * @var Graph
16
     */
17
    protected $graph;
18
19
    /**
20
     * @var array[]
21
     */
22
    protected $steps;
23
24
    /**
25
     * @var PredicateFactory
26
     */
27
    protected $predicate_factory;
28
29 61
    public function __construct(Graph $graph) {
30 61
        $this->graph = $graph;
31 61
        $this->steps = [];
32 61
        $this->predicate_factory = new PredicateFactory();
33 61
    }
34
35
    /**
36
     * @inheritdocs
37
     */
38 41
    public function predicate_factory() {
39 41
        return $this->predicate_factory;
40
    }
41
42
    /**
43
     * @inheritdocs
44
     */
45 34
    public function expand(\Closure $expander) {
46 34
        $clone = clone $this;
47 34
        $clone->steps[] = ["expand", $expander];
48 34
        assert('$this->steps != $clone->steps');
49 34
        return $clone;
50
    }
51
52
    /**
53
     * @inheritdocs
54
     */
55 60
    public function extract(\Closure $extractor) {
56 60
        $clone = clone $this;
57 60
        $clone->steps[] = ["extract", $extractor];
58 60
        assert('$this->steps != $clone->steps');
59 60
        return $clone;
60
    }
61
62
    /**
63
     * @inheritdocs
64
     */
65 54
    public function filter(Predicate $predicate) {
66 54
        $clone = clone $this;
67 54
        if (count($this->steps) == 0) {
68
            // On the first step we keep the predicate to later put it into
69
            // graph->nodes.
70 54
            $clone->steps[] = ["filter", $predicate];
71 54
        }
72
        else {
73
            // For later steps, we already compile the predicate to only
74
            // compile it once.
75 27
            $clone->steps[] = ["filter", $predicate->compile()];
76
        }
77 54
        assert('$this->steps != $clone->steps');
78 54
        return $clone;
79
    }
80
81
    /**
82
     * @inheritdocs
83
     */
84 60
    public function run($result) {
85 60
        $steps = $this->steps;
86 60
        if (count($steps) > 0 && $steps[0][0] == "filter") {
87 54
            $nodes = $this->graph->nodes($steps[0][1]);
88 54
            array_shift($steps);
89 54
        }
90
        else {
91 6
            $nodes = $this->graph->nodes();
92
        }
93 60
        $nodes = $this->add_result($nodes, $result);
94
95 60
        foreach ($steps as $step) {
96 60
            $nodes = $this->switch_run_command($nodes, $step);
97 60
        }
98
99 60
        $res = array();
100 60
        while ($nodes->valid()) {
101 43
            $val = $nodes->current();
102 43
            $res[] = $val[1];
103 43
            $nodes->next();
104 43
        }
105 60
        return $res;
106
    }
107
108
    /**
109
     * @return  Iterator <[Node,mixed]>
110
     */
111 60
    protected function switch_run_command(\Iterator $nodes, $step) {
112 60
        list($cmd,$par) = $step;
113 60
        if ($cmd == "expand") {
114 34
            return $this->run_expand($nodes, $par);
115
        }
116 60
        elseif ($cmd == "extract") {
117 60
            return $this->run_extract($nodes, $par);
118
        }
119 27
        elseif ($cmd == "filter") {
120 27
            return $this->run_filter($nodes, $par);
121
        }
122
        else {
123
            throw new \LogicException("Unknown command: $cmd");
124
        }
125
    }
126
127
    /**
128
     * @return  Iterator <[Node,mixed]>
129
     */
130 34 View Code Duplication
    protected function run_expand(\Iterator $nodes, \Closure $clsr) {
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...
131 34
        while ($nodes->valid()) {
132 27
            list($node, $result) = $nodes->current();
133
            // TODO: let closure return an Iterator too.
134 27
            foreach($clsr($node) as $new_node) {
135 22
                yield [$new_node, $result];
136 27
            }
137 27
            $nodes->next();
138 27
        }
139 34
    }
140
141
    /**
142
     * @return  Iterator <[Node,mixed]>
143
     */
144 60
    protected function run_extract(\Iterator $nodes, \Closure $clsr) {
145 60
        while ($nodes->valid()) {
146 44
            list($node, $result) = $nodes->current();
147 44
            if (is_object($result)) {
148
                $result = clone($result);
149
            }
150 44
            $clsr($node, $result);
151 44
            yield [$node, $result];
152 44
            $nodes->next();
153 44
        }
154 60
    }
155
156
    /**
157
     * @return  Iterator <[Node,mixed]>
158
     */
159 27 View Code Duplication
    protected function run_filter(\Iterator $nodes, \Closure $predicate) {
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...
160 27
        while ($nodes->valid()) {
161 21
            $val = $nodes->current();
162 21
            list($node, $result) = $val;
163 21
            if ($predicate($node, $result)) {
164 12
                yield $val;
165 12
            }
166 21
            $nodes->next();
167 21
        }
168 27
    }
169
170
    /**
171
     * @return  Iterator <[Node,mixed]>
172
     */
173 60
    protected function add_result(\Iterator $nodes, &$result) {
174 60
        while ($nodes->valid()) {
175 57
            $node = $nodes->current();
176 57
            yield [$node, $result];
177 57
            $nodes->next();
178 57
        }
179 60
    }
180
181
    // Convenience Functions
182
183
    /**
184
     * @inheritdocs
185
     */
186 13
    public function filter_by_types(array $types) {
187 13
        $f = $this->predicate_factory();
188 13
        return $this->filter
189 13
            ( $f->_and
0 ignored issues
show
Bug introduced by
It seems like $f->_and(array_map(funct...ype_is($t); }, $types)) can be null; however, filter() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
190 13
                ( array_map
191
                    ( function($t) use ($f) {
192 13
                            return $f->_type_is($t);
193
                        }
194 13
                    , $types
195 13
                    )
196 13
                )
197 13
            );
198
    }
199
200
    /**
201
     * @inheritdocs
202
     */
203 29
    public function expand_relations(array $types) {
204
        return $this->expand(function(Node $n) use (&$types) {
205
            return array_filter
206 23
                ( $n->relations()
207
                , function(Relation $r) use (&$types) {
208 22
                    return in_array($r->type(), $types);
209 23
                });
210 29
        });
211
    }
212
213
    /**
214
     * @inheritdocs
215
     */
216
    public function expand_target() {
217 18
        return $this->expand(function(Relation $r) {
218 14
            return [$r->target()];
219 18
        });
220
    }
221
}
222