Completed
Push — 3.x ( e5eb1c...76cecd )
by Paul
9s
created

AbstractQuery::build()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 1
c 0
b 0
f 0
ccs 0
cts 0
cp 0
nc 1
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 408
    public function __construct(Quoter $quoter, $builder, $seq_bind_prefix = '')
88
    {
89 408
        $this->quoter = $quoter;
90 408
        $this->builder = $builder;
0 ignored issues
show
Bug introduced by
The property builder does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
91 408
        $this->seq_bind_prefix = $seq_bind_prefix;
92 408
    }
93
94
    /**
95
     *
96
     * Returns the prefix for rewritten sequential-binding placeholders
97
     * (@see getSeqPlaceholder()).
98
     *
99
     * @return string
100
     *
101
     */
102 1
    public function getSeqBindPrefix()
103
    {
104 1
        return $this->seq_bind_prefix;
105
    }
106
107
    /**
108
     *
109
     * Returns this query object as an SQL statement string.
110
     *
111
     * @return string
112
     *
113
     */
114 243
    public function __toString()
115
    {
116 243
        return $this->getStatement();
117
    }
118
119
    /**
120
     *
121
     * Returns this query object as an SQL statement string.
122
     *
123
     * @return string
124
     *
125
     */
126 69
    public function getStatement()
127
    {
128 69
        return $this->build();
129
    }
130
131
    /**
132
     *
133
     * Builds this query object into a string.
134
     *
135
     * @return string
136
     *
137
     */
138
    abstract protected function build();
139
140
    /**
141
     *
142
     * Returns the prefix to use when quoting identifier names.
143
     *
144
     * @return string
145
     *
146
     */
147 257
    public function getQuoteNamePrefix()
148
    {
149 257
        return $this->quoter->getQuoteNamePrefix();
150
    }
151
152
    /**
153
     *
154
     * Returns the suffix to use when quoting identifier names.
155
     *
156
     * @return string
157
     *
158
     */
159 257
    public function getQuoteNameSuffix()
160
    {
161 257
        return $this->quoter->getQuoteNameSuffix();
162
    }
163
164
    /**
165
     *
166
     * Binds multiple values to placeholders; merges with existing values.
167
     *
168
     * @param array $bind_values Values to bind to placeholders.
169
     *
170
     * @return $this
171
     *
172
     */
173 31
    public function bindValues(array $bind_values)
174
    {
175
        // array_merge() renumbers integer keys, which is bad for
176
        // question-mark placeholders
177 31
        foreach ($bind_values as $key => $val) {
178 31
            $this->bindValue($key, $val);
179
        }
180 31
        return $this;
181
    }
182
183
    /**
184
     *
185
     * Binds a single value to the query.
186
     *
187
     * @param string $name The placeholder name or number.
188
     *
189
     * @param mixed $value The value to bind to the placeholder.
190
     *
191
     * @return $this
192
     *
193
     */
194 72
    public function bindValue($name, $value)
195
    {
196 72
        $this->bind_values[$name] = $value;
197 72
        return $this;
198
    }
199
200
    /**
201
     *
202
     * Gets the values to bind to placeholders.
203
     *
204
     * @return array
205
     *
206
     */
207 126
    public function getBindValues()
208
    {
209 126
        return $this->bind_values;
210
    }
211
212
    /**
213
     *
214
     * Reset all values bound to named placeholders.
215
     *
216
     * @return $this
217
     *
218
     */
219 15
    public function resetBindValues()
220
    {
221 15
        $this->bind_values = array();
222 15
        return $this;
223
    }
224
225
    /**
226
     *
227
     * Sets or unsets specified flag.
228
     *
229
     * @param string $flag Flag to set or unset
230
     *
231
     * @param bool $enable Flag status - enabled or not (default true)
232
     *
233
     * @return null
234
     *
235
     */
236 43
    protected function setFlag($flag, $enable = true)
237
    {
238 43
        if ($enable) {
239 43
            $this->flags[$flag] = true;
240
        } else {
241 5
            unset($this->flags[$flag]);
242
        }
243 43
    }
244
245
    /**
246
     *
247
     * Returns true if the specified flag was enabled by setFlag().
248
     *
249
     * @param string $flag Flag to check
250
     *
251
     * @return bool
252
     *
253
     */
254 10
    protected function hasFlag($flag)
255
    {
256 10
        return isset($this->flags[$flag]);
257
    }
