QueryBuilder::execute()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Query;
21
22
use Doctrine\DBAL\ParameterType;
23
use Doctrine\DBAL\Query\Expression\CompositeExpression;
24
use Doctrine\DBAL\Connection;
25
use function array_key_exists;
26
use function array_keys;
27
use function array_unshift;
28
use function func_get_args;
29
use function func_num_args;
30
use function implode;
31
use function is_array;
32
use function is_null;
33
use function is_object;
34
use function key;
35
use function strtoupper;
36
use function substr;
37
38
/**
39
 * QueryBuilder class is responsible to dynamically create SQL queries.
40
 *
41
 * Important: Verify that every feature you use will work with your database vendor.
42
 * SQL Query Builder does not attempt to validate the generated SQL at all.
43
 *
44
 * The query builder does no validation whatsoever if certain features even work with the
45
 * underlying database vendor. Limit queries and joins are NOT applied to UPDATE and DELETE statements
46
 * even if some vendors such as MySQL support it.
47
 *
48
 * @link   www.doctrine-project.org
49
 * @since  2.1
50
 * @author Guilherme Blanco <[email protected]>
51
 * @author Benjamin Eberlei <[email protected]>
52
 */
53
class QueryBuilder
54
{
55
    /*
56
     * The query types.
57
     */
58
    const SELECT = 0;
59
    const DELETE = 1;
60
    const UPDATE = 2;
61
    const INSERT = 3;
62
63
    /*
64
     * The builder states.
65
     */
66
    const STATE_DIRTY = 0;
67
    const STATE_CLEAN = 1;
68
69
    /**
70
     * The DBAL Connection.
71
     *
72
     * @var \Doctrine\DBAL\Connection
73
     */
74
    private $connection;
75
76
    /**
77
     * @var array The array of SQL parts collected.
78
     */
79
    private $sqlParts = [
80
        'select'  => [],
81
        'from'    => [],
82
        'join'    => [],
83
        'set'     => [],
84
        'where'   => null,
85
        'groupBy' => [],
86
        'having'  => null,
87
        'orderBy' => [],
88
        'values'  => [],
89
    ];
90
91
    /**
92
     * The complete SQL string for this query.
93
     *
94
     * @var string
95
     */
96
    private $sql;
97
98
    /**
99
     * The query parameters.
100
     *
101
     * @var array
102
     */
103
    private $params = [];
104
105
    /**
106
     * The parameter type map of this query.
107
     *
108
     * @var array
109
     */
110
    private $paramTypes = [];
111
112
    /**
113
     * The type of query this is. Can be select, update or delete.
114
     *
115
     * @var int
116
     */
117
    private $type = self::SELECT;
118
119
    /**
120
     * The state of the query object. Can be dirty or clean.
121
     *
122
     * @var int
123
     */
124
    private $state = self::STATE_CLEAN;
125
126
    /**
127
     * The index of the first result to retrieve.
128
     *
129
     * @var int
130
     */
131
    private $firstResult = null;
132
133
    /**
134
     * The maximum number of results to retrieve.
135
     *
136
     * @var int
137
     */
138
    private $maxResults = null;
139
140
    /**
141
     * The counter of bound parameters used with {@see bindValue).
142
     *
143
     * @var int
144
     */
145
    private $boundCounter = 0;
146
147
    /**
148
     * Initializes a new <tt>QueryBuilder</tt>.
149
     *
150
     * @param \Doctrine\DBAL\Connection $connection The DBAL Connection.
151
     */
152
    public function __construct(Connection $connection)
153
    {
154
        $this->connection = $connection;
155
    }
156
157
    /**
158
     * Gets an ExpressionBuilder used for object-oriented construction of query expressions.
159
     * This producer method is intended for convenient inline usage. Example:
160
     *
161
     * <code>
162
     *     $qb = $conn->createQueryBuilder()
163
     *         ->select('u')
164
     *         ->from('users', 'u')
165
     *         ->where($qb->expr()->eq('u.id', 1));
166
     * </code>
167
     *
168
     * For more complex expression construction, consider storing the expression
169
     * builder object in a local variable.
170
     *
171
     * @return \Doctrine\DBAL\Query\Expression\ExpressionBuilder
172
     */
173
    public function expr()
174
    {
175
        return $this->connection->getExpressionBuilder();
176
    }
177
178
    /**
179
     * Gets the type of the currently built query.
180
     *
181
     * @return int
182
     */
183
    public function getType()
184
    {
185
        return $this->type;
186
    }
187
188
    /**
189
     * Gets the associated DBAL Connection for this query builder.
190
     *
191
     * @return \Doctrine\DBAL\Connection
192
     */
193
    public function getConnection()
194
    {
195
        return $this->connection;
196
    }
197
198
    /**
199
     * Gets the state of this query builder instance.
200
     *
201
     * @return int Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
202
     */
203
    public function getState()
204
    {
205
        return $this->state;
206
    }
207
208
    /**
209
     * Executes this query using the bound parameters and their types.
210
     *
211
     * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate}
212
     * for insert, update and delete statements.
213
     *
214
     * @return \Doctrine\DBAL\Driver\Statement|int
0 ignored issues
show
Documentation introduced by
Should the return type not be \Doctrine\DBAL\Driver\ResultStatement|integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
215
     */
216
    public function execute()
217
    {
218
        if ($this->type == self::SELECT) {
219
            return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes);
220
        }
221
222
        return $this->connection->executeUpdate($this->getSQL(), $this->params, $this->paramTypes);
223
    }
