Completed
Push — master ( 186a7f...bab9dc )
by Gabriel
10:46
created

Select::parseGroup()   B

Complexity

Conditions 10
Paths 6

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 39.634

Importance

Changes 0
Metric Value
dl 0
loc 26
ccs 5
cts 15
cp 0.3333
rs 7.6666
c 0
b 0
f 0
cc 10
nc 6
nop 0
crap 39.634

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
    /**
19
     * @param $name
20
     * @param $arguments
21
     * @return AbstractQuery|Select
22
     */
23 8
    public function __call($name, $arguments)
24
    {
25 8
        if (in_array($name, ['min', 'max', 'count', 'avg', 'sum'])) {
26
            $input = reset($arguments);
27
28
            if (is_array($input)) {
29
                $input[] = false;
30
            } else {
31
                $alias = isset($arguments[1]) ? $arguments[1] : null;
32
                $protected = isset($arguments[2]) ? $arguments[2] : null;
33
                $input = [$input, $alias, $protected];
34
            }
35
36
            $input[0] = strtoupper($name).'('.$this->protect($input[0]).')';
0 ignored issues
show
Documentation introduced by
$input[0] is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
37
38
            return $this->cols($input);
0 ignored issues
show
Unused Code introduced by
The call to Select::cols() has too many arguments starting with $input.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
39
        }
40
41 8
        return parent::__call($name, $arguments);
42
    }
43
44
    /**
45
     * Inserts FULLTEXT statement into $this->select and $this->where
46
     *
47
     * @param mixed $fields
48
     * @param string $against
49
     * @param string $alias
50
     * @param boolean $boolean_mode
51
     * @return $this
52
     */
53
    public function match($fields, $against, $alias, $boolean_mode = true)
54
    {
55
        if (!is_array($fields)) {
56
            $fields = [];
57
        }
58
59
        $match = [];
60
        foreach ($fields as $itemField) {
61
            if (!is_array($itemField)) {
62
                $itemField = [$itemField];
63
64
                $field = isset($itemField[0]) ? $itemField[0] : false;
65
                $protected = isset($itemField[1]) ? $itemField[1] : true;
66
67
                $match[] = $protected ? $this->protect($field) : $field;
68
            }
69
        }
70
        $match = "MATCH(".implode(",",
71
                $match).") AGAINST ('".$against."'".($boolean_mode ? " IN BOOLEAN MODE" : "").")";
72
73
        return $this->cols([$match, $alias, false])->where([$match]);
0 ignored issues
show
Unused Code introduced by
The call to Select::cols() has too many arguments starting with array($match, $alias, false).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
74
    }
75
76
    /**
77
     * Inserts JOIN entry for the last table inserted by $this->from()
78
     *
79
     * @param mixed $table the table to be joined, given as simple string or name - alias pair
80
     * @param string|boolean $on
81
     * @param string $type SQL join type (INNER, OUTER, LEFT INNER, etc.)
82
     * @return $this
83
     */
84 2
    public function join($table, $on = false, $type = '')
85
    {
86 2
        $lastTable = end($this->parts['from']);
87
88 2
        if (!$lastTable) {
89
            trigger_error('No previous table to JOIN', E_USER_ERROR);
90
        }
91
92 2
        if (is_array($lastTable)) {
93
            $lastTable = $lastTable[1];
94
        }
95
96 2
        $this->parts['join'][$lastTable][] = [$table, $on, $type];
97
98 2
        return $this;
99
    }
100
101
    /**
102
     * Sets the group paramater for the query
103
     *
104
     * @param array $fields
105
     * @param boolean $rollup suport for modifier WITH ROLLUP
106
     * @return $this
107
     */
108
    public function group($fields, $rollup = false)
109
    {
110
        $this->parts['group']['fields'] = $fields;
111
        $this->parts['group']['rollup'] = $rollup;
112
113
        return $this;
114
    }
115
116
    /**
117
     * @return string
118
     */
119 7
    public function assemble()
120
    {
121 7
        $select = $this->parseCols();
122 7
        $options = $this->parseOptions();
123 7
        $from = $this->parseFrom();
124
125 7
        $group = $this->parseGroup();
126 7
        $having = $this->parseHaving();
127
128 7
        $order = $this->parseOrder();
129
130 7
        $query = "SELECT";
131
132 7
        if (!empty($options)) {
133 1
            $query .= " $options";
134
        }
135
136 7
        if (!empty($select)) {
137 7
            $query .= " $select";
138
        }
139
140 7
        if (!empty($from)) {
141 7
            $query .= " FROM $from";
142
        }
143
144 7
        $query .= $this->assembleWhere();
145
146 7
        if (!empty($group)) {
147
            $query .= " GROUP BY $group";
148
        }
149
150 7
        if (!empty($having)) {
151
            $query .= " HAVING $having";
152
        }
153
154 7
        if (!empty($order)) {
155
            $query .= " ORDER BY $order";
156
        }
157
158 7
        $query .= $this->assembleLimit();
159
160 7
        return $query;
161
    }
162
163
    /**
164
     * @return null|string
165
     */
166 7
    public function parseOptions()
167
    {
168 7
        if (!empty($this->parts['options'])) {
169 1
            return implode(" ", array_map("strtoupper", $this->parts['options']));
170
        }
171
172 6
        return null;
173
    }
174
175
    /**
176
     * @param $query
177
     * @return Union
178
     */
179
    public function union($query)
180
    {
181
        return new Union($this, $query);
182
    }
