Where   A
last analyzed

Complexity

Total Complexity 35

Size/Duplication

Total Lines 339
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
wmc 35
lcom 1
cbo 0
dl 0
loc 339
rs 9.6
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A create() 0 4 1
A createWhereIn() 0 4 1
A createWhereNotIn() 0 4 1
A createGroupCondition() 0 12 1
A extractValues() 0 10 2
A escapeSet() 0 15 3
A __construct() 0 7 2
A setOperator() 0 6 1
A isEmpty() 0 4 2
A transmute() 0 7 1
B addWhere() 0 37 6
A andWhere() 0 4 1
A orWhere() 0 4 1
A setStack() 0 6 1
A __toString() 0 4 2
A hasElement() 0 4 1
A getElement() 0 4 1
A parse() 0 13 3
A getValues() 0 18 4
1
<?php
2
/*
3
 * This file is part of the PommProject/Foundation package.
4
 *
5
 * (c) 2011 - 2017 Grégoire HUBERT <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace PommProject\Foundation;
11
12
/**
13
 * Where
14
 *
15
 * This class represents a WHERE clause of a SQL statement. It deals with AND &
16
 * OR operator you can add using handy methods. This allows you to build
17
 * queries dynamically.
18
 *
19
 * @package   Foundation
20
 * @copyright 2014 - 2017 Grégoire HUBERT
21
 * @author    Grégoire HUBERT <[email protected]>
22
 * @license   X11 {@link http://opensource.org/licenses/mit-license.php}
23
 */