258
259
    /**
260
     *
261
     * Reset all query flags.
262
     *
263
     * @return $this
264
     *
265
     */
266 20
    public function resetFlags()
267
    {
268 20
        $this->flags = array();
269 20
        return $this;
270
    }
271
272
    /**
273
     *
274
     * Adds a WHERE condition to the query by AND or OR. If the condition has
275
     * ?-placeholders, additional arguments to the method will be bound to
276
     * those placeholders sequentially.
277
     *
278
     * @param string $andor Add the condition using this operator, typically
279
     * 'AND' or 'OR'.
280
     *
281
     * @param string $cond The WHERE condition.
282
     *
283
     * @param array ...$bind arguments to bind to placeholders
284
     *
285
     * @return $this
286
     *
287
     */
288 60
    protected function addWhere($andor, $cond, ...$bind)
289
    {
290 60
        $this->addClauseCondWithBind('where', $andor, $cond, $bind);
291 60
        return $this;
292
    }
293
294
    /**
295
     *
296
     * Adds conditions and binds values to a clause.
297
     *
298
     * @param string $clause The clause to work with, typically 'where' or
299
     * 'having'.
300
     *
301
     * @param string $andor Add the condition using this operator, typically
302
     * 'AND' or 'OR'.
303
     *
304
     * @param string $cond The WHERE condition.
305
306
     * @param array $bind arguments to bind to placeholders
307
     *
308
     * @return null
309
     *
310
     */
311 70
    protected function addClauseCondWithBind($clause, $andor, $cond, $bind)
312
    {
313 70
        $cond = $this->rebuildCondAndBindValues($cond, $bind);
314
315
        // add condition to clause; eg $this->where or $this->having
316 70
        $clause =& $this->$clause;
317 70
        if ($clause) {
318 49
            $clause[] = "$andor $cond";
319
        } else {
320 70
            $clause[] = $cond;
321
        }
322 70
    }
323
324
    /**
325
     *
326
     * Rebuilds a condition string, replacing sequential placeholders with
327
     * named placeholders, and binding the sequential values to the named
328
     * placeholders.
329
     *
330
     * @param string $cond The condition with sequential placeholders.
331
     *
332
     * @param array $bind_values The values to bind to the sequential
333
     * placeholders under their named versions.
334
     *
335
     * @return string The rebuilt condition string.
336
     *
337
     */
338 120
    protected function rebuildCondAndBindValues($cond, array $bind_values)
339
    {
340 120
        $cond = $this->quoter->quoteNamesIn($cond);
341
342
        // bind values against ?-mark placeholders, but because PDO is finicky
343
        // about the numbering of sequential placeholders, convert each ?-mark
344
        // to a named placeholder
345 120
        $parts = preg_split('/(\?)/', $cond, null, PREG_SPLIT_DELIM_CAPTURE);
346 120
        foreach ($parts as $key => $val) {
347 120
            if ($val != '?') {
348 120
                continue;
349
            }
350
351 80
            $bind_value = array_shift($bind_values);
352 80
            if ($bind_value instanceof SubselectInterface) {
353 10
                $parts[$key] = $bind_value->getStatement();
354 10
                $this->bind_values = array_merge(
355 10
                    $this->bind_values,
356 10
                    $bind_value->getBindValues()
357
                );
358 10
                continue;
359
            }
360
361 75
            $placeholder = $this->getSeqPlaceholder();
362 75
            $parts[$key] = ':' . $placeholder;
363 75
            $this->bind_values[$placeholder] = $bind_value;
364
        }
365
366 120
        $cond = implode('', $parts);
367 120
        return $cond;
368
    }
369
370
    /**
371
     *
372
     * Gets the current sequential placeholder name.
373
     *
374
     * @return string
375
     *
376
     */
377 75
    protected function getSeqPlaceholder()
378
    {
379 75
        $i = count($this->bind_values) + 1;
380 75
        return $this->seq_bind_prefix . "_{$i}_";
381
    }
382
383
    /**
384
     *
385
     * Adds a column order to the query.
386
     *
387
     * @param array $spec The columns and direction to order by.
388
     *
389
     * @return $this
390
     *
391
     */
392 9
    protected function addOrderBy(array $spec)
393
    {
394 9
        foreach ($spec as $col) {
395 9
            $this->order_by[] = $this->quoter->quoteNamesIn($col);
396
        }
397 9
        return $this;
398
    }
399
}
400