224
225
    /**
226
     * Gets the complete SQL string formed by the current specifications of this QueryBuilder.
227
     *
228
     * <code>
229
     *     $qb = $em->createQueryBuilder()
230
     *         ->select('u')
231
     *         ->from('User', 'u')
232
     *     echo $qb->getSQL(); // SELECT u FROM User u
233
     * </code>
234
     *
235
     * @return string The SQL query string.
236
     */
237
    public function getSQL()
238
    {
239
        if ($this->sql !== null && $this->state === self::STATE_CLEAN) {
240
            return $this->sql;
241
        }
242
243
        switch ($this->type) {
244
            case self::INSERT:
245
                $sql = $this->getSQLForInsert();
246
                break;
247
            case self::DELETE:
248
                $sql = $this->getSQLForDelete();
249
                break;
250
251
            case self::UPDATE:
252
                $sql = $this->getSQLForUpdate();
253
                break;
254
255
            case self::SELECT:
256
            default:
257
                $sql = $this->getSQLForSelect();
258
                break;
259
        }
260
261
        $this->state = self::STATE_CLEAN;
262
        $this->sql = $sql;
263
264
        return $sql;
265
    }
266
267
    /**
268
     * Sets a query parameter for the query being constructed.
269
     *
270
     * <code>
271
     *     $qb = $conn->createQueryBuilder()
272
     *         ->select('u')
273
     *         ->from('users', 'u')
274
     *         ->where('u.id = :user_id')
275
     *         ->setParameter(':user_id', 1);
276
     * </code>
277
     *
278
     * @param string|int      $key   The parameter position or name.
279
     * @param mixed           $value The parameter value.
280
     * @param string|int|null $type  One of the {@link \Doctrine\DBAL\ParameterType} constants.
281
     *
282
     * @return $this This QueryBuilder instance.
283
     */
284
    public function setParameter($key, $value, $type = null)
285
    {
286
        if ($type !== null) {
287
            $this->paramTypes[$key] = $type;
288
        }
289
290
        $this->params[$key] = $value;
291
292
        return $this;
293
    }
294
295
    /**
296
     * Sets a collection of query parameters for the query being constructed.
297
     *
298
     * <code>
299
     *     $qb = $conn->createQueryBuilder()
300
     *         ->select('u')
301
     *         ->from('users', 'u')
302
     *         ->where('u.id = :user_id1 OR u.id = :user_id2')
303
     *         ->setParameters(array(
304
     *             ':user_id1' => 1,
305
     *             ':user_id2' => 2
306
     *         ));
307
     * </code>
308
     *
309
     * @param array $params The query parameters to set.
310
     * @param array $types  The query parameters types to set.
311
     *
312
     * @return $this This QueryBuilder instance.
313
     */
314
    public function setParameters(array $params, array $types = [])
315
    {
316
        $this->paramTypes = $types;
317
        $this->params = $params;
318
319
        return $this;
320
    }
321
322
    /**
323
     * Gets all defined query parameters for the query being constructed indexed by parameter index or name.
324
     *
325
     * @return array The currently defined query parameters indexed by parameter index or name.
326
     */
327
    public function getParameters()
328
    {
329
        return $this->params;
330
    }
331
332
    /**
333
     * Gets a (previously set) query parameter of the query being constructed.
334
     *
335
     * @param mixed $key The key (index or name) of the bound parameter.
336
     *
337
     * @return mixed The value of the bound parameter.
338
     */
339
    public function getParameter($key)
340
    {
341
        return $this->params[$key] ?? null;
342
    }
343
344
    /**
345
     * Gets all defined query parameter types for the query being constructed indexed by parameter index or name.
346
     *
347
     * @return array The currently defined query parameter types indexed by parameter index or name.
348
     */
349
    public function getParameterTypes()
350
    {
351
        return $this->paramTypes;
352
    }
353
354
    /**
355
     * Gets a (previously set) query parameter type of the query being constructed.
356
     *
357
     * @param mixed $key The key (index or name) of the bound parameter type.
358
     *
359
     * @return mixed The value of the bound parameter type.
360
     */
361
    public function getParameterType($key)
362
    {
363
        return $this->paramTypes[$key] ?? null;
364
    }
365
366
    /**
367
     * Sets the position of the first result to retrieve (the "offset").
368
     *
369
     * @param int $firstResult The first result to return.
370
     *
371
     * @return $this This QueryBuilder instance.
372
     */
373
    public function setFirstResult($firstResult)
374
    {
375
        $this->state = self::STATE_DIRTY;
376
        $this->firstResult = $firstResult;
377
378
        return $this;
379
    }
380
381
    /**
382
     * Gets the position of the first result the query object was set to retrieve (the "offset").
383
     * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder.
384
     *
385
     * @return int The position of the first result.
386
     */
387
    public function getFirstResult()
388
    {
389
        return $this->firstResult;
390
    }
391
392
    /**
393
     * Sets the maximum number of results to retrieve (the "limit").
394
     *
395
     * @param int $maxResults The maximum number of results to retrieve.
396
     *
397
     * @return $this This QueryBuilder instance.
398
     */
399
    public function setMaxResults($maxResults)
400
    {
401
        $this->state = self::STATE_DIRTY;
402
        $this->maxResults = $maxResults;
403
404
        return $this;
405
    }
