Select::parseJoin()   C
last analyzed

Complexity

Conditions 15
Paths 2

Size

Total Lines 45
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 15.0915

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 15
eloc 29
c 2
b 0
f 0
nc 2
nop 1
dl 0
loc 45
ccs 25
cts 27
cp 0.9259
crap 15.0915
rs 5.9166

How to fix   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
namespace Nip\Database\Query;
4
5
use Nip\Database\Query\Select\Union;
6
7
/**
8
 * Class Select
9
 * @package Nip\Database\Query
10
 *
11
 * @method $this options() options(string $option = null)
12
 * @method $this setFrom() setFrom(string $table = null)
13
 * @method $this setOrder() setOrder(array | string $cols = null)
14
 */
15
class Select extends AbstractQuery
16
{
17
    /**
18
     * @param $name
19
     * @param $arguments
20
     * @return AbstractQuery|Select
21
     */
22
    public function __call($name, $arguments)
23 11
    {
24
        if (in_array($name, ['min', 'max', 'count', 'avg', 'sum'])) {
25 11
            $input = reset($arguments);
26
27
            if (is_array($input)) {
28
                $input[] = false;
29
            } else {
30
                $alias = isset($arguments[1]) ? $arguments[1] : null;
31
                $protected = isset($arguments[2]) ? $arguments[2] : null;
32
                $input = [$input, $alias, $protected];
33
            }
34
35
            $input[0] = strtoupper($name) . '(' . $this->protect($input[0]) . ')';
36
37
            return $this->cols($input);
38
        }
39
40
        return parent::__call($name, $arguments);
41 11
    }
42
43
    /**
44
     * Inserts FULLTEXT statement into $this->select and $this->where
45
     *
46
     * @param mixed $fields
47
     * @param string $against
48
     * @param string $alias
49
     * @param boolean $boolean_mode
50
     * @return $this
51
     */
52
    public function match($fields, $against, $alias, $boolean_mode = true)
53
    {
54
        if (!is_array($fields)) {
55
            $fields = [];
56
        }
57
58
        $match = [];
59
        foreach ($fields as $itemField) {
60
            if (!is_array($itemField)) {
61
                $itemField = [$itemField];
62
63
                $field = isset($itemField[0]) ? $itemField[0] : false;
64
                $protected = isset($itemField[1]) ? $itemField[1] : true;
65
66
                $match[] = $protected ? $this->protect($field) : $field;
0 ignored issues
show
Bug introduced by
It seems like $field can also be of type false; however, parameter $input of Nip\Database\Query\AbstractQuery::protect() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

66
                $match[] = $protected ? $this->protect(/** @scrutinizer ignore-type */ $field) : $field;
Loading history...
67
            }
68
        }
69
        $match = 'MATCH(' . implode(
70
            ',',
71
            $match
72
        ) . ") AGAINST ('" . $against . "'" . ($boolean_mode ? ' IN BOOLEAN MODE' : '') . ')';
73
74
        return $this->cols([$match, $alias, false])->where([$match]);
75
    }
76
77
    /**
78
     * Inserts JOIN entry for the last table inserted by $this->from()
79
     *
80
     * @param mixed $table the table to be joined, given as simple string or name - alias pair
81
     * @param string|boolean $on
82
     * @param string $type SQL join type (INNER, OUTER, LEFT INNER, etc.)
83
     * @return $this
84 3
     */
85
    public function join($table, $on = false, $type = '')
86 3
    {
87
        $lastTable = end($this->parts['from']);
0 ignored issues
show
Bug introduced by
$this->parts['from'] of type null is incompatible with the type array|object expected by parameter $array of end(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

87
        $lastTable = end(/** @scrutinizer ignore-type */ $this->parts['from']);
Loading history...
88 3
89
        if (!$lastTable) {
90
            trigger_error('No previous table to JOIN', E_USER_ERROR);
91
        }
92 3
93
        if (is_array($lastTable)) {
94
            $lastTable = $lastTable[1];
95
        }
96 3
97
        $this->parts['join'][$lastTable][] = [$table, $on, $type];
98 3
99
        return $this;
100
    }
101
102
    /**
103
     * Sets the group paramater for the query
104
     *
105
     * @param array $fields
106
     * @param boolean $rollup suport for modifier WITH ROLLUP
107
     * @return $this
108
     */
109
    public function group($fields, $rollup = false)
110
    {
111
        $this->parts['group']['fields'] = $fields;
112
        $this->parts['group']['rollup'] = $rollup;
113
114
        return $this;
115
    }
116
117
    /**
118
     * @return string
119 10
     */
120
    public function assemble()
121 10
    {
122 10
        $select = $this->parseCols();
123 10
        $options = $this->parseOptions();
124
        $from = $this->parseFrom();
125 10
126 10
        $group = $this->parseGroup();
127
        $having = $this->parseHaving();
128 10
129
        $order = $this->parseOrder();
130 10
131
        $query = "SELECT";
132 10
133 1
        if (!empty($options)) {
134
            $query .= " $options";
135
        }
136 10
137 10
        if (!empty($select)) {
138
            $query .= " $select";
139
        }
140 10
141 10
        if (!empty($from)) {
142
            $query .= " FROM $from";
143
        }
144 10
145
        $query .= $this->assembleWhere();
146 10
147
        if (!empty($group)) {
148
            $query .= " GROUP BY $group";
149
        }
150 10
151
        if (!empty($having)) {
152
            $query .= " HAVING $having";
153
        }
154 10
155
        if (!empty($order)) {
156
            $query .= " ORDER BY $order";
157
        }
158 10
159
        $query .= $this->assembleLimit();
160 10
161
        return $query;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query returns the type string which is incompatible with the return type mandated by Nip\Database\Query\AbstractQuery::assemble() of null.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
162
    }
163
164
    /**
165
     * @return null|string
166 10
     */
167
    public function parseOptions()
168 10
    {
169 1
        if (!empty($this->parts['options'])) {
170
            return implode(" ", array_map("strtoupper", $this->parts['options']));
0 ignored issues
show
Bug introduced by
$this->parts['options'] of type null is incompatible with the type array expected by parameter $array of array_map(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

170
            return implode(" ", array_map("strtoupper", /** @scrutinizer ignore-type */ $this->parts['options']));
Loading history...
171
        }
172 9
173
        return null;
174
    }
175
176
    /**
177
     * @param $query
178
     * @return Union
179 1
     */
180
    public function union($query)
181 1
    {
182
        return new Union($this, $query);
183
    }
184
185
    /**
186
     * Parses SELECT entries
187
     *
188
     * @return string
189 10
     */
190
    protected function parseCols()
191 10
    {
192 5
        if (!isset($this->parts['cols']) || !is_array($this->parts['cols']) || count($this->parts['cols']) < 1) {
0 ignored issues
show
introduced by
The condition is_array($this->parts['cols']) is always false.
Loading history...
193
            return '*';
194 5
        } else {
195
            $selectParts = [];
196 5
197 5
            foreach ($this->parts['cols'] as $itemSelect) {
198
                if (is_array($itemSelect)) {
199
                    $field = isset($itemSelect[0]) ? $itemSelect[0] : false;
200
                    $alias = isset($itemSelect[1]) ? $itemSelect[1] : false;
201
                    $protected = isset($itemSelect[2]) ? $itemSelect[2] : true;
202
203
                    $selectParts[] = ($protected ? $this->protect($field) : $field) . (!empty($alias) ? ' AS ' . $this->protect($alias) : '');
204 5
                } else {
205
                    $selectParts[] = $itemSelect;
206
                }
207
            }
208 5
209
            return implode(', ', $selectParts);
210
        }
211
    }
212
213
    /**
214
     * Parses FROM entries
215
     * @return string
216 10
     */
217
    private function parseFrom()
218 10
    {
219 10
        if (!empty($this->parts['from'])) {
220
            $parts = [];
221 10
222 10
            foreach ($this->parts['from'] as $key => $item) {
0 ignored issues
show
Bug introduced by
The expression $this->parts['from'] of type null is not traversable.
Loading history...
223
                if (is_array($item)) {
224
                    $table = isset($item[0]) ? $item[0] : false;
225
                    $alias = isset($item[1]) ? $item[1] : false;
226
227
                    if (is_object($table)) {
228
                        if (!$alias) {
229
                            trigger_error('Select statements in for need aliases defined', E_USER_ERROR);
230
                        }
231
                        $parts[$key] = '(' . $table . ') AS ' . $this->protect($alias) . $this->parseJoin($alias);
0 ignored issues
show
Bug introduced by
It seems like $alias can also be of type false; however, parameter $input of Nip\Database\Query\AbstractQuery::protect() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

231
                        $parts[$key] = '(' . $table . ') AS ' . $this->protect(/** @scrutinizer ignore-type */ $alias) . $this->parseJoin($alias);
Loading history...
Bug introduced by
Are you sure $table of type object can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

231
                        $parts[$key] = '(' . /** @scrutinizer ignore-type */ $table . ') AS ' . $this->protect($alias) . $this->parseJoin($alias);
Loading history...
Bug introduced by
It seems like $alias can also be of type false; however, parameter $table of Nip\Database\Query\Select::parseJoin() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

231
                        $parts[$key] = '(' . $table . ') AS ' . $this->protect($alias) . $this->parseJoin(/** @scrutinizer ignore-type */ $alias);
Loading history...
232
                    } else {
233
                        $parts[$key] = $this->protect($table) . ' AS ' . $this->protect((!empty($alias) ? $alias : $table)) . $this->parseJoin($alias);
234 10
                    }
235 5
                } elseif (!strpos($item, ' ')) {
236
                    $parts[] = $this->protect($item) . $this->parseJoin($item);
237 10
                } else {
238
                    $parts[] = $item;
239
                }
240
            }
241 10
242
            return implode(", ", array_unique($parts));
243
        }
244
245
        return null;
246
    }
247
248
    /**
249
     * Parses JOIN entries for a given table
250
     * Concatenates $this->join entries for input table
251
     *
252
     * @param string $table table to build JOIN statement for
253
     * @return string
254 5
     */
255
    private function parseJoin($table)
256 5
    {
257
        $result = '';
258 5
259 3
        if (isset($this->parts['join'][$table])) {
260 3
            foreach ($this->parts['join'][$table] as $join) {
261 1
                if (!is_array($join[0])) {
262
                    $join[0] = [$join[0]];
263
                }
264 3
265 3
                $joinTable = isset($join[0][0]) ? $join[0][0] : false;
266 3
                $joinAlias = isset($join[0][1]) ? $join[0][1] : false;
267
                $joinOn = isset($join[1]) ? $join[1] : false;
268
269 3
270
                $joinType = isset($join[2]) ? $join[2] : '';
271 3
272 3
                $result .= ($joinType ? ' ' . strtoupper($joinType) : '') . ' JOIN ';
273 1
                if ($joinTable instanceof AbstractQuery) {
274 1
                    $result .= '(' . $joinTable . ')';
275
                    if (empty($joinAlias)) {
276
                        $joinAlias = 'join1';
277 1
                    }
278 2
                    $joinTable = $joinAlias;
279
                } elseif (strpos($joinTable, '(') !== false) {
0 ignored issues
show
Bug introduced by
It seems like $joinTable can also be of type false; however, parameter $haystack of strpos() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

279
                } elseif (strpos(/** @scrutinizer ignore-type */ $joinTable, '(') !== false) {
Loading history...
280
                    $result .= $joinTable;
281 2
                } else {
282
                    $result .= $this->protect($joinTable);
0 ignored issues
show
Bug introduced by
It seems like $joinTable can also be of type false; however, parameter $input of Nip\Database\Query\AbstractQuery::protect() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

282
                    $result .= $this->protect(/** @scrutinizer ignore-type */ $joinTable);
Loading history...
283 3
                }
284
                $result .= (!empty($joinAlias) ? ' AS ' . $this->protect($joinAlias) : '');
285 3
286 3
                if ($joinOn) {
287 3
                    $result .= ' ON ';
288 3
                    if (is_array($joinOn)) {
289 3
                        $result .= $this->protect($table . '.' . $joinOn[0])
290 3
                            . ' = '
291
                            . $this->protect($joinTable . '.' . $joinOn[1]);
292 3
                    } else {
293
                        $result .= '(' . $joinOn . ')';
294
                    }
295
                }
296
            }
297
        }
298 5
299
        return $result;
300
    }
301
302
    /**
303
     * Parses GROUP entries
304
     *
305
     * @uses $this->group['fields'] array with elements to group by
306
     * @return string
307 10
     */
308
    private function parseGroup()
309 10
    {
310 10
        $group = '';
311
        if (isset($this->parts['group']['fields'])) {
312
            if (is_array($this->parts['group']['fields'])) {
313
                $groupFields = [];
314
                foreach ($this->parts['group']['fields'] as $field) {
315
                    $field = is_array($field) ? $field : [$field];
316
                    $column = isset($field[0]) ? $field[0] : false;
317
                    $type = isset($field[1]) ? $field[1] : '';
318
319
                    $groupFields[] = $this->protect($column) . ($type ? ' ' . strtoupper($type) : '');
0 ignored issues
show
Bug introduced by
It seems like $column can also be of type false; however, parameter $input of Nip\Database\Query\AbstractQuery::protect() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

319
                    $groupFields[] = $this->protect(/** @scrutinizer ignore-type */ $column) . ($type ? ' ' . strtoupper($type) : '');
Loading history...
320
                }
321
322
                $group .= implode(', ', $groupFields);
323
            } else {
324
                $group .= $this->parts['group']['fields'];
325
            }
326
        }
327 10
328
        if (isset($this->parts['group']['rollup']) && $this->parts['group']['rollup'] !== false) {
329
            $group .= ' WITH ROLLUP';
330
        }
331 10
332
        return $group;
333
    }
334
}
335