Completed
Push — 1.1 ( 6ab797...def185 )
by David
03:11
created

Select::setLimit()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 1
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
34
namespace SQLParser\Query;
35
36
use Doctrine\DBAL\Connection;
37
use Mouf\MoufInstanceDescriptor;
38
use SQLParser\Node\NodeFactory;
39
use Mouf\MoufManager;
40
use SQLParser\Node\NodeInterface;
41
use SQLParser\Node\Traverser\NodeTraverser;
42
use SQLParser\Node\Traverser\VisitorInterface;
43
44
/**
45
 * This class represents a <code>SELECT</code> query. You can use it to generate a SQL query statement
46
 * using the <code>toSql</code> method.
47
 * You can use the <code>QueryResult</code> class if you want to run the query directly.
48
 *
49
 * @author David Négrier <[email protected]>
50
 * @ExtendedAction {"name":"Generate from SQL", "url":"parseselect/", "default":false}
51
 * @ExtendedAction {"name":"Test query", "url":"parseselect/tryQuery", "default":false}
52
 * @Renderer { "smallLogo":"vendor/mouf/database.querywriter/icons/database_go.png" }
53
 */
54
class Select implements StatementInterface, NodeInterface
55
{
56
    private $distinct;
57
58
    /**
59
     * Returns true if the SELECT is a SELECT DISTINCT.
60
     *
61
     * @return bool
62
     */
63
    public function getDistinct()
64
    {
65
        return $this->distinct;
66
    }
67
68
    /**
69
     * Sets to true if the SELECT is a SELECT DISTINCT.
70
     *
71
     * @param bool $distinct
72
     */
73
    public function setDistinct($distinct)
74
    {
75
        $this->distinct = $distinct;
76
    }
77
78
    private $columns;
79
80
    /**
81
     * Returns the list of columns for this SQL select.
82
     *
83
     * @return NodeInterface[]
84
     */
85
    public function getColumns()
86
    {
87
        return $this->columns;
88
    }
89
90
    /**
91
     * Sets the list of columns for this SQL select.
92
     *
93
     * @param NodeInterface[] $columns
94
     */
95
    public function setColumns($columns)
96
    {
97
        $this->columns = $columns;
98
    }
99
100
    private $from;
101
102
    /**
103
     * Returns the list of tables for this SQL select.
104
     *
105
     * @return NodeInterface[]
106
     */
107
    public function getFrom()
108
    {
109
        return $this->from;
110
    }
111
112
    /**
113
     * Sets the list of tables for this SQL select.
114
     *
115
     * @param NodeInterface[] $from
116
     */
117
    public function setFrom($from)
118
    {
119
        $this->from = $from;
120
    }
121
122
    private $where;
123
124
    /**
125
     * Returns the list of where statements.
126
     *
127
     * @return NodeInterface[]
128
     */
129
    public function getWhere()
130
    {
131
        return $this->where;
132
    }
133
134
    /**
135
     * Sets the list of where statements.
136
     *
137
     * @param NodeInterface[]|NodeInterface $where
138
     */
139
    public function setWhere($where)
140
    {
141
        $this->where = $where;
142
    }
143
144
    private $group;
145
146
    /**
147
     * Returns the list of group statements.
148
     *
149
     * @return NodeInterface[]
150
     */
151
    public function getGroup()
152
    {
153
        return $this->group;
154
    }
155
156
    /**
157
     * Sets the list of group statements.
158
     *
159
     * @param NodeInterface[]|NodeInterface $group
160
     */
161
    public function setGroup($group)
162
    {
163
        $this->group = $group;
164
    }
165
166
    private $having;
167
168
    /**
169
     * Returns the list of having statements.
170
     *
171
     * @return NodeInterface[]|NodeInterface
172
     */
173
    public function getHaving()
174
    {
175
        return $this->having;
176
    }
177
178
    /**
179
     * Sets the list of having statements.
180
     *
181
     * @param NodeInterface[]|NodeInterface $having
182
     */
183
    public function setHaving($having)
184
    {
185
        $this->having = $having;
186
    }
187
188
    private $order;
189
190
    /**
191
     * Returns the list of order statements.
192
     *
193
     * @return NodeInterface[]|NodeInterface
194
     */
195
    public function getOrder()
196
    {
197
        return $this->order;
198
    }
199
200
    /**
201
     * Sets the list of order statements.
202
     *
203
     * @param NodeInterface[]|NodeInterface $order
204
     */
205
    public function setOrder($order)
206
    {
207
        $this->order = $order;
208
    }
209
210
    private $options;
211
212
    /**
213
     * Returns the list of options to be applied just after the "SELECT" keyword.
214
     *
215
     * @return string[]
216
     */
217
    public function getOptions()
218
    {
219
        return $this->options;
220
    }
221
222
    /**
223
     * Sets the list of options to be applied just after the "SELECT" keyword.
224
     *
225
     * @param string[] $options
226
     */
227
    public function setOptions($options)
228
    {
229
        $this->options = $options;
230
    }
231
232
    private $limit;
233
234
    /**
235
     * @return NodeInterface
236
     */
237
    public function getLimit() {
238
        return $this->limit;
239
    }
240
241
    /**
242
     * @param NodeInterface $limit
243
     */
244
    public function setLimit($limit) {
245
        $this->limit = $limit;
246
    }
247
248
    private $offset;
249
250
    /**
251
     * @return NodeInterface
252
     */
253
    public function getOffset()
254
    {
255
        return $this->offset;
256
    }
257
258
    /**
259
     * @param NodeInterface $offset
260
     */
261
    public function setOffset($offset)
262
    {
263
        $this->offset = $offset;
264
    }
265
266
    /**
267
     * @param MoufManager $moufManager
268
     *
269
     * @return MoufInstanceDescriptor
270
     */
271 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...
272
    {
273
        $instanceDescriptor = $moufManager->createInstance(get_called_class());
274
        $instanceDescriptor->getProperty('distinct')->setValue($this->distinct);
275
        $instanceDescriptor->getProperty('columns')->setValue(NodeFactory::nodeToInstanceDescriptor($this->columns, $moufManager));
276
        $instanceDescriptor->getProperty('from')->setValue(NodeFactory::nodeToInstanceDescriptor($this->from, $moufManager));
277
        $instanceDescriptor->getProperty('where')->setValue(NodeFactory::nodeToInstanceDescriptor($this->where, $moufManager));
278
        $instanceDescriptor->getProperty('group')->setValue(NodeFactory::nodeToInstanceDescriptor($this->group, $moufManager));
279
        $instanceDescriptor->getProperty('having')->setValue(NodeFactory::nodeToInstanceDescriptor($this->having, $moufManager));
280
        $instanceDescriptor->getProperty('order')->setValue(NodeFactory::nodeToInstanceDescriptor($this->order, $moufManager));
281
        $instanceDescriptor->getProperty('offset')->setValue(NodeFactory::nodeToInstanceDescriptor($this->offset, $moufManager));
282
        $instanceDescriptor->getProperty('limit')->setValue(NodeFactory::nodeToInstanceDescriptor($this->limit, $moufManager));
283
        $instanceDescriptor->getProperty('options')->setValue($this->options);
284
285
        return $instanceDescriptor;
286
    }
287
288
    /**
289
     * Configure the $instanceDescriptor describing this object (it must already exist as a Mouf instance).
290
     *
291
     * @param MoufManager $moufManager
292
     *
293
     * @return MoufInstanceDescriptor
294
     */
295 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...
296
    {
297
        //$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...
298
        $instanceDescriptor = $moufManager->getInstanceDescriptor($name);
299
        $instanceDescriptor->getProperty('distinct')->setValue($this->distinct);
300
        $instanceDescriptor->getProperty('columns')->setValue(NodeFactory::nodeToInstanceDescriptor($this->columns, $moufManager));
301
        $instanceDescriptor->getProperty('from')->setValue(NodeFactory::nodeToInstanceDescriptor($this->from, $moufManager));
302
        $instanceDescriptor->getProperty('where')->setValue(NodeFactory::nodeToInstanceDescriptor($this->where, $moufManager));
303
        $instanceDescriptor->getProperty('group')->setValue(NodeFactory::nodeToInstanceDescriptor($this->group, $moufManager));
304
        $instanceDescriptor->getProperty('having')->setValue(NodeFactory::nodeToInstanceDescriptor($this->having, $moufManager));
305
        $instanceDescriptor->getProperty('order')->setValue(NodeFactory::nodeToInstanceDescriptor($this->order, $moufManager));
306
        $instanceDescriptor->getProperty('offset')->setValue(NodeFactory::nodeToInstanceDescriptor($this->offset, $moufManager));
307
        $instanceDescriptor->getProperty('limit')->setValue(NodeFactory::nodeToInstanceDescriptor($this->limit, $moufManager));
308
        $instanceDescriptor->getProperty('options')->setValue($this->options);
309
310
        return $instanceDescriptor;
311
    }
312
313
    /**
314
     * Renders the object as a SQL string.
315
     *
316
     * @param array $parameters
317
     * @param Connection $dbConnection
318
     * @param int|number $indent
319
     * @param int $conditionsMode
320
     * @return string
321
     */
322
    public function toSql(array $parameters = array(), Connection $dbConnection = null, $indent = 0, $conditionsMode = self::CONDITION_APPLY)
323
    {
324
        $sql = 'SELECT ';
325
        if ($this->distinct) {
326
            $sql .= 'DISTINCT ';
327
        }
328
        if (is_array($this->options)) {
329
            $sql .= implode(' ', $this->options)."\n";
330
        }
331
332 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...
333
            $sql .= NodeFactory::toSql($this->columns, $dbConnection, $parameters, ',', false, $indent + 2, $conditionsMode);
334
        }
335
336 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...
337
            $from = NodeFactory::toSql($this->from, $dbConnection, $parameters, ' ', false, $indent + 2, $conditionsMode);
338
            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...
339
                $sql .= "\nFROM ".$from;
340
            }