406
407
    /**
408
     * Gets the maximum number of results the query object was set to retrieve (the "limit").
409
     * Returns NULL if {@link setMaxResults} was not applied to this query builder.
410
     *
411
     * @return int The maximum number of results.
412
     */
413
    public function getMaxResults()
414
    {
415
        return $this->maxResults;
416
    }
417
418
    /**
419
     * Either appends to or replaces a single, generic query part.
420
     *
421
     * The available parts are: 'select', 'from', 'set', 'where',
422
     * 'groupBy', 'having' and 'orderBy'.
423
     *
424
     * @param string $sqlPartName
425
     * @param string $sqlPart
426
     * @param bool   $append
427
     *
428
     * @return $this This QueryBuilder instance.
429
     */
430
    public function add($sqlPartName, $sqlPart, $append = false)
431
    {
432
        $isArray = is_array($sqlPart);
433
        $isMultiple = is_array($this->sqlParts[$sqlPartName]);
434
435
        if ($isMultiple && !$isArray) {
436
            $sqlPart = [$sqlPart];
437
        }
438
439
        $this->state = self::STATE_DIRTY;
440
441
        if ($append) {
442
            if ($sqlPartName == "orderBy" || $sqlPartName == "groupBy" || $sqlPartName == "select" || $sqlPartName == "set") {
443
                foreach ($sqlPart as $part) {
0 ignored issues
show
Bug introduced by
The expression $sqlPart of type array<integer,string,{"0":"string"}>|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
444
                    $this->sqlParts[$sqlPartName][] = $part;
445
                }
446
            } elseif ($isArray && is_array($sqlPart[key($sqlPart)])) {
447
                $key = key($sqlPart);
448
                $this->sqlParts[$sqlPartName][$key][] = $sqlPart[$key];
449
            } elseif ($isMultiple) {
450
                $this->sqlParts[$sqlPartName][] = $sqlPart;
451
            } else {
452
                $this->sqlParts[$sqlPartName] = $sqlPart;
453
            }
454
455
            return $this;
456
        }
457
458
        $this->sqlParts[$sqlPartName] = $sqlPart;
459
460
        return $this;
461
    }
462
463
    /**
464
     * Specifies an item that is to be returned in the query result.
465
     * Replaces any previously specified selections, if any.
466
     *
467
     * <code>
468
     *     $qb = $conn->createQueryBuilder()
469
     *         ->select('u.id', 'p.id')
470
     *         ->from('users', 'u')
471
     *         ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
472
     * </code>
473
     *
474
     * @param mixed $select The selection expressions.
475
     *
476
     * @return $this This QueryBuilder instance.
477
     */
478 View Code Duplication
    public function select($select = null)
479
    {
480
        $this->type = self::SELECT;
481
482
        if (empty($select)) {
483
            return $this;
484
        }
485
486
        $selects = is_array($select) ? $select : func_get_args();
487
488
        return $this->add('select', $selects);
0 ignored issues
show
Documentation introduced by
$selects is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
489
    }
490
491
    /**
492
     * Adds an item that is to be returned in the query result.
493
     *
494
     * <code>
495
     *     $qb = $conn->createQueryBuilder()
496
     *         ->select('u.id')
497
     *         ->addSelect('p.id')
498
     *         ->from('users', 'u')
499
     *         ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id');
500
     * </code>
501
     *
502
     * @param mixed $select The selection expression.
503
     *
504
     * @return $this This QueryBuilder instance.
505
     */
506 View Code Duplication
    public function addSelect($select = null)
507
    {
508
        $this->type = self::SELECT;
509
510
        if (empty($select)) {
511
            return $this;
512
        }
513
514
        $selects = is_array($select) ? $select : func_get_args();
515
516
        return $this->add('select', $selects, true);
0 ignored issues
show
Documentation introduced by
$selects is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
517
    }
518
519
    /**
520
     * Turns the query being built into a bulk delete query that ranges over
521
     * a certain table.
522
     *
523
     * <code>
524
     *     $qb = $conn->createQueryBuilder()
525
     *         ->delete('users', 'u')
526
     *         ->where('u.id = :user_id');
527
     *         ->setParameter(':user_id', 1);
528
     * </code>
529
     *
530
     * @param string $delete The table whose rows are subject to the deletion.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $delete not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
531
     * @param string $alias  The table alias used in the constructed query.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $alias not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
532
     *
533
     * @return $this This QueryBuilder instance.
534
     */
535 View Code Duplication
    public function delete($delete = null, $alias = null)
536
    {
537
        $this->type = self::DELETE;
538
539
        if ( ! $delete) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $delete of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
540
            return $this;
541
        }
542
543
        return $this->add('from', [
0 ignored issues
show
Documentation introduced by
array('table' => $delete, 'alias' => $alias) is of type array<string,string|null..."alias":"string|null"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
544
            'table' => $delete,
545
            'alias' => $alias
546
        ]);
547
    }
548
549
    /**
550
     * Turns the query being built into a bulk update query that ranges over
551
     * a certain table
552
     *
553
     * <code>
554
     *     $qb = $conn->createQueryBuilder()
555
     *         ->update('users', 'u')
556
     *         ->set('u.last_login', 'NOW()')
557
     *         ->where('u.id = ?');
558
     * </code>
559
     *
560
     * @param string $update The table whose rows are subject to the update.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $update not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
561
     * @param string $alias  The table alias used in the constructed query.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $alias not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
562
     *
563
     * @return $this This QueryBuilder instance.
564
     */
565 View Code Duplication
    public function update($update = null, $alias = null)
