Select::toSql()   F
last analyzed

Complexity

Conditions 26
Paths > 20000

Size

Total Lines 80
Code Lines 48

Duplication

Lines 33
Ratio 41.25 %

Importance

Changes 8
Bugs 0 Features 0
Metric Value
c 8
b 0
f 0
dl 33
loc 80
rs 2.294
cc 26
eloc 48
nc 50544
nop 4

How to fix   Long Method    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
/**
4
 * expression-types.php.
5
 *
6
 *
7
 * Copyright (c) 2010-2013, Justin Swanhart
8
 * with contributions by André Rothe <[email protected], [email protected]>
9
 * and David Négrier <[email protected]>
10
 *
11
 * All rights reserved.
12
 *
13
 * Redistribution and use in source and binary forms, with or without modification,
14
 * are permitted provided that the following conditions are met:
15
 *
16
 *   * Redistributions of source code must retain the above copyright notice,
17
 *     this list of conditions and the following disclaimer.
18
 *   * Redistributions in binary form must reproduce the above copyright notice,
19
 *     this list of conditions and the following disclaimer in the documentation
20
 *     and/or other materials provided with the distribution.
21
 *
22
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
23
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25
 * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31
 * DAMAGE.
32
 */
33
namespace SQLParser\Query;
34
35
use Doctrine\DBAL\Connection;
36
use Mouf\MoufInstanceDescriptor;
37
use SQLParser\Node\NodeFactory;
38
use Mouf\MoufManager;
39
use SQLParser\Node\NodeInterface;
40
use SQLParser\Node\Traverser\NodeTraverser;
41
use SQLParser\Node\Traverser\VisitorInterface;
42
43
/**
44
 * This class represents a <code>SELECT</code> query. You can use it to generate a SQL query statement
45
 * using the <code>toSql</code> method.
46
 * You can use the <code>QueryResult</code> class if you want to run the query directly.
47
 *
48
 * @author David Négrier <[email protected]>
49
 * @ExtendedAction {"name":"Generate from SQL", "url":"parseselect/", "default":false}
50
 * @ExtendedAction {"name":"Test query", "url":"parseselect/tryQuery", "default":false}
51
 * @Renderer { "smallLogo":"vendor/mouf/database.querywriter/icons/database_go.png" }
52
 */