341
        }
342
343 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...
344
            $where = NodeFactory::toSql($this->where, $dbConnection, $parameters, ' ', false, $indent + 2, $conditionsMode);
345
            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...
346
                $sql .= "\nWHERE ".$where;
347
            }
348
        }
349
350 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...
351
            $groupBy = NodeFactory::toSql($this->group, $dbConnection, $parameters, ' ', false, $indent + 2, $conditionsMode);
352
            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...
353
                $sql .= "\nGROUP BY ".$groupBy;
354
            }
355
        }
356
357 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...
358
            $having = NodeFactory::toSql($this->having, $dbConnection, $parameters, ' ', false, $indent + 2, $conditionsMode);
359
            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...
360
                $sql .= "\nHAVING ".$having;
361
            }
362
        }
363
364 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...
365
            $order = NodeFactory::toSql($this->order, $dbConnection, $parameters, ',', false, $indent + 2, $conditionsMode);
366
            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...
367
                $sql .= "\nORDER BY ".$order;
368
            }
369
        }
370
371
        if (!empty($this->offset) && empty($this->limit)) {
372
            throw new \Exception('There is no offset if no limit is provided. An error may have occurred during SQLParsing.');
373
        } else if (!empty($this->limit)) {
374
            $limit = NodeFactory::toSql($this->limit, $dbConnection, $parameters, ',', false, $indent + 2, $conditionsMode);
375
            if ($limit === '' || substr(trim($limit), 0, 1) == ':') {
376
                $limit = null;
377
            }
378
            if (!empty($this->offset)) {
379
                $offset = NodeFactory::toSql($this->offset, $dbConnection, $parameters, ',', false, $indent + 2, $conditionsMode);
380
                if ($offset === '' || substr(trim($offset), 0, 1) == ':') {
381
                    $offset = null;
382
                }
383
            } else {
384
                $offset = null;
385
            }
386
387
            if ($limit === null && $offset !== null) {
388
                throw new \Exception('There is no offset if no limit is provided. An error may have occurred during SQLParsing.');
389
            }
390
391
            if($limit !== null) {
392
                $sql .= "\nLIMIT ";
393
                if ($offset !== null) {
394
                    $sql .= $offset.', ';
395
                }
396
                $sql .= $limit;
397
            }
398
        }