566
    {
567
        $this->type = self::UPDATE;
568
569
        if ( ! $update) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $update of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
570
            return $this;
571
        }
572
573
        return $this->add('from', [
0 ignored issues
show
Documentation introduced by
array('table' => $update, 'alias' => $alias) is of type array<string,string|null..."alias":"string|null"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
574
            'table' => $update,
575
            'alias' => $alias
576
        ]);
577
    }
578
579
    /**
580
     * Turns the query being built into an insert query that inserts into
581
     * a certain table
582
     *
583
     * <code>
584
     *     $qb = $conn->createQueryBuilder()
585
     *         ->insert('users')
586
     *         ->values(
587
     *             array(
588
     *                 'name' => '?',
589
     *                 'password' => '?'
590
     *             )
591
     *         );
592
     * </code>
593
     *
594
     * @param string $insert The table into which the rows should be inserted.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $insert not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
595
     *
596
     * @return $this This QueryBuilder instance.
597
     */
598
    public function insert($insert = null)
599
    {
600
        $this->type = self::INSERT;
601
602
        if ( ! $insert) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $insert of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
603
            return $this;
604
        }
605
606
        return $this->add('from', [
0 ignored issues
show
Documentation introduced by
array('table' => $insert) is of type array<string,string,{"table":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
607
            'table' => $insert
608
        ]);
609
    }
610
611
    /**
612
     * Creates and adds a query root corresponding to the table identified by the
613
     * given alias, forming a cartesian product with any existing query roots.
614
     *
615
     * <code>
616
     *     $qb = $conn->createQueryBuilder()
617
     *         ->select('u.id')
618
     *         ->from('users', 'u')
619
     * </code>
620
     *
621
     * @param string      $from  The table.
622
     * @param string|null $alias The alias of the table.
623
     *
624
     * @return $this This QueryBuilder instance.
625
     */
626
    public function from($from, $alias = null)
627
    {
628
        return $this->add('from', [
0 ignored issues
show
Documentation introduced by
array('table' => $from, 'alias' => $alias) is of type array<string,string|null..."alias":"string|null"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
629
            'table' => $from,
630
            'alias' => $alias
631
        ], true);
632
    }
633
634
    /**
635
     * Creates and adds a join to the query.
636
     *
637
     * <code>
638
     *     $qb = $conn->createQueryBuilder()
639
     *         ->select('u.name')
640
     *         ->from('users', 'u')
641
     *         ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1');
642
     * </code>
643
     *
644
     * @param string $fromAlias The alias that points to a from clause.
645
     * @param string $join      The table name to join.
646
     * @param string $alias     The alias of the join table.
647
     * @param string $condition The condition for the join.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $condition not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
648
     *
649
     * @return $this This QueryBuilder instance.
650
     */
651
    public function join($fromAlias, $join, $alias, $condition = null)
652
    {
653
        return $this->innerJoin($fromAlias, $join, $alias, $condition);
654
    }
655
656
    /**
657
     * Creates and adds a join to the query.
658
     *
659
     * <code>
660
     *     $qb = $conn->createQueryBuilder()
661
     *         ->select('u.name')
662
     *         ->from('users', 'u')
663
     *         ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
664
     * </code>
665
     *
666
     * @param string $fromAlias The alias that points to a from clause.
667
     * @param string $join      The table name to join.
668
     * @param string $alias     The alias of the join table.
669
     * @param string $condition The condition for the join.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $condition not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
670
     *
671
     * @return $this This QueryBuilder instance.
672
     */
673 View Code Duplication
    public function innerJoin($fromAlias, $join, $alias, $condition = null)
