Completed
Pull Request — 1.3 (#52)
by David
01:39
created

Expression::setBaseExpression()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
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
namespace SQLParser\Node;
34
35
use Doctrine\DBAL\Connection;
36
use function is_iterable;
37
use Mouf\MoufInstanceDescriptor;
38
use Mouf\MoufManager;
39
use SQLParser\Node\Traverser\NodeTraverser;
40
use SQLParser\Node\Traverser\VisitorInterface;
41
42
/**
43
 * This class represents a node that is an SQL expression.
44
 *
45
 * @author David Négrier <[email protected]>
46
 */
47
class Expression implements NodeInterface, BypassableInterface
48
{
49
    private $baseExpression;
50
51
    /**
52
     * Returns the base expression (the string that generated this expression).
53
     *
54
     * @return string
55
     */
56
    public function getBaseExpression()
57
    {
58
        return $this->baseExpression;
59
    }
60
61
    /**
62
     * Sets the base expression (the string that generated this expression).
63
     *
64
     * @param string $baseExpression
65
     */
66
    public function setBaseExpression($baseExpression)
67
    {
68
        $this->baseExpression = $baseExpression;
69
    }
70
71
    private $subTree;
72
73
    public function getSubTree()
74
    {
75
        return $this->subTree;
76
    }
77
78
    /**
79
     * Sets the subtree.
80
     *
81
     * @Important
82
     *
83
     * @param array<NodeInterface>|NodeInterface $subTree
84
     */
85
    public function setSubTree($subTree)
86
    {
87
        $this->subTree = $subTree;
88
        $this->subTree = NodeFactory::simplify($this->subTree);
89
    }
90
91
    private $alias;
92
93
    public function getAlias()
94
    {
95
        return $this->alias;
96
    }
97
98
    /**
99
     * Sets the alias.
100
     *
101
     * @Important
102
     *
103
     * @param string $alias
104
     */
105
    public function setAlias($alias)
106
    {
107
        $this->alias = $alias;
108
    }
109
110
    private $direction;
111
112
    public function getDiretion()
113
    {
114
        return $this->direction;
115
    }
116
117
    /**
118
     * Sets the direction.
119
     *
120
     * @Important
121
     *
122
     * @param string $direction
123
     */
124
    public function setDirection($direction)
125
    {
126
        $this->direction = $direction;
127
    }
128
129
    private $brackets = false;
130
131
    /**
132
     * Returns true if the expression is between brackets.
133
     *
134
     * @return bool
135
     */
136
    public function hasBrackets()
137
    {
138
        return $this->brackets;
139
    }
140
141
    /**
142
     * Sets to true if the expression is between brackets.
143
     *
144
     * @Important
145
     *
146
     * @param bool $brackets
147
     */
148
    public function setBrackets($brackets)
149
    {
150
        $this->brackets = $brackets;
151
    }
152
153
    private $delimiter = ' ';
154
155
    /**
156
     * @return string
157
     */
158
    public function getDelimiter()
159
    {
160
        return $this->delimiter;
161
    }
162
163
    /**
164
     * Sets the delimiter for the list. Defaults to ' '.
165
     *
166
     * @param string $delimiter
167
     */
168
    public function setDelimiter($delimiter)
169
    {
170
        $this->delimiter = $delimiter;
171
    }
172
173
174
175
    /**
176
     * Returns a Mouf instance descriptor describing this object.
177
     *
178
     * @param MoufManager $moufManager
179
     *
180
     * @return MoufInstanceDescriptor
181
     */
182
    public function toInstanceDescriptor(MoufManager $moufManager)
183
    {
184
        $instanceDescriptor = $moufManager->createInstance(get_called_class());
185
        $instanceDescriptor->getProperty('baseExpression')->setValue(NodeFactory::nodeToInstanceDescriptor($this->baseExpression, $moufManager));
186
        $instanceDescriptor->getProperty('subTree')->setValue(NodeFactory::nodeToInstanceDescriptor($this->subTree, $moufManager));
187
        $instanceDescriptor->getProperty('alias')->setValue(NodeFactory::nodeToInstanceDescriptor($this->alias, $moufManager));
188
        $instanceDescriptor->getProperty('direction')->setValue(NodeFactory::nodeToInstanceDescriptor($this->direction, $moufManager));
189
        $instanceDescriptor->getProperty('brackets')->setValue(NodeFactory::nodeToInstanceDescriptor($this->brackets, $moufManager));
190
        $instanceDescriptor->getProperty('delimiter')->setValue(NodeFactory::nodeToInstanceDescriptor($this->delimiter, $moufManager));
191
192
        return $instanceDescriptor;
193
    }
194
195
    /**
196
     * Renders the object as a SQL string.
197
     *
198
     * @param Connection $dbConnection
199
     * @param array      $parameters
200
     * @param number     $indent
201
     * @param int        $conditionsMode
202
     *
203
     * @return string|null
204
     */
205
    public function toSql(array $parameters = array(), Connection $dbConnection = null, $indent = 0, $conditionsMode = self::CONDITION_APPLY, bool $extrapolateParameters = true)
206
    {
207
        if (empty($this->subTree)) {
208
            return $this->getBaseExpression();
209
        }
210
        $sql = NodeFactory::toSql($this->subTree, $dbConnection, $parameters, $this->delimiter, false, $indent, $conditionsMode, $extrapolateParameters);
211
212
        if ($sql === null) {
213
            return null;
214
        }
215
216
        if ($this->alias) {
217
            $sql .= ' AS '.$this->alias;
218
        }
219
        if ($this->direction) {
220
            $sql .= ' '.$this->direction;
221
        }
222
        if ($this->brackets) {
223
            $sql = '('.$sql.')';
224
        }
225
226
227
        return $sql;
228
    }
229
230
    /**
231
     * Walks the tree of nodes, calling the visitor passed in parameter.
232
     *
233
     * @param VisitorInterface $visitor
234
     */
235 View Code Duplication
    public function walk(VisitorInterface $visitor)
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...
236
    {
237
        $node = $this;
238
        $result = $visitor->enterNode($node);
239
        if ($result instanceof NodeInterface) {
240
            $node = $result;
241
        }
242
        if ($result !== NodeTraverser::DONT_TRAVERSE_CHILDREN) {
243
            if (is_array($this->subTree)) {
244
                foreach ($this->subTree as $key => $operand) {
245
                    $result2 = $operand->walk($visitor);
246
                    if ($result2 === NodeTraverser::REMOVE_NODE) {
247
                        unset($this->subTree[$key]);
248
                    } elseif ($result2 instanceof NodeInterface) {
249
                        $this->subTree[$key] = $result2;
250
                    }
251
                }
252
            } else {
253
                $result2 = $this->subTree->walk($visitor);
254
                if ($result2 === NodeTraverser::REMOVE_NODE) {
255
                    $this->subTree = [];
256
                } elseif ($result2 instanceof NodeInterface) {
257
                    $this->subTree = $result2;
258
                }
259
            }
260
        }
261
262
        return $visitor->leaveNode($node);
263
    }
264
265
    /**
266
     * Returns if this node should be removed from the tree.
267
     */
268
    public function canBeBypassed(array $parameters): bool
269
    {
270
        if (empty($this->subTree)) {
271
            // Some expression can (rarely) have no subtree. Don't know why.
272
            return false;
273
        }
274
        if (is_iterable($this->subTree)) {
275
            foreach ($this->subTree as $node) {
0 ignored issues
show
Bug introduced by
The expression $this->subTree of type object<SQLParser\Node\NodeInterface>|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
276
                if (!$node instanceof BypassableInterface || !$node->canBeBypassed($parameters)) {
277
                    return false;
278
                }
279
            }
280
        } elseif (!$this->subTree instanceof BypassableInterface || !$this->subTree->canBeBypassed($parameters)) {
281
            return false;
282
        }
283
        return true;
284
    }
285
}
286