399
400
        return $sql;
401
    }
402
403
    /**
404
     * Walks the tree of nodes, calling the visitor passed in parameter.
405
     *
406
     * @param VisitorInterface $visitor
407
     */
408
    public function walk(VisitorInterface $visitor) {
409
        $node = $this;
410
        $result = $visitor->enterNode($node);
411
        if ($result instanceof NodeInterface) {
412
            $node = $result;
413
        }
414
        if ($result !== NodeTraverser::DONT_TRAVERSE_CHILDREN) {
415
            $this->walkChildren($this->columns, $visitor);
416
            $this->walkChildren($this->from, $visitor);
417
            $this->walkChildren($this->where, $visitor);
418
            $this->walkChildren($this->group, $visitor);
419
            $this->walkChildren($this->having, $visitor);
420
            $this->walkChildren($this->order, $visitor);
421
        }
422
        return $visitor->leaveNode($node);
423
    }
424
425
    private function walkChildren(&$children, VisitorInterface $visitor) {
426
        if ($children) {
427
            if (is_array($children)) {
428
                foreach ($children as $key => $operand) {
429
                    if($operand) {
430
                        $result2 = $operand->walk($visitor);
431
                        if ($result2 === NodeTraverser::REMOVE_NODE) {
432
                            unset($children[$key]);
433
                        } elseif ($result2 instanceof NodeInterface) {
434
                            $children[$key] = $result2;
435
                        }
436
                    }
437
                }
438
            } else {
439
                $result2 = $children->walk($visitor);
440
                if ($result2 === NodeTraverser::REMOVE_NODE) {
441
                    $children = null;
442
                } elseif ($result2 instanceof NodeInterface) {
443
                    $children = $result2;
444
                }
445
            }
446
        }
447
    }
448
}
449