674
    {
675
        return $this->add('join', [
0 ignored issues
show
Documentation introduced by
array($fromAlias => arra...dition' => $condition)) is of type array<string,array<strin...ition":"string|null"}>>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
676
            $fromAlias => [
677
                'joinType'      => 'inner',
678
                'joinTable'     => $join,
679
                'joinAlias'     => $alias,
680
                'joinCondition' => $condition
681
            ]
682
        ], true);
683
    }
684
685
    /**
686
     * Creates and adds a left join to the query.
687
     *
688
     * <code>
689
     *     $qb = $conn->createQueryBuilder()
690
     *         ->select('u.name')
691
     *         ->from('users', 'u')
692
     *         ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
693
     * </code>
694
     *
695
     * @param string $fromAlias The alias that points to a from clause.
696
     * @param string $join      The table name to join.
697
     * @param string $alias     The alias of the join table.
698
     * @param string $condition The condition for the join.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $condition not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
699
     *
700
     * @return $this This QueryBuilder instance.
701
     */
702 View Code Duplication
    public function leftJoin($fromAlias, $join, $alias, $condition = null)
703
    {
704
        return $this->add('join', [
0 ignored issues
show
Documentation introduced by
array($fromAlias => arra...dition' => $condition)) is of type array<string,array<strin...ition":"string|null"}>>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
705
            $fromAlias => [
706
                'joinType'      => 'left',
707
                'joinTable'     => $join,
708
                'joinAlias'     => $alias,
709
                'joinCondition' => $condition
710
            ]
711
        ], true);
712
    }
713
714
    /**
715
     * Creates and adds a right join to the query.
716
     *
717
     * <code>
718
     *     $qb = $conn->createQueryBuilder()
719
     *         ->select('u.name')
720
     *         ->from('users', 'u')
721
     *         ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
722
     * </code>
723
     *
724
     * @param string $fromAlias The alias that points to a from clause.
725
     * @param string $join      The table name to join.
726
     * @param string $alias     The alias of the join table.
727
     * @param string $condition The condition for the join.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $condition not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
728
     *
729
     * @return $this This QueryBuilder instance.
730
     */
731 View Code Duplication
    public function rightJoin($fromAlias, $join, $alias, $condition = null)
732
    {
733
        return $this->add('join', [
0 ignored issues
show
Documentation introduced by
array($fromAlias => arra...dition' => $condition)) is of type array<string,array<strin...ition":"string|null"}>>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
734
            $fromAlias => [
735
                'joinType'      => 'right',
736
                'joinTable'     => $join,
737
                'joinAlias'     => $alias,
738
                'joinCondition' => $condition
739
            ]
740
        ], true);
741
    }
742
743
    /**
744
     * Sets a new value for a column in a bulk update query.
745
     *
746
     * <code>
747
     *     $qb = $conn->createQueryBuilder()
748
     *         ->update('users', 'u')
749
     *         ->set('u.last_login', 'NOW()')
750
     *         ->where('u.id = ?');
751
     * </code>
752
     *
753
     * @param string $key   The column to set.
754
     * @param string $value The value, expression, placeholder, etc.
755
     *
756
     * @return $this This QueryBuilder instance.
757
     */
758
    public function set($key, $value)
759
    {
760
        return $this->add('set', $key .' = ' . $value, true);
761
    }
762
763
    /**
764
     * Specifies one or more restrictions to the query result.
765
     * Replaces any previously specified restrictions, if any.
766
     *
767
     * <code>
768
     *     $qb = $conn->createQueryBuilder()
769
     *         ->select('u.name')
770
     *         ->from('users', 'u')
771
     *         ->where('u.id = ?');
772
     *
773
     *     // You can optionally programatically build and/or expressions
774
     *     $qb = $conn->createQueryBuilder();
775
     *
776
     *     $or = $qb->expr()->orx();
777
     *     $or->add($qb->expr()->eq('u.id', 1));
778
     *     $or->add($qb->expr()->eq('u.id', 2));
779
     *
780
     *     $qb->update('users', 'u')
781
     *         ->set('u.last_login', 'NOW()')
782
     *         ->where($or);
783
     * </code>
784
     *
785
     * @param mixed $predicates The restriction predicates.
786
     *
787
     * @return $this This QueryBuilder instance.
788
     */
789 View Code Duplication
    public function where($predicates)
790
    {
791
        if ( ! (func_num_args() == 1 && $predicates instanceof CompositeExpression)) {
792
            $predicates = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args());
793
        }
794
795
        return $this->add('where', $predicates);
796
    }
797
798
    /**
799
     * Adds one or more restrictions to the query results, forming a logical
800
     * conjunction with any previously specified restrictions.
801
     *
802
     * <code>
803
     *     $qb = $conn->createQueryBuilder()
804
     *         ->select('u')
805
     *         ->from('users', 'u')
806
     *         ->where('u.username LIKE ?')
807
     *         ->andWhere('u.is_active = 1');
808
     * </code>
809
     *
810
     * @param mixed $where The query restrictions.
811
     *
812
     * @return $this This QueryBuilder instance.
813
     *
814
     * @see where()
815
     */
816 View Code Duplication
    public function andWhere($where)
0 ignored issues
show
Unused Code introduced by
The parameter $where is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
817
    {
818
        $args = func_get_args();
819
        $where = $this->getQueryPart('where');
820
821
        if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_AND) {
822
            $where->addMultiple($args);
823
        } else {
824
            array_unshift($args, $where);
825
            $where = new CompositeExpression(CompositeExpression::TYPE_AND, $args);
826
        }
827
828
        return $this->add('where', $where, true);
829
    }
830
831
    /**
832
     * Adds one or more restrictions to the query results, forming a logical
833
     * disjunction with any previously specified restrictions.
834
     *
835
     * <code>
836
     *     $qb = $em->createQueryBuilder()
837
     *         ->select('u.name')
838
     *         ->from('users', 'u')
839
     *         ->where('u.id = 1')
840
     *         ->orWhere('u.id = 2');
841
     * </code>
842
     *
843
     * @param mixed $where The WHERE statement.
844
     *
845
     * @return $this This QueryBuilder instance.
846
     *
847
     * @see where()
848
     */
849 View Code Duplication
    public function orWhere($where)
0 ignored issues
show
Unused Code introduced by
The parameter $where is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
850
    {
851
        $args = func_get_args();
852
        $where = $this->getQueryPart('where');
853
854
        if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_OR) {
855
            $where->addMultiple($args);
856
        } else {
857
            array_unshift($args, $where);
858
            $where = new CompositeExpression(CompositeExpression::TYPE_OR, $args);
859
        }
860
861
        return $this->add('where', $where, true);
862
    }
863
864
    /**
865
     * Specifies a grouping over the results of the query.
866
     * Replaces any previously specified groupings, if any.
867
     *
868
     * <code>
869
     *     $qb = $conn->createQueryBuilder()
870
     *         ->select('u.name')
871
     *         ->from('users', 'u')
872
     *         ->groupBy('u.id');
873
     * </code>
874
     *
875
     * @param mixed $groupBy The grouping expression.
876
     *
877
     * @return $this This QueryBuilder instance.
878
     */
879 View Code Duplication
    public function groupBy($groupBy)
