Completed
Push — 3.x ( 53c3d5...e5eb1c )
by Paul
04:01 queued 01:43
created

AbstractQuery::getQuoteNamePrefix()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
/**
3
 *
4
 * This file is part of Aura for PHP.
5
 *
6
 * @license http://opensource.org/licenses/bsd-license.php BSD
7
 *
8
 */
9
namespace Aura\SqlQuery;
10
11
use Aura\SqlQuery\Common\SubselectInterface;
12
13
/**
14
 *
15
 * Abstract query object.
16
 *
17
 * @package Aura.SqlQuery
18
 *
19
 */
20
abstract class AbstractQuery
21
{
22
    /**
23
     *
24
     * Data to be bound to the query.
25
     *
26
     * @var array
27
     *
28
     */
29
    protected $bind_values = array();
30
31
    /**
32
     *
33
     * The list of WHERE conditions.
34
     *
35
     * @var array
36
     *
37
     */
38
    protected $where = array();
39
40
    /**
41
     *
42
     * ORDER BY these columns.
43
     *
44
     * @var array
45
     *
46
     */
47
    protected $order_by = array();
48
49
    /**
50
     *
51
     * The list of flags.
52
     *
53
     * @var array
54
     *
55
     */
56
    protected $flags = array();
57
58
    /**
59
     *
60
     * A helper for quoting identifier names.
61
     *
62
     * @var Quoter
63
     *
64
     */
65
    protected $quoter;
66
67
    /**
68
     *
69
     * Prefix to use on placeholders for "sequential" bound values; used for
70
     * deconfliction when merging bound values from sub-selects, etc.
71
     *
72
     * @var mixed
73
     *
74
     */
75
    protected $seq_bind_prefix = '';
76
77
    /**
78
     *
79
     * Constructor.
80
     *
81
     * @param Quoter $quoter A helper for quoting identifier names.
82
     *
83
     * @param string $seq_bind_prefix A prefix for rewritten sequential-binding
84
     * placeholders (@see getSeqPlaceholder()).
85
     *
86
     */
87 387
    public function __construct(Quoter $quoter, $seq_bind_prefix = '')
88
    {
89 387
        $this->quoter = $quoter;
90 387
        $this->seq_bind_prefix = $seq_bind_prefix;
91
    }
92
93
    /**
94
     *
95
     * Returns the prefix for rewritten sequential-binding placeholders
96
     * (@see getSeqPlaceholder()).
97
     *
98
     * @return string
99
     *
100
     */
101
    public function getSeqBindPrefix()
102
    {
103
        return $this->seq_bind_prefix;
104
    }
105
106
    /**
107
     *
108
     * Returns this query object as an SQL statement string.
109
     *
110
     * @return string
111
     *
112
     */
113 5
    public function __toString()
114
    {
115 5
        return $this->getStatement();
116
    }
117
118
    /**
119
     *
120
     * Returns this query object as an SQL statement string.
121
     *
122
     * @return string
123
     *
124
     */
125
    public function getStatement()
126
    {
127
        return $this->build();
128
    }
129
130
    /**
131
     *
132
     * Builds this query object into a string.
133
     *
134
     * @return string
135
     *
136
     */
137
    abstract protected function build();
138
139
    /**
140
     *
141
     * Returns the prefix to use when quoting identifier names.
142
     *
143
     * @return string
144
     *
145
     */
146
    public function getQuoteNamePrefix()
147
    {
148
        return $this->quoter->getQuoteNamePrefix();
149
    }
150
151
    /**
152
     *
153
     * Returns the suffix to use when quoting identifier names.
154
     *
155
     * @return string
156
     *
157
     */
158
    public function getQuoteNameSuffix()
159
    {
160
        return $this->quoter->getQuoteNameSuffix();
161
    }
162
163
    /**
164
     *
165
     * Returns an array as an indented comma-separated values string.
166
     *
167
     * @param array $list The values to convert.
168
     *
169
     * @return string
170
     *
171
     */
172 61
    protected function indentCsv(array $list)
173
    {
174 61
        return PHP_EOL . '    '
175
             . implode(',' . PHP_EOL . '    ', $list);
176 61
    }
177
178
    /**
179
     *
180
     * Returns an array as an indented string.
181
     *
182
     * @param array $list The values to convert.
183
     *
184
     * @return string
185
     *
186
     */
187 50
    protected function indent(array $list)
188
    {
189 50
        return PHP_EOL . '    '
190
             . implode(PHP_EOL . '    ', $list);
191 50
    }
192
193
    /**
194
     *
195
     * Binds multiple values to placeholders; merges with existing values.
196
     *
197
     * @param array $bind_values Values to bind to placeholders.
198
     *
199
     * @return $this
200
     *
201
     */
202 16
    public function bindValues(array $bind_values)
203
    {
204
        // array_merge() renumbers integer keys, which is bad for
205
        // question-mark placeholders
206
        foreach ($bind_values as $key => $val) {
207
            $this->bindValue($key, $val);
208
        }
209 16
        return $this;
210 16
    }
211
212
    /**
213
     *
214
     * Binds a single value to the query.
215
     *
216
     * @param string $name The placeholder name or number.
217
     *
218
     * @param mixed $value The value to bind to the placeholder.
219
     *
220
     * @return $this
221
     *
222
     */
223 21
    public function bindValue($name, $value)
224
    {
225 21
        $this->bind_values[$name] = $value;
226 21
        return $this;
227
    }
228
229
    /**
230
     *
231
     * Gets the values to bind to placeholders.
232
     *
233
     * @return array
234
     *
235
     */
236 106
    public function getBindValues()
237
    {
238 106
        return $this->bind_values;
239
    }
240
241
    /**
242
     *
243
     * Reset all values bound to named placeholders.
244
     *
245
     * @return $this
246
     *
247
     */
248 15
    public function resetBindValues()
249
    {
250 15
        $this->bind_values = array();
251 15
        return $this;
252
    }
253
254
    /**
255
     *
256
     * Builds the flags as a space-separated string.
257
     *
258
     * @return string
259
     *
260
     */
261 213
    protected function buildFlags()
262
    {
263 213
        if (empty($this->flags)) {
264 175
            return ''; // not applicable
265
        }
266
267
        return ' ' . implode(' ', array_keys($this->flags));
268
    }
269
270
    /**
271
     *
272
     * Sets or unsets specified flag.
273
     *
274
     * @param string $flag Flag to set or unset
275
     *
276
     * @param bool $enable Flag status - enabled or not (default true)
277
     *
278
     * @return null
279
     *
280
     */
281 37
    protected function setFlag($flag, $enable = true)
282
    {
283 32
        if ($enable) {
284 37
            $this->flags[$flag] = true;
285
        } else {
286 5
            unset($this->flags[$flag]);
287 37
        }
288 32
    }
289
290
    /**
291
     *
292
     * Returns true if the specified flag was enabled by setFlag().
293
     *
294
     * @param string $flag Flag to check
295
     *
296
     * @return bool
297
     *
298
     */
299 10
    protected function hasFlag($flag)
300
    {
301 10
        return isset($this->flags[$flag]);
302
    }
303
304
    /**
305
     *
306
     * Reset all query flags.
307
     *
308
     * @return $this
309
     *
310
     */
311 20
    public function resetFlags()
312
    {
313 20
        $this->flags = array();
314 20
        return $this;
315
    }
316
317
    /**
318
     *
319
     * Adds a WHERE condition to the query by AND or OR. If the condition has
320
     * ?-placeholders, additional arguments to the method will be bound to
321
     * those placeholders sequentially.
322
     *
323
     * @param string $andor Add the condition using this operator, typically
324
     * 'AND' or 'OR'.
325
     *
326
     * @param string $cond The WHERE condition.
327
     *
328
     * @param array ...$bind arguments to bind to placeholders
329
     *
330
     * @return $this
331
     *
332
     */
333 11
    protected function addWhere($andor, $cond, ...$bind)
334
    {
335
        $this->addClauseCondWithBind('where', $andor, $cond, $bind);
336 11
        return $this;
337
    }
338
339
    /**
340
     *
341
     * Adds conditions and binds values to a clause.
342
     *
343
     * @param string $clause The clause to work with, typically 'where' or
344
     * 'having'.
345
     *
346
     * @param string $andor Add the condition using this operator, typically
347
     * 'AND' or 'OR'.
348
     *
349
     * @param string $cond The WHERE condition.
350
351
     * @param array $bind arguments to bind to placeholders
352
     *
353
     * @return null
354
     *
355
     */
356 50
    protected function addClauseCondWithBind($clause, $andor, $cond, $bind)
357
    {
358
        $cond = $this->rebuildCondAndBindValues($cond, $bind);
359
360
        // add condition to clause; eg $this->where or $this->having
361 6
        $clause =& $this->$clause;
362 6
        if ($clause) {
363 25
            $clause[] = "$andor $cond";
364
        } else {
365 50
            $clause[] = $cond;
366 25
        }
367 6
    }
368
369
    /**
370
     *
371
     * Rebuilds a condition string, replacing sequential placeholders with
372
     * named placeholders, and binding the sequential values to the named
373
     * placeholders.
374
     *
375
     * @param string $cond The condition with sequential placeholders.
376
     *
377
     * @param array $bind_values The values to bind to the sequential
378
     * placeholders under their named versions.
379
     *
380
     * @return string The rebuilt condition string.
381
     *
382
     */
383 31
    protected function rebuildCondAndBindValues($cond, array $bind_values)
384
    {
385
        $cond = $this->quoter->quoteNamesIn($cond);
386
387
        // bind values against ?-mark placeholders, but because PDO is finicky
388
        // about the numbering of sequential placeholders, convert each ?-mark
389
        // to a named placeholder
390
        $parts = preg_split('/(\?)/', $cond, null, PREG_SPLIT_DELIM_CAPTURE);
391
        foreach ($parts as $key => $val) {
392 10
            if ($val != '?') {
393 10
                continue;
394
            }
395
396
            $bind_value = array_shift($bind_values);
397 31
            if ($bind_value instanceof SubselectInterface) {
398
                $parts[$key] = $bind_value->getStatement();
399 10
                $this->bind_values = array_merge(
400 10
                    $this->bind_values,
401 10
                    $bind_value->getBindValues()
402
                );
403 10
                continue;
404
            }
405
406
            $placeholder = $this->getSeqPlaceholder();
407 26
            $parts[$key] = ':' . $placeholder;
408 26
            $this->bind_values[$placeholder] = $bind_value;
409
        }
410
411
        $cond = implode('', $parts);
412 21
        return $cond;
413 21
    }
414
415
    /**
416
     *
417
     * Gets the current sequential placeholder name.
418
     *
419
     * @return string
420
     *
421
     */
422 26
    protected function getSeqPlaceholder()
423
    {
424
        $i = count($this->bind_values) + 1;
425 26
        return $this->seq_bind_prefix . "_{$i}_";
426
    }
427
428
    /**
429
     *
430
     * Builds the `WHERE` clause of the statement.
431
     *
432
     * @return string
433
     *
434
     */
435 166
    protected function buildWhere()
436
    {
437 166
        if (empty($this->where)) {
438 126
            return ''; // not applicable
439
        }
440
441
        return PHP_EOL . 'WHERE' . $this->indent($this->where);
442
    }
443
444
    /**
445
     *
446
     * Adds a column order to the query.
447
     *
448
     * @param array $spec The columns and direction to order by.
449
     *
450
     * @return $this
451
     *
452
     */
453 9
    protected function addOrderBy(array $spec)
454
    {
455
        foreach ($spec as $col) {
456
            $this->order_by[] = $this->quoter->quoteNamesIn($col);
457
        }
458 9
        return $this;
459 9
    }
460
461
    /**
462
     *
463
     * Builds the `ORDER BY ...` clause of the statement.
464
     *
465
     * @return string
466
     *
467
     */
468 166
    protected function buildOrderBy()
469
    {
470 166
        if (empty($this->order_by)) {
471 157
            return ''; // not applicable
472
        }
473
474
        return PHP_EOL . 'ORDER BY' . $this->indentCsv($this->order_by);
475
    }
476
}
477