53
class Select implements StatementInterface, NodeInterface
54
{
55
    private $distinct;
56
57
    /**
58
     * Returns true if the SELECT is a SELECT DISTINCT.
59
     *
60
     * @return bool
61
     */
62
    public function getDistinct()
63
    {
64
        return $this->distinct;
65
    }
66
67
    /**
68
     * Sets to true if the SELECT is a SELECT DISTINCT.
69
     *
70
     * @param bool $distinct
71
     */
72
    public function setDistinct($distinct)
73
    {
74
        $this->distinct = $distinct;
75
    }
76
77
    private $columns;
78
79
    /**
80
     * Returns the list of columns for this SQL select.
81
     *
82
     * @return NodeInterface[]
83
     */
84
    public function getColumns()
85
    {
86
        return $this->columns;
87
    }
88
89
    /**
90
     * Sets the list of columns for this SQL select.
91
     *
92
     * @param NodeInterface[] $columns
93
     */
94
    public function setColumns($columns)
95
    {
96
        $this->columns = $columns;
97
    }
98
99
    private $from;
100
101
    /**
102
     * Returns the list of tables for this SQL select.
103
     *
104
     * @return NodeInterface[]
105
     */
106
    public function getFrom()
107
    {
108
        return $this->from;
109
    }
110
111
    /**
112
     * Sets the list of tables for this SQL select.
113
     *
114
     * @param NodeInterface[] $from
115
     */
116
    public function setFrom($from)
117
    {
118
        $this->from = $from;
119
    }
120
121
    private $where;
122
123
    /**
124
     * Returns the list of where statements.
125
     *
126
     * @return NodeInterface[]
127
     */
128
    public function getWhere()
129
    {
130
        return $this->where;
131
    }
132
133
    /**
134
     * Sets the list of where statements.
135
     *
136
     * @param NodeInterface[]|NodeInterface $where
137
     */
138
    public function setWhere($where)
139
    {
140
        $this->where = $where;
141
    }
142
143
    private $group;
144
145
    /**
146
     * Returns the list of group statements.
147
     *
148
     * @return NodeInterface[]
149
     */
150
    public function getGroup()
151
    {
152
        return $this->group;
153
    }
154
155
    /**
156
     * Sets the list of group statements.
157
     *
158
     * @param NodeInterface[]|NodeInterface $group
159
     */
160
    public function setGroup($group)
161
    {
162
        $this->group = $group;
163
    }
164
165
    private $having;
166
167
    /**
168
     * Returns the list of having statements.
169
     *
170
     * @return NodeInterface[]|NodeInterface
171
     */
172
    public function getHaving()
173
    {
174
        return $this->having;
175
    }
176
177
    /**
178
     * Sets the list of having statements.
179
     *
180
     * @param NodeInterface[]|NodeInterface $having
181
     */
182
    public function setHaving($having)
183
    {
184
        $this->having = $having;
185
    }
186
187
    private $order;
188
189
    /**
190
     * Returns the list of order statements.
191
     *
192
     * @return NodeInterface[]|NodeInterface
193
     */
194
    public function getOrder()
195
    {
196
        return $this->order;
197
    }
198
199
    /**
200
     * Sets the list of order statements.
201
     *
202
     * @param NodeInterface[]|NodeInterface $order
203
     */
204
    public function setOrder($order)
205
    {
206
        $this->order = $order;
207
    }
208
209
    private $options;
210
211
    /**
212
     * Returns the list of options to be applied just after the "SELECT" keyword.
213
     *
214
     * @return string[]
215
     */
216
    public function getOptions()
217
    {
218
        return $this->options;
219
    }
220
221
    /**
222
     * Sets the list of options to be applied just after the "SELECT" keyword.
223
     *
224
     * @param string[] $options
225
     */
226
    public function setOptions($options)
227
    {
228
        $this->options = $options;
229
    }
230
231
    private $limit;
232
233
    /**
234
     * @return NodeInterface
235
     */
236
    public function getLimit()
237
    {
238
        return $this->limit;
239
    }
240
241
    /**
242
     * @param NodeInterface $limit
243
     */
244
    public function setLimit($limit)
245
    {
246
        $this->limit = $limit;
247
    }
248
249
    private $offset;
250
251
    /**
252
     * @return NodeInterface
253
     */
254
    public function getOffset()
255
    {
256
        return $this->offset;
257
    }
258
259
    /**
260
     * @param NodeInterface $offset
261
     */
262
    public function setOffset($offset)
263
    {
264
        $this->offset = $offset;
265
    }
266
267
    /**
268
     * @param MoufManager $moufManager
269
     *
270
     * @return MoufInstanceDescriptor
271
     */
272 View Code Duplication
    public function toInstanceDescriptor(MoufManager $moufManager)
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...
273
    {
274
        $instanceDescriptor = $moufManager->createInstance(get_called_class());
275
        $instanceDescriptor->getProperty('distinct')->setValue($this->distinct);
276
        $instanceDescriptor->getProperty('columns')->setValue(NodeFactory::nodeToInstanceDescriptor($this->columns, $moufManager));
277
        $instanceDescriptor->getProperty('from')->setValue(NodeFactory::nodeToInstanceDescriptor($this->from, $moufManager));
278
        $instanceDescriptor->getProperty('where')->setValue(NodeFactory::nodeToInstanceDescriptor($this->where, $moufManager));
279
        $instanceDescriptor->getProperty('group')->setValue(NodeFactory::nodeToInstanceDescriptor($this->group, $moufManager));
280
        $instanceDescriptor->getProperty('having')->setValue(NodeFactory::nodeToInstanceDescriptor($this->having, $moufManager));
281
        $instanceDescriptor->getProperty('order')->setValue(NodeFactory::nodeToInstanceDescriptor($this->order, $moufManager));
282
        $instanceDescriptor->getProperty('offset')->setValue(NodeFactory::nodeToInstanceDescriptor($this->offset, $moufManager));
283
        $instanceDescriptor->getProperty('limit')->setValue(NodeFactory::nodeToInstanceDescriptor($this->limit, $moufManager));
284
        $instanceDescriptor->getProperty('options')->setValue($this->options);
285
286
        return $instanceDescriptor;
287
    }
288
289
    /**
290
     * Configure the $instanceDescriptor describing this object (it must already exist as a Mouf instance).
291
     *
292
     * @param MoufManager $moufManager
293
     *
294
     * @return MoufInstanceDescriptor
295
     */
296 View Code Duplication
    public function overwriteInstanceDescriptor($name, MoufManager $moufManager)
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...
297
    {
298
        //$name = $moufManager->findInstanceName($this);
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
299
        $instanceDescriptor = $moufManager->getInstanceDescriptor($name);
300
        $instanceDescriptor->getProperty('distinct')->setValue($this->distinct);
301
        $instanceDescriptor->getProperty('columns')->setValue(NodeFactory::nodeToInstanceDescriptor($this->columns, $moufManager));
302
        $instanceDescriptor->getProperty('from')->setValue(NodeFactory::nodeToInstanceDescriptor($this->from, $moufManager));
303
        $instanceDescriptor->getProperty('where')->setValue(NodeFactory::nodeToInstanceDescriptor($this->where, $moufManager));
304
        $instanceDescriptor->getProperty('group')->setValue(NodeFactory::nodeToInstanceDescriptor($this->group, $moufManager));
305
        $instanceDescriptor->getProperty('having')->setValue(NodeFactory::nodeToInstanceDescriptor($this->having, $moufManager));
306
        $instanceDescriptor->getProperty('order')->setValue(NodeFactory::nodeToInstanceDescriptor($this->order, $moufManager));
307
        $instanceDescriptor->getProperty('offset')->setValue(NodeFactory::nodeToInstanceDescriptor($this->offset, $moufManager));
308
        $instanceDescriptor->getProperty('limit')->setValue(NodeFactory::nodeToInstanceDescriptor($this->limit, $moufManager));
309
        $instanceDescriptor->getProperty('options')->setValue($this->options);
310
311
        return $instanceDescriptor;
312
    }
313
314
    /**
315
     * Renders the object as a SQL string.
316
     *
317
     * @param array      $parameters
318
     * @param Connection $dbConnection
319
     * @param int|number $indent
320
     * @param int        $conditionsMode
321
     *
322
     * @return string
323
     */
324
    public function toSql(array $parameters = array(), Connection $dbConnection = null, $indent = 0, $conditionsMode = self::CONDITION_APPLY)
325
    {
326
        $sql = 'SELECT ';
327
        if ($this->distinct) {
328
            $sql .= 'DISTINCT ';
329
        }
330
        if (is_array($this->options)) {
331
            $sql .= implode(' ', $this->options)."\n";
332
        }
333
334 View Code Duplication
        if (!empty($this->columns)) {
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...
335
            $sql .= NodeFactory::toSql($this->columns, $dbConnection, $parameters, ',', false, $indent + 2, $conditionsMode);
336
        }
337
338 View Code Duplication
        if (!empty($this->from)) {
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...
339
            $from = NodeFactory::toSql($this->from, $dbConnection, $parameters, ' ', false, $indent + 2, $conditionsMode);
340
            if ($from) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $from of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
341
                $sql .= "\nFROM ".$from;
342
            }
343
        }
344
345 View Code Duplication
        if (!empty($this->where)) {
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...
346
            $where = NodeFactory::toSql($this->where, $dbConnection, $parameters, ' ', false, $indent + 2, $conditionsMode);
347
            if ($where) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $where of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
348
                $sql .= "\nWHERE ".$where;
349
            }
350
        }