183
184
    /**
185
     * Parses SELECT entries
186
     *
187
     * @return string
188
     */
189 7
    protected function parseCols()
190
    {
191 7
        if (!isset($this->parts['cols']) || !is_array($this->parts['cols']) || count($this->parts['cols']) < 1) {
192 2
            return '*';
193
        } else {
194 5
            $selectParts = [];
195
196 5
            foreach ($this->parts['cols'] as $itemSelect) {
197 5
                if (is_array($itemSelect)) {
198
                    $field = isset($itemSelect[0]) ? $itemSelect[0] : false;
199
                    $alias = isset($itemSelect[1]) ? $itemSelect[1] : false;
200
                    $protected = isset($itemSelect[2]) ? $itemSelect[2] : true;
201
202
                    $selectParts[] = ($protected ? $this->protect($field) : $field).(!empty($alias) ? ' AS '.$this->protect($alias) : '');
203
                } else {
204 5
                    $selectParts[] = $itemSelect;
205
                }
206
            }
207
208 5
            return implode(', ', $selectParts);
209
        }
210
    }
211
212
    /**
213
     * Parses FROM entries
214
     * @return string
215
     */
216 7
    private function parseFrom()
217
    {
218 7
        if (!empty($this->parts['from'])) {
219 7
            $parts = [];
220
221 7
            foreach ($this->parts['from'] as $key => $item) {
222 7
                if (is_array($item)) {
223
                    $table = isset($item[0]) ? $item[0] : false;
224
                    $alias = isset($item[1]) ? $item[1] : false;
225
226
                    if (is_object($table)) {
227
                        if (!$alias) {
228
                            trigger_error('Select statements in for need aliases defined', E_USER_ERROR);
229
                        }
230
                        $parts[$key] = '('.$table.') AS '.$this->protect($alias).$this->parseJoin($alias);
231
                    } else {
232
                        $parts[$key] = $this->protect($table).' AS '.$this->protect((!empty($alias) ? $alias : $table)).$this->parseJoin($alias);
233
                    }
234 7
                } elseif (!strpos($item, ' ')) {
235 2
                    $parts[] = $this->protect($item).$this->parseJoin($item);
236
                } else {
237 7
                    $parts[] = $item;
238
                }
239
            }
240
241 7
            return implode(", ", array_unique($parts));
242
        }
243
244
        return null;
245
    }
246
247
    /**
248
     * Parses JOIN entries for a given table
249
     * Concatenates $this->join entries for input table
250
     *
251
     * @param string $table table to build JOIN statement for
252
     * @return string
253
     */
254 2
    private function parseJoin($table)
255
    {
256 2
        $result = '';
257
258 2
        if (isset($this->parts['join'][$table])) {
259 2
            foreach ($this->parts['join'][$table] as $join) {
260 2
                if (!is_array($join[0])) {
261 1
                    $join[0] = [$join[0]];
262
                }
263
264 2
                $joinTable = isset($join[0][0]) ? $join[0][0] : false;
265 2
                $joinAlias = isset($join[0][1]) ? $join[0][1] : false;
266 2
                $joinOn = isset($join[1]) ? $join[1] : false;
267
268
269 2
                $joinType = isset($join[2]) ? $join[2] : '';
270
271 2
                $result .= ($joinType ? ' '.strtoupper($joinType) : '').' JOIN ';
272 2
                if ($joinTable instanceof AbstractQuery) {
273
                    $result .= '('.$joinTable.')';
274
                    if (empty($joinAlias)) {
275
                        $joinAlias = 'join1';
276
                    }
277
                    $joinTable = $joinAlias;
278 2
                } elseif (strpos($joinTable, '(') !== false) {
279
                    $result .= $joinTable;
280
                } else {
281 2
                    $result .= $this->protect($joinTable);
282
                }
283 2
                $result .= (!empty($joinAlias) ? ' AS '.$this->protect($joinAlias) : '');
284
285 2
                if ($joinOn) {
286 2
                    $result .= ' ON ';
287 2
                    if (is_array($joinOn)) {
288 2
                        $result .= $this->protect($table.'.'.$joinOn[0])
289 2
                            .' = '
290 2
                            .$this->protect($joinTable.'.'.$joinOn[1]);
291
                    } else {
292 2
                        $result .= '('.$joinOn.')';
293
                    }
294
                }
295
            }
296
        }
297
298 2
        return $result;
299
    }
300
301
    /**
302
     * Parses GROUP entries
303
     *
304
     * @uses $this->group['fields'] array with elements to group by
305
     * @return string
306
     */
307 7
    private function parseGroup()
308
    {
309 7
        $group = '';
310 7
        if (isset($this->parts['group']['fields'])) {
311
            if (is_array($this->parts['group']['fields'])) {
312
                $groupFields = [];
313
                foreach ($this->parts['group']['fields'] as $field) {
314
                    $field = is_array($field) ? $field : [$field];
315
                    $column = isset($field[0]) ? $field[0] : false;
316
                    $type = isset($field[1]) ? $field[1] : '';
317
318
                    $groupFields[] = $this->protect($column).($type ? ' '.strtoupper($type) : '');
319
                }
320
321
                $group .= implode(', ', $groupFields);
322
            } else {
323
                $group .= $this->parts['group']['fields'];
324
            }
325
        }
326
327 7
        if (isset($this->parts['group']['rollup']) && $this->parts['group']['rollup'] !== false) {
328
            $group .= ' WITH ROLLUP';
329
        }
330
331 7
        return $group;
332
    }
333
}
334