24
class Where
25
{
26
    public $stack = [];
27
    public $element;
28
    public $values = [];
29
    public $operator;
30
31
    /**
32
     * create
33
     *
34
     * A constructor you can chain from.
35
     *
36
     * @param  string $element
37
     * @param  array  $values
38
     * @return Where
39
     */
40
    public static function create($element = null, array $values = [])
41
    {
42
        return new self($element, $values);
43
    }
44
45
    /**
46
     * createWhereIn
47
     *
48
     * Create an escaped IN clause.
49
     *
50
     * @param  string $element
51
     * @param  array  $values
52
     * @return Where
53
     */
54
    public static function createWhereIn($element, array $values)
55
    {
56
        return self::createGroupCondition($element, 'IN', $values);
57
    }
58
59
    /**
60
     * createWhereNotIn
61
     *
62
     * Create an escaped NOT IN clause.
63
     *
64
     * @param  string $element
65
     * @param  array $values
66
     * @return Where
67
     */
68
    public static function createWhereNotIn($element, array $values)
69
    {
70
        return self::createGroupCondition($element, 'NOT IN', $values);
71
    }
72
73
    /**
74
     * createGroupCondition
75
     *
76
     * Create a Where instance with multiple escaped parameters. This is mainly
77
     * useful for IN or NOT IN clauses.
78
     *
79
     * @param  string $element
80
     * @param  string $operation
81
     * @param  array  $values
82
     * @return Where
83
     */
84
    public static function createGroupCondition($element, $operation, array $values)
85
    {
86
        return new self(
87
            sprintf(
88
                "%s %s (%s)",
89
                $element,
90
                $operation,
91
                join(", ", static::escapeSet($values))
92
            ),
93
            static::extractValues($values)
94
        );
95
    }
96
97
    /**
98
     * extractValues
99
     *
100
     * Extract values with consistent keys.
101
     *
102
     * @param  array $values
103
     * @return array
104
     */
105
    protected static function extractValues(array $values)
106
    {
107
        $array = [];
108
109
        foreach (new \RecursiveIteratorIterator(new \RecursiveArrayIterator($values)) as $value) {
110
            $array[] = $value;
111
        }
112
113
        return $array;
114
    }
115
116
    /**
117
     * escapeSet
118
     *
119
     * Create an array of escaped strings from a value set.
120
     *
121
     * @param  array $values
122
     * @return array
123
     */
124
    protected static function escapeSet(array $values)
125
    {
126
        $escaped_values = [];
127
128
        foreach ($values as $value) {
129
            if (is_array($value)) {
130
                $escaped_values[] =
131
                    sprintf("(%s)", join(', ', static::escapeSet($value)));
132
            } else {
133
                $escaped_values[] = '$*';
134
            }
135
        }
136
137
        return $escaped_values;
138
    }
139
140
    /**
141
     * __construct
142
     *
143
     * @param string $element (optional)
144
     * @param array  $values  (optional)
145
     */
146
    public function __construct($element = null, array $values = [])
147
    {
148
        if ($element !== null) {
149
            $this->element = $element;
150
            $this->values = $values;
151
        }
152
    }
153
154
    /**
155
     * setOperator
156
     *
157
     * is it an AND or an OR ?
158
     * or something else.
159
     * XOR can be expressed as "A = !B"
160
     *
161
     * @param  string $operator
162
     * @return Where
163
     */
164
    public function setOperator($operator)
165
    {
166
        $this->operator = $operator;
167
168
        return $this;
169
    }
170
171
    /**
172
     * isEmpty
173
     *
174
     * is it a fresh brand new object ?
175
     *
176
     * @return boolean
177
     */
178
    public function isEmpty()
179
    {
180
        return (bool) ($this->element === null && count($this->stack) == 0);
181
    }
182
183
    /**
184
     * transmute
185
     *
186
     * Absorbing another Where instance.
187
     *
188
     * @param  Where $where
189
     * @return Where $this
190
     */
191
    private function transmute(Where $where)
192
    {
193
        $this->stack    = $where->stack;
194
        $this->element  = $where->element;
195
        $this->operator = $where->operator;
196
        $this->values   = $where->values;
197
    }
198
199
    /**
200
     * addWhere
201
     *
202
     * You can add a new WHERE clause with your own operator.
203
     *
204
     * @param  mixed  $element
205
     * @param  array  $values
206
     * @param  string $operator
207
     * @return Where
208
     */
209
    public function addWhere($element, array $values, $operator)
210
    {
211
        if (!$element instanceof Where) {
212
            $element = new self($element, $values);
213
        }
214
215
        if ($element->isEmpty()) {
216
            return $this;
217
        }
218
219
        if ($this->isEmpty()) {
220
            $this->transmute($element);
221
222
            return $this;
223
        }
224
225
        if ($this->hasElement()) {
226
            $this->stack = [new self($this->getElement(), $this->values), $element];
227
            $this->element = null;
228
            $this->values = [];
229
        } else {
230
            if ($this->operator == $operator) {
231
                $this->stack[] = $element;
232
            } else {
233
                $this->stack = [
234
                    self::create()
235
                        ->setStack($this->stack)
236
                        ->setOperator($this->operator),
237
                    $element
238
                ];
239
            }
240
        }
241
242
        $this->operator = $operator;
243
244
        return $this;
245
    }
246
247
    /**
248
     * andWhere
249
     *
250
     * Or use a ready to use AND where clause.
251
     *
252
     * @param  mixed $element
253
     * @param  array $values
254
     * @return Where
255
     */
256
    public function andWhere($element, array $values = [])
257
    {
258
        return $this->addWhere($element, $values, 'AND');
259
    }
260
261
    /**
262
     * orWhere
263
     *
264
     * @param  mixed $element
265
     * @param  array $values
266
     * @return Where
267
     */
268
    public function orWhere($element, array $values = [])
269
    {
270
        return $this->addWhere($element, $values, 'OR');
271
    }
272
273
    /**
274
     * setStack
275
     *
276
     * @param  array $stack
277
     * @return Where
278
     */
279
    public function setStack(array $stack)
280
    {
281
        $this->stack = $stack;
282
283
        return $this;
284
    }
285
286
    /**
287
     * __toString
288
     *
289
     * where your SQL statement is built.
290
     *
291
     * @return string
292
     */
293
    public function __toString()
294
    {
295
        return $this->isEmpty() ? 'true' : $this->parse();
296
    }
297
298
    /**
299
     * hasElement
300
     *
301
     * @return boolean
302
     */
303
    public function hasElement()
304
    {
305
        return $this->element !== null;
306
    }
307
308
    /**
309
     * getElement
310
     *
311
     * @return string
312
     */
313
    public function getElement()
314
    {
315
        return $this->element;
316
    }
317
318
    /**
319
     * parse
320
     *
321
     * @return string
322
     */
323
    protected function parse()
324
    {
325
        if ($this->hasElement()) {
326
            return $this->getElement();
327
        }
328
329
        $stack = [];
330
        foreach ($this->stack as $offset => $where) {
331
            $stack[$offset] = $where->parse();
332
        }
333
334
        return sprintf('(%s)', join(sprintf(' %s ', $this->operator), $stack));
335
    }
336
337
    /**
338
     * getValues
339
     *
340
     * Get all the values back for the prepared statement.
341
     *
342
     * @return array
343
     */
344
    public function getValues()
345
    {
346
        if ($this->isEmpty()) {
347
            return [];
348
        }
349
350
        if ($this->hasElement()) {
351
            return $this->values;
352
        }
353
354
        $values = [];
355
356
        foreach ($this->stack as $where) {
357
            $values[] = $where->getValues();
358
        }
359
360
        return call_user_func_array('array_merge', $values);
361
    }
362
}
363