Issues (194)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/SQLParser/Query/Select.php (14 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
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
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
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
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
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
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
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
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