351
352 View Code Duplication
        if (!empty($this->group)) {
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...
353
            $groupBy = NodeFactory::toSql($this->group, $dbConnection, $parameters, ',', false, $indent + 2, $conditionsMode);
354
            if ($groupBy) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $groupBy of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
355
                $sql .= "\nGROUP BY ".$groupBy;
356
            }
357
        }
358
359 View Code Duplication
        if (!empty($this->having)) {
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...
360
            $having = NodeFactory::toSql($this->having, $dbConnection, $parameters, ' ', false, $indent + 2, $conditionsMode);
361
            if ($having) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $having of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
362
                $sql .= "\nHAVING ".$having;
363
            }
364
        }
365
366 View Code Duplication
        if (!empty($this->order)) {
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...
367
            $order = NodeFactory::toSql($this->order, $dbConnection, $parameters, ',', false, $indent + 2, $conditionsMode);
368
            if ($order) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $order of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
369
                $sql .= "\nORDER BY ".$order;
370
            }
371
        }
372
373
        if (!empty($this->offset) && empty($this->limit)) {
374
            throw new \Exception('There is no offset if no limit is provided. An error may have occurred during SQLParsing.');
375
        } elseif (!empty($this->limit)) {
376
            $limit = NodeFactory::toSql($this->limit, $dbConnection, $parameters, ',', false, $indent + 2, $conditionsMode);
377
            if ($limit === '' || substr(trim($limit), 0, 1) == ':') {
378
                $limit = null;
379
            }
380
            if (!empty($this->offset)) {
381
                $offset = NodeFactory::toSql($this->offset, $dbConnection, $parameters, ',', false, $indent + 2, $conditionsMode);
382
                if ($offset === '' || substr(trim($offset), 0, 1) == ':') {
383
                    $offset = null;
384
                }
385
            } else {
386
                $offset = null;
387
            }
388
389
            if ($limit === null && $offset !== null) {
390
                throw new \Exception('There is no offset if no limit is provided. An error may have occurred during SQLParsing.');
391
            }
392
393
            if ($limit !== null) {
394
                $sql .= "\nLIMIT ";
395
                if ($offset !== null) {
396
                    $sql .= $offset.', ';
397
                }
398
                $sql .= $limit;
399
            }
400
        }
