Completed
Push — 2.0 ( 143803...4e64fa )
by grégoire
08:42 queued 04:22
created

Where   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 359
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 5
Bugs 0 Features 2
Metric Value
wmc 35
lcom 1
cbo 0
dl 0
loc 359
c 5
b 0
f 2
rs 9

19 Methods

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