Completed
Push — master ( 4fd125...05840b )
by Richard
05:36
created

QueryImpl   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 201
Duplicated Lines 7.96 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 97.14%

Importance

Changes 0
Metric Value
wmc 28
lcom 1
cbo 5
dl 16
loc 201
ccs 102
cts 105
cp 0.9714
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 6 1
B run() 0 23 5
A switch_run_command() 0 15 4
A run_expand() 8 10 3
A run_extract() 0 11 3
A run_filter() 8 11 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
        $clone->steps[] = ["filter", $predicate];
68 54
        assert('$this->steps != $clone->steps');
69 54
        return $clone;
70
    }
71
72
    /**
73
     * @inheritdocs
74
     */
75 60
    public function run($result) {
76 60
        $steps = $this->steps;
77 60
        if (count($steps) > 0 && $steps[0][0] == "filter") {
78 54
            $nodes = $this->graph->nodes($steps[0][1]);
79 54
            array_shift($steps);
80 54
        }
81
        else {
82 6
            $nodes = $this->graph->nodes();
83
        }
84 60
        $nodes = $this->add_result($nodes, $result);
85
86 60
        foreach ($this->steps as $step) {
87 60
            $nodes = $this->switch_run_command($nodes, $step);
88 60
        }
89
90 60
        $res = array();
91 60
        while ($nodes->valid()) {
92 43
            $val = $nodes->current();
93 43
            $res[] = $val[1];
94 43
            $nodes->next();
95 43
        }
96 60
        return $res;
97
    }
98
99
    /**
100
     * @return  Iterator <[Node,mixed]>
101
     */
102 60
    protected function switch_run_command(\Iterator $nodes, $step) {
103 60
        list($cmd,$par) = $step;
104 60
        if ($cmd == "expand") {
105 34
            return $this->run_expand($nodes, $par);
106
        }
107 60
        elseif ($cmd == "extract") {
108 60
            return $this->run_extract($nodes, $par);
109
        }
110 54
        elseif ($cmd == "filter") {
111 54
            return $this->run_filter($nodes, $par);
112
        }
113
        else {
114
            throw new \LogicException("Unknown command: $cmd");
115
        }
116
    }
117
118
    /**
119
     * @return  Iterator <[Node,mixed]>
120
     */
121 34
    protected function run_expand(\Iterator $nodes, \Closure $clsr) {
122 34 View Code Duplication
        while ($nodes->valid()) {
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...
123 27
            list($node, $result) = $nodes->current();
124
            // TODO: let closure return an Iterator too.
125 27
            foreach($clsr($node) as $new_node) {
126 22
                yield [$new_node, $result];
127 27
            }
128 27
            $nodes->next();
129 27
        }
130 34
    }
131
132
    /**
133
     * @return  Iterator <[Node,mixed]>
134
     */
135 60
    protected function run_extract(\Iterator $nodes, \Closure $clsr) {
136 60
        while ($nodes->valid()) {
137 44
            list($node, $result) = $nodes->current();
138 44
            if (is_object($result)) {
139
                $result = clone($result);
140
            }
141 44
            $clsr($node, $result);
142 44
            yield [$node, $result];
143 44
            $nodes->next();
144 44
        }
145 60
    }
146
147
    /**
148
     * @return  Iterator <[Node,mixed]>
149
     */
150 54
    protected function run_filter(\Iterator $nodes, Predicate $predicate) {
151 54
        $clsr = $predicate->compile();
152 54 View Code Duplication
        while ($nodes->valid()) {
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...
153 52
            $val = $nodes->current();
154 52
            list($node, $result) = $val;
155 52
            if ($clsr($node, $result)) {
156 52
                yield $val;
157 52
            }
158 52
            $nodes->next();
159 52
        }
160 54
    }
161
162
    /**
163
     * @return  Iterator <[Node,mixed]>
164
     */
165 60
    protected function add_result(\Iterator $nodes, &$result) {
166 60
        while ($nodes->valid()) {
167 57
            $node = $nodes->current();
168 57
            yield [$node, $result];
169 57
            $nodes->next();
170 57
        }
171 60
    }
172
173
    // Convenience Functions
174
175
    /**
176
     * @inheritdocs
177
     */
178 13
    public function filter_by_types(array $types) {
179 13
        $f = $this->predicate_factory();
180 13
        return $this->filter
181 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...
182 13
                ( array_map
183
                    ( function($t) use ($f) {
184 13
                            return $f->_type_is($t);
185
                        }
186 13
                    , $types
187 13
                    )
188 13
                )
189 13
            );
190
    }
191
192
    /**
193
     * @inheritdocs
194
     */
195 29
    public function expand_relations(array $types) {
196
        return $this->expand(function(Node $n) use (&$types) {
197
            return array_filter
198 23
                ( $n->relations()
199
                , function(Relation $r) use (&$types) {
200 22
                    return in_array($r->type(), $types);
201 23
                });
202 29
        });
203
    }
204
205
    /**
206
     * @inheritdocs
207
     */
208
    public function expand_target() {
209 18
        return $this->expand(function(Relation $r) {
210 14
            return [$r->target()];
211 18
        });
212
    }
213
}
214