401
402
        return $sql;
403
    }
404
405
    /**
406
     * Walks the tree of nodes, calling the visitor passed in parameter.
407
     *
408
     * @param VisitorInterface $visitor
409
     */
410
    public function walk(VisitorInterface $visitor)
411
    {
412
        $node = $this;
413
        $result = $visitor->enterNode($node);
414
        if ($result instanceof NodeInterface) {
415
            $node = $result;
416
        }
417
        if ($result !== NodeTraverser::DONT_TRAVERSE_CHILDREN) {
418
            $this->walkChildren($this->columns, $visitor);
419
            $this->walkChildren($this->from, $visitor);
420
            $this->walkChildren($this->where, $visitor);
421
            $this->walkChildren($this->group, $visitor);
422
            $this->walkChildren($this->having, $visitor);
423
            $this->walkChildren($this->order, $visitor);
424
        }
425
426
        return $visitor->leaveNode($node);
427
    }
428
429
    private function walkChildren(&$children, VisitorInterface $visitor)
430
    {
431
        if ($children) {
432
            if (is_array($children)) {
433
                foreach ($children as $key => $operand) {
434
                    if ($operand) {
435
                        $result2 = $operand->walk($visitor);
436
                        if ($result2 === NodeTraverser::REMOVE_NODE) {
437
                            unset($children[$key]);
438
                        } elseif ($result2 instanceof NodeInterface) {
439
                            $children[$key] = $result2;
440
                        }
441
                    }
442
                }
443
            } else {
444
                $result2 = $children->walk($visitor);
445
                if ($result2 === NodeTraverser::REMOVE_NODE) {
446
                    $children = null;
447
                } elseif ($result2 instanceof NodeInterface) {
448
                    $children = $result2;
449
                }
450
            }
451
        }
452
    }
453
}
454