880
    {
881
        if (empty($groupBy)) {
882
            return $this;
883
        }
884
885
        $groupBy = is_array($groupBy) ? $groupBy : func_get_args();
886
887
        return $this->add('groupBy', $groupBy, false);
0 ignored issues
show
Documentation introduced by
$groupBy is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
888
    }
889
890
891
    /**
892
     * Adds a grouping expression to the query.
893
     *
894
     * <code>
895
     *     $qb = $conn->createQueryBuilder()
896
     *         ->select('u.name')
897
     *         ->from('users', 'u')
898
     *         ->groupBy('u.lastLogin');
899
     *         ->addGroupBy('u.createdAt')
900
     * </code>
901
     *
902
     * @param mixed $groupBy The grouping expression.
903
     *
904
     * @return $this This QueryBuilder instance.
905
     */
906 View Code Duplication
    public function addGroupBy($groupBy)
907
    {
908
        if (empty($groupBy)) {
909
            return $this;
910
        }
911
912
        $groupBy = is_array($groupBy) ? $groupBy : func_get_args();
913
914
        return $this->add('groupBy', $groupBy, true);
0 ignored issues
show
Documentation introduced by
$groupBy is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
915
    }
916
917
    /**
918
     * Sets a value for a column in an insert query.
919
     *
920
     * <code>
921
     *     $qb = $conn->createQueryBuilder()
922
     *         ->insert('users')
923
     *         ->values(
924
     *             array(
925
     *                 'name' => '?'
926
     *             )
927
     *         )
928
     *         ->setValue('password', '?');
929
     * </code>
930
     *
931
     * @param string $column The column into which the value should be inserted.
932
     * @param string $value  The value that should be inserted into the column.
933
     *
934
     * @return $this This QueryBuilder instance.
935
     */
936
    public function setValue($column, $value)
937
    {
938
        $this->sqlParts['values'][$column] = $value;
939
940
        return $this;
941
    }
942
943
    /**
944
     * Specifies values for an insert query indexed by column names.
945
     * Replaces any previous values, if any.
946
     *
947
     * <code>
948
     *     $qb = $conn->createQueryBuilder()
949
     *         ->insert('users')
950
     *         ->values(
951
     *             array(
952
     *                 'name' => '?',
953
     *                 'password' => '?'
954
     *             )
955
     *         );
956
     * </code>
957
     *
958
     * @param array $values The values to specify for the insert query indexed by column names.
959
     *
960
     * @return $this This QueryBuilder instance.
961
     */
962
    public function values(array $values)
963
    {
964
        return $this->add('values', $values);
0 ignored issues
show
Documentation introduced by
$values is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
965
    }
966
967
    /**
968
     * Specifies a restriction over the groups of the query.
969
     * Replaces any previous having restrictions, if any.
970
     *
971
     * @param mixed $having The restriction over the groups.
972
     *
973
     * @return $this This QueryBuilder instance.
974
     */
975 View Code Duplication
    public function having($having)
976
    {
977
        if ( ! (func_num_args() == 1 && $having instanceof CompositeExpression)) {
978
            $having = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args());
979
        }
980
981
        return $this->add('having', $having);
982
    }
983
984
    /**
985
     * Adds a restriction over the groups of the query, forming a logical
986
     * conjunction with any existing having restrictions.
987
     *
988
     * @param mixed $having The restriction to append.
989
     *
990
     * @return $this This QueryBuilder instance.
991
     */
992 View Code Duplication
    public function andHaving($having)
0 ignored issues
show
Unused Code introduced by
The parameter $having is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
993
    {
994
        $args = func_get_args();
995
        $having = $this->getQueryPart('having');
996
997
        if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_AND) {
998
            $having->addMultiple($args);
999
        } else {
1000
            array_unshift($args, $having);
1001
            $having = new CompositeExpression(CompositeExpression::TYPE_AND, $args);
1002
        }
1003
1004
        return $this->add('having', $having);
1005
    }
1006
1007
    /**
1008
     * Adds a restriction over the groups of the query, forming a logical
1009
     * disjunction with any existing having restrictions.
1010
     *
1011
     * @param mixed $having The restriction to add.
1012
     *
1013
     * @return $this This QueryBuilder instance.
1014
     */
1015 View Code Duplication
    public function orHaving($having)
0 ignored issues
show
Unused Code introduced by
The parameter $having is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1016
    {
1017
        $args = func_get_args();
1018
        $having = $this->getQueryPart('having');
1019
1020
        if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_OR) {
1021
            $having->addMultiple($args);
1022
        } else {
1023
            array_unshift($args, $having);
1024
            $having = new CompositeExpression(CompositeExpression::TYPE_OR, $args);
1025
        }
1026
1027
        return $this->add('having', $having);
1028
    }
1029
1030
    /**
1031
     * Specifies an ordering for the query results.
1032
     * Replaces any previously specified orderings, if any.
1033
     *
1034
     * @param string $sort  The ordering expression.
1035
     * @param string $order The ordering direction.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $order not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1036
     *
1037
     * @return $this This QueryBuilder instance.
1038
     */
1039
    public function orderBy($sort, $order = null)
1040
    {
1041
        return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), false);
1042
    }
1043
1044
    /**
1045
     * Adds an ordering to the query results.
1046
     *
1047
     * @param string $sort  The ordering expression.
1048
     * @param string $order The ordering direction.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $order not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1049
     *
1050
     * @return $this This QueryBuilder instance.
1051
     */
