Completed
Push — 3.x-named-placeholders-only ( fcb764 )
by Paul
02:02
created

AbstractQuery::getSeqPlaceholder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 0
crap 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
use Aura\SqlQuery\Common\QuoterInterface;
13
14
/**
15
 *
16
 * Abstract query object.
17
 *
18
 * @package Aura.SqlQuery
19
 *
20
 */
21
abstract class AbstractQuery
22
{
23
    /**
24
     *
25
     * Data to be bound to the query.
26
     *
27
     * @var array
28
     *
29
     */
30
    protected $bind_values = array();
31
32
    /**
33
     *
34
     * The list of WHERE conditions.
35
     *
36
     * @var array
37
     *
38
     */
39
    protected $where = array();
40
41
    /**
42
     *
43
     * ORDER BY these columns.
44
     *
45
     * @var array
46
     *
47
     */
48
    protected $order_by = array();
49
50
    /**
51
     *
52
     * The list of flags.
53
     *
54
     * @var array
55
     *
56
     */
57
    protected $flags = array();
58
59
    /**
60
     *
61
     * A helper for quoting identifier names.
62
     *
63
     * @var Quoter
64
     *
65
     */
66
    protected $quoter;
67
68
    /**
69
     *
70
     * Prefix to use on placeholders for "sequential" bound values; used for
71
     * deconfliction when merging bound values from sub-selects, etc.
72
     *
73
     * @var mixed
74
     *
75
     */
76
    protected $seq_bind_prefix = '';
77
78
    /**
79
     *
80
     * Constructor.
81
     *
82
     * @param Quoter $quoter A helper for quoting identifier names.
83
     *
84
     * @param string $seq_bind_prefix A prefix for rewritten sequential-binding
85
     * placeholders (@see getSeqPlaceholder()).
86
     *
87
     */
88 408
    public function __construct(QuoterInterface $quoter, $builder, $seq_bind_prefix = '')
89
    {
90 408
        $this->quoter = $quoter;
0 ignored issues
show
Documentation Bug introduced by
It seems like $quoter of type object<Aura\SqlQuery\Common\QuoterInterface> is incompatible with the declared type object<Aura\SqlQuery\Quoter> of property $quoter.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

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