Completed
Push — master ( 4857c1...ffa509 )
by Dominik
02:16
created

QueryBuilderGenerator   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 179
Duplicated Lines 22.35 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
wmc 32
lcom 1
cbo 11
dl 40
loc 179
rs 9.6
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A createQueryBuilderNode() 0 4 1
A createObjectNode() 0 4 1
A createArrayNode() 0 4 1
B createScalarNode() 16 16 6
A __construct() 0 4 1
A generateByJson() 0 16 2
C structureCode() 0 32 8
B appendChildrenToArrayNode() 12 22 6
B appendChildrenToObjectNode() 12 22 6

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
declare(strict_types=1);
4
5
namespace Saxulum\ElasticSearchQueryBuilder;
6
7
use PhpParser\Node\Arg;
8
use PhpParser\Node\Expr;
9
use PhpParser\Node\Expr\Assign;
10
use PhpParser\Node\Expr\ConstFetch;
11
use PhpParser\Node\Expr\MethodCall;
12
use PhpParser\Node\Expr\New_;
13
use PhpParser\Node\Expr\Variable;
14
use PhpParser\Node\Name;
15
use PhpParser\Node\Scalar\DNumber;
16
use PhpParser\Node\Scalar\LNumber;
17
use PhpParser\Node\Scalar\String_;
18
use PhpParser\PrettyPrinter\Standard as PhpGenerator;
19
20
final class QueryBuilderGenerator
21
{
22
    /**
23
     * @var PhpGenerator
24
     */
25
    private $phpGenerator;
26
27
    /**
28
     * @param PhpGenerator $phpGenerator
29
     */
30
    public function __construct(PhpGenerator $phpGenerator)
31
    {
32
        $this->phpGenerator = $phpGenerator;
33
    }
34
35
    /**
36
     * @param $query
37
     * @return string
38
     */
39
    public function generateByJson($query): string
40
    {
41
        $data = json_decode($query, false);
42
        if (JSON_ERROR_NONE !== json_last_error()) {
43
            throw new \InvalidArgumentException(sprintf('Message: %s, query: %s', json_last_error_msg(), $query));
44
        }
45
46
        $queryBuilder = new Variable('queryBuilder');
47
48
        $stmts = [];
49
50
        $stmts[] = $this->createQueryBuilderNode();
51
        $stmts[] = $this->appendChildrenToObjectNode($queryBuilder, $queryBuilder, $data);
52
53
        return $this->structureCode($this->phpGenerator->prettyPrint($stmts));
54
    }
55
56
    /**
57
     * @param string $code
58
     * @return string
59
     */
60
    private function structureCode(string $code): string
61
    {
62
        $codeWithLinebreaks = str_replace('->add', "\n->add", $code);
63
        $codeWithLinebreaks = str_replace('->end', "\n->end", $codeWithLinebreaks);
64
65
        $lines = explode("\n", $codeWithLinebreaks);
66
67
        $position = 0;
68
69
        $structuredLines = [];
70
71
        foreach ($lines as $i => $line) {
72
            $lastLine = $lines[$i-1] ?? '';
73
            if (0 === strpos($line, '->add')) {
74
                if (false === strpos($lastLine, '->end') && false === strpos($lastLine, '->scalarNode')) {
75
                    $position++;
76
                }
77
                $structuredLines[] = str_pad('', $position * 4) . $line;
78
            } elseif (0 === strpos($line, '->end')) {
79
                if (strpos($lastLine, '->objectNode') || strpos($lastLine, '->arrayNode')) {
80
                    $structuredLines[count($structuredLines) - 1] .= '->end()';
81
                } else {
82
                    $position--;
83
                    $structuredLines[] = str_pad('', $position * 4) . $line;
84
                }
85
            } else {
86
                $structuredLines[] = $line;
87
            }
88
        }
89
90
        return implode("\n", $structuredLines);
91
    }
92
93
    /**
94
     * @return Expr
95
     */
96
    private function createQueryBuilderNode(): Expr
97
    {
98
        return new Assign(new Variable('queryBuilder'), new New_(new Name('QueryBuilder')));
99
    }
100
101
    /**
102
     * @param Expr $expr
103
     * @return Expr
104
     */
105
    private function createObjectNode(Expr $expr): Expr
106
    {
107
        return new MethodCall($expr, 'objectNode');
108
    }
109
110
    /**
111
     * @param Expr $expr
112
     * @return Expr
113
     */
114
    private function createArrayNode(Expr $expr): Expr
115
    {
116
        return new MethodCall($expr, 'arrayNode');
117
    }
118
119
    /**
120
     * @param Expr $expr
121
     * @param string|float|int|bool|null $value
122
     * @return Expr
123
     */
124 View Code Duplication
    private function createScalarNode(Expr $expr, $value): Expr
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...
125
    {
126
        if (is_string($value)) {
127
            $valueExpr = new String_($value);
128
        } elseif (is_int($value)) {
129
            $valueExpr = new LNumber($value);
130
        } elseif (is_float($value)) {
131
            $valueExpr = new DNumber($value);
132
        } elseif (is_bool($value)) {
133
            $valueExpr = new ConstFetch(new Name($value ? 'true' : 'false'));
134
        } else {
135
            $valueExpr = new ConstFetch(new Name('null'));
136
        }
137
138
        return new MethodCall($expr, 'scalarNode', [new Arg($valueExpr)]);
139
    }
140
141
    /**
142
     * @param Expr $queryBuilder
143
     * @param Expr $expr
144
     * @param array $data
145
     * @return Expr
146
     */
147
    private function appendChildrenToArrayNode(Expr $queryBuilder, Expr $expr, array $data)
148
    {
149
        foreach ($data as $value) {
150 View Code Duplication
            if ($value instanceof \stdClass) {
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...
151
                $argument = $this->createObjectNode($queryBuilder);
152
            } elseif (is_array($value)) {
153
                $argument = $this->createArrayNode($queryBuilder);
154
            } else {
155
                $argument = $this->createScalarNode($queryBuilder, $value);
156
            }
157
158
            $expr = new MethodCall($expr, 'addToArrayNode', [new Arg($argument)]);
159
160 View Code Duplication
            if ($value instanceof \stdClass) {
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...
161
                $expr = new MethodCall($this->appendChildrenToObjectNode($queryBuilder, $expr, $value), 'end');
162
            } elseif (is_array($value)) {
163
                $expr = new MethodCall($this->appendChildrenToArrayNode($queryBuilder, $expr, $value), 'end');
164
            }
165
        }
166
167
        return $expr;
168
    }
169
170
    /**
171
     * @param Expr $queryBuilder
172
     * @param Expr $expr
173
     * @param \stdClass $data
174
     * @return Expr
175
     */
176
    private function appendChildrenToObjectNode(Expr $queryBuilder, Expr $expr, \stdClass $data)
177
    {
178
        foreach ($data as $key => $value) {
0 ignored issues
show
Bug introduced by
The expression $data of type object<stdClass> is not traversable.
Loading history...
179 View Code Duplication
            if ($value instanceof \stdClass) {
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...
180
                $argument = $this->createObjectNode($queryBuilder);
181
            } elseif (is_array($value)) {
182
                $argument = $this->createArrayNode($queryBuilder);
183
            } else {
184
                $argument = $this->createScalarNode($queryBuilder, $value);
185
            }
186
187
            $expr = new MethodCall($expr, 'addToObjectNode', [new Arg(new String_($key)), new Arg($argument)]);
188
189 View Code Duplication
            if ($value instanceof \stdClass) {
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...
190
                $expr = new MethodCall($this->appendChildrenToObjectNode($queryBuilder, $expr, $value), 'end');
191
            } elseif (is_array($value)) {
192
                $expr = new MethodCall($this->appendChildrenToArrayNode($queryBuilder, $expr, $value), 'end');
193
            }
194
        }
195
196
        return $expr;
197
    }
198
}
199