1052
    public function addOrderBy($sort, $order = null)
1053
    {
1054
        return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), true);
1055
    }
1056
1057
    /**
1058
     * Gets a query part by its name.
1059
     *
1060
     * @param string $queryPartName
1061
     *
1062
     * @return mixed
1063
     */
1064
    public function getQueryPart($queryPartName)
1065
    {
1066
        return $this->sqlParts[$queryPartName];
1067
    }
1068
1069
    /**
1070
     * Gets all query parts.
1071
     *
1072
     * @return array
1073
     */
1074
    public function getQueryParts()
1075
    {
1076
        return $this->sqlParts;
1077
    }
1078
1079
    /**
1080
     * Resets SQL parts.
1081
     *
1082
     * @param array|null $queryPartNames
1083
     *
1084
     * @return $this This QueryBuilder instance.
1085
     */
1086
    public function resetQueryParts($queryPartNames = null)
1087
    {
1088
        if ($queryPartNames === null) {
1089
            $queryPartNames = array_keys($this->sqlParts);
1090
        }
1091
1092
        foreach ($queryPartNames as $queryPartName) {
1093
            $this->resetQueryPart($queryPartName);
1094
        }
1095
1096
        return $this;
1097
    }
1098
1099
    /**
1100
     * Resets a single SQL part.
1101
     *
1102
     * @param string $queryPartName
1103
     *
1104
     * @return $this This QueryBuilder instance.
1105
     */
1106
    public function resetQueryPart($queryPartName)
1107
    {
1108
        $this->sqlParts[$queryPartName] = is_array($this->sqlParts[$queryPartName])
1109
            ? [] : null;
1110
1111
        $this->state = self::STATE_DIRTY;
1112
1113
        return $this;
1114
    }
1115
1116
    /**
1117
     * @return string
1118
     *
1119
     * @throws \Doctrine\DBAL\Query\QueryException
1120
     */
1121
    private function getSQLForSelect()
1122
    {
1123
        $query = 'SELECT ' . implode(', ', $this->sqlParts['select']);
1124
1125
        $query .= ($this->sqlParts['from'] ? ' FROM ' . implode(', ', $this->getFromClauses()) : '')
1126
            . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '')
1127
            . ($this->sqlParts['groupBy'] ? ' GROUP BY ' . implode(', ', $this->sqlParts['groupBy']) : '')
1128
            . ($this->sqlParts['having'] !== null ? ' HAVING ' . ((string) $this->sqlParts['having']) : '')
1129
            . ($this->sqlParts['orderBy'] ? ' ORDER BY ' . implode(', ', $this->sqlParts['orderBy']) : '');
1130
1131
        if ($this->isLimitQuery()) {
1132
            return $this->connection->getDatabasePlatform()->modifyLimitQuery(
1133
                $query,
1134
                $this->maxResults,
1135
                $this->firstResult
1136
            );
1137
        }
1138
1139
        return $query;
1140
    }
1141
1142
    /**
1143
     * @return string[]
1144
     */
1145
    private function getFromClauses()
1146
    {
1147
        $fromClauses = [];
1148
        $knownAliases = [];
1149
1150
        // Loop through all FROM clauses
1151
        foreach ($this->sqlParts['from'] as $from) {
1152
            if ($from['alias'] === null) {
1153
                $tableSql = $from['table'];
1154
                $tableReference = $from['table'];
1155
            } else {
1156
                $tableSql = $from['table'] . ' ' . $from['alias'];
1157
                $tableReference = $from['alias'];
1158
            }
1159
1160
            $knownAliases[$tableReference] = true;
1161
1162
            $fromClauses[$tableReference] = $tableSql . $this->getSQLForJoins($tableReference, $knownAliases);
1163
        }
1164
1165
        $this->verifyAllAliasesAreKnown($knownAliases);
1166
1167
        return $fromClauses;
1168
    }
1169
1170
    /**
1171
     * @param array $knownAliases
1172
     *
1173
     * @throws QueryException
1174
     */
1175
    private function verifyAllAliasesAreKnown(array $knownAliases)
1176
    {
1177
        foreach ($this->sqlParts['join'] as $fromAlias => $joins) {
1178
            if ( ! isset($knownAliases[$fromAlias])) {
1179
                throw QueryException::unknownAlias($fromAlias, array_keys($knownAliases));
1180
            }
1181
        }
1182
    }
1183
1184
    /**
1185
     * @return bool
1186
     */
1187
    private function isLimitQuery()
1188
    {
1189
        return $this->maxResults !== null || $this->firstResult !== null;
1190
    }
1191
1192
    /**
1193
     * Converts this instance into an INSERT string in SQL.
1194
     *
1195
     * @return string
1196
     */
1197
    private function getSQLForInsert()
1198
    {
1199
        return 'INSERT INTO ' . $this->sqlParts['from']['table'] .
1200
        ' (' . implode(', ', array_keys($this->sqlParts['values'])) . ')' .
1201
        ' VALUES(' . implode(', ', $this->sqlParts['values']) . ')';
1202
    }
1203
1204
    /**
1205
     * Converts this instance into an UPDATE string in SQL.
1206
     *
1207
     * @return string
1208
     */
1209
    private function getSQLForUpdate()
1210
    {
1211
        $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : '');
1212
        $query = 'UPDATE ' . $table
1213
            . ' SET ' . implode(", ", $this->sqlParts['set'])
1214
            . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '');
1215
1216
        return $query;
1217
    }
1218
1219
    /**
1220
     * Converts this instance into a DELETE string in SQL.
1221
     *
1222
     * @return string
1223
     */
1224
    private function getSQLForDelete()
1225
    {
1226
        $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : '');
1227
        $query = 'DELETE FROM ' . $table . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '');
1228
1229
        return $query;
1230
    }
1231
1232
    /**
1233
     * Gets a string representation of this QueryBuilder which corresponds to
1234
     * the final SQL query being constructed.
1235
     *
1236
     * @return string The string representation of this QueryBuilder.
1237
     */
1238
    public function __toString()
1239
    {
1240
        return $this->getSQL();
1241
    }
1242
1243
    /**
1244
     * Creates a new named parameter and bind the value $value to it.
1245
     *
1246
     * This method provides a shortcut for PDOStatement::bindValue
1247
     * when using prepared statements.
1248
     *
1249
     * The parameter $value specifies the value that you want to bind. If
1250
     * $placeholder is not provided bindValue() will automatically create a
1251
     * placeholder for you. An automatic placeholder will be of the name
1252
     * ':dcValue1', ':dcValue2' etc.
1253
     *
1254
     * For more information see {@link http://php.net/pdostatement-bindparam}
1255
     *
1256
     * Example:
1257
     * <code>
1258
     * $value = 2;
1259
     * $q->eq( 'id', $q->bindValue( $value ) );
1260
     * $stmt = $q->executeQuery(); // executed with 'id = 2'
1261
     * </code>
1262
     *
1263
     * @license New BSD License
1264
     * @link http://www.zetacomponents.org
1265
     *
1266
     * @param mixed  $value
1267
     * @param mixed  $type
1268
     * @param string $placeHolder The name to bind with. The string must start with a colon ':'.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $placeHolder not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1269
     *
1270
     * @return string the placeholder name used.
1271
     */
1272
    public function createNamedParameter($value, $type = ParameterType::STRING, $placeHolder = null)
1273
    {
1274
        if ($placeHolder === null) {
1275
            $this->boundCounter++;
1276
            $placeHolder = ":dcValue" . $this->boundCounter;
1277
        }
1278
        $this->setParameter(substr($placeHolder, 1), $value, $type);
1279
1280
        return $placeHolder;
1281
    }
1282
1283
    /**
1284
     * Creates a new positional parameter and bind the given value to it.
1285
     *
1286
     * Attention: If you are using positional parameters with the query builder you have
1287
     * to be very careful to bind all parameters in the order they appear in the SQL
1288
     * statement , otherwise they get bound in the wrong order which can lead to serious
1289
     * bugs in your code.
1290
     *
1291
     * Example:
1292
     * <code>
1293
     *  $qb = $conn->createQueryBuilder();
1294
     *  $qb->select('u.*')
1295
     *     ->from('users', 'u')
1296
     *     ->where('u.username = ' . $qb->createPositionalParameter('Foo', ParameterType::STRING))
1297
     *     ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', ParameterType::STRING))
1298
     * </code>
1299
     *
1300
     * @param mixed $value
1301
     * @param int   $type
1302
     *
1303
     * @return string
1304
     */
1305
    public function createPositionalParameter($value, $type = ParameterType::STRING)
1306
    {
1307
        $this->boundCounter++;
1308
        $this->setParameter($this->boundCounter, $value, $type);
1309
1310
        return "?";
1311
    }
1312
1313
    /**
1314
     * @param string $fromAlias
1315
     * @param array  $knownAliases
1316
     *
1317
     * @return string
1318
     *
1319
     * @throws QueryException
1320
     */
1321
    private function getSQLForJoins($fromAlias, array &$knownAliases)
1322
    {
1323
        $sql = '';
1324
1325
        if (isset($this->sqlParts['join'][$fromAlias])) {
1326
            foreach ($this->sqlParts['join'][$fromAlias] as $join) {
1327
                if (array_key_exists($join['joinAlias'], $knownAliases)) {
1328
                    throw QueryException::nonUniqueAlias($join['joinAlias'], array_keys($knownAliases));
1329
                }
1330
                $sql .= ' ' . strtoupper($join['joinType'])
1331
                    . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias']
1332
                    . ' ON ' . ((string) $join['joinCondition']);
1333
                $knownAliases[$join['joinAlias']] = true;
1334
            }
1335
1336
            foreach ($this->sqlParts['join'][$fromAlias] as $join) {
1337
                $sql .= $this->getSQLForJoins($join['joinAlias'], $knownAliases);
1338
            }
1339
        }
1340
1341
        return $sql;
1342
    }
1343
1344
    /**
1345
     * Deep clone of all expression objects in the SQL parts.
1346
     *
1347
     * @return void
1348
     */
1349
    public function __clone()
1350
    {
1351
        foreach ($this->sqlParts as $part => $elements) {
1352
            if (is_array($this->sqlParts[$part])) {
1353
                foreach ($this->sqlParts[$part] as $idx => $element) {
1354
                    if (is_object($element)) {
1355
                        $this->sqlParts[$part][$idx] = clone $element;
1356
                    }
1357
                }
1358
            } elseif (is_object($elements)) {
1359
                $this->sqlParts[$part] = clone $elements;
1360
            }
1361
        }
1362
1363
        foreach ($this->params as $name => $param) {
1364
            if (is_object($param)) {
1365
                $this->params[$name] = clone $param;
1366
            }
1367
        }
1368
    }
1369
}
1370