Completed
Pull Request — 3.x (#112)
by
unknown
01:39
created

Select::isDistinct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
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\Common;
10
11
use Aura\SqlQuery\AbstractQuery;
12
use Aura\SqlQuery\Exception;
13
14
/**
15
 *
16
 * An object for SELECT queries.
17
 *
18
 * @package Aura.SqlQuery
19
 *
20
 */
21
class Select extends AbstractQuery implements SelectInterface, SubselectInterface
22
{
23
    /**
24
     *
25
     * An array of union SELECT statements.
26
     *
27
     * @var array
28
     *
29
     */
30
    protected $union = array();
31
32
    /**
33
     *
34
     * Is this a SELECT FOR UPDATE?
35
     *
36
     * @var
37
     *
38
     */
39
    protected $for_update = false;
40
41
    /**
42
     *
43
     * The columns to be selected.
44
     *
45
     * @var array
46
     *
47
     */
48
    protected $cols = array();
49
50
    /**
51
     *
52
     * Select from these tables; includes JOIN clauses.
53
     *
54
     * @var array
55
     *
56
     */
57
    protected $from = array();
58
59
    /**
60
     *
61
     * The current key in the `$from` array.
62
     *
63
     * @var int
64
     *
65
     */
66
    protected $from_key = -1;
67
68
    /**
69
     *
70
     * Tracks which JOIN clauses are attached to which FROM tables.
71
     *
72
     * @var array
73
     *
74
     */
75
    protected $join = array();
76
77
    /**
78
     *
79
     * GROUP BY these columns.
80
     *
81
     * @var array
82
     *
83
     */
84
    protected $group_by = array();
85
86
    /**
87
     *
88
     * The list of HAVING conditions.
89
     *
90
     * @var array
91
     *
92
     */
93
    protected $having = array();
94
95
    /**
96
     *
97
     * The page number to select.
98
     *
99
     * @var int
100
     *
101
     */
102
    protected $page = 0;
103
104
    /**
105
     *
106
     * The number of rows per page.
107
     *
108
     * @var int
109
     *
110
     */
111
    protected $paging = 10;
112
113
    /**
114
     *
115
     * Tracks table references to avoid duplicate identifiers.
116
     *
117
     * @var array
118
     *
119
     */
120
    protected $table_refs = array();
121
122
    /**
123
     *
124
     * Returns this query object as an SQL statement string.
125
     *
126
     * @return string An SQL statement string.
127
     *
128
     */
129 184
    public function getStatement()
130
    {
131 184
        $union = '';
132 184
        if ($this->union) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->union of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
133 15
            $union = implode(PHP_EOL, $this->union) . PHP_EOL;
134 15
        }
135 184
        return $union . $this->build();
136
    }
137
138
    /**
139
     *
140
     * Sets the number of rows per page.
141
     *
142
     * @param int $paging The number of rows to page at.
143
     *
144
     * @return $this
145
     *
146
     */
147 10
    public function setPaging($paging)
148
    {
149 10
        $this->paging = (int) $paging;
150 10
        if ($this->page) {
151 5
            $this->setPagingLimitOffset();
152 5
        }
153 10
        return $this;
154
    }
155
156
    /**
157
     *
158
     * Gets the number of rows per page.
159
     *
160
     * @return int The number of rows per page.
161
     *
162
     */
163 10
    public function getPaging()
164
    {
165 10
        return $this->paging;
166
    }
167
168
    /**
169
     *
170
     * Makes the select FOR UPDATE (or not).
171
     *
172
     * @param bool $enable Whether or not the SELECT is FOR UPDATE (default
173
     * true).
174
     *
175
     * @return $this
176
     *
177
     */
178 20
    public function forUpdate($enable = true)
179
    {
180 20
        $this->for_update = (bool) $enable;
181 20
        return $this;
182
    }
183
184
    /**
185
     *
186
     * Makes the select DISTINCT (or not).
187
     *
188
     * @param bool $enable Whether or not the SELECT is DISTINCT (default
189
     * true).
190
     *
191
     * @return $this
192
     *
193
     */
194 16
    public function distinct($enable = true)
195
    {
196 16
        $this->setFlag('DISTINCT', $enable);
197 16
        return $this;
198
    }
199
200
    /**
201
     *
202
     * Is the select DISTINCT?
203
     *
204
     * @return bool
205
     *
206
     */
207 10
    public function isDistinct()
208
    {
209 10
        return $this->hasFlag('DISTINCT');
210
    }
211
212
    /**
213
     *
214
     * Adds columns to the query.
215
     *
216
     * Multiple calls to cols() will append to the list of columns, not
217
     * overwrite the previous columns.
218
     *
219
     * @param array $cols The column(s) to add to the query. The elements can be
220
     * any mix of these: `array("col", "col AS alias", "col" => "alias")`
221
     *
222
     * @return $this
223
     *
224
     */
225 224
    public function cols(array $cols)
226
    {
227 224
        foreach ($cols as $key => $val) {
228 224
            $this->addCol($key, $val);
229 224
        }
230 224
        return $this;
231
    }
232
233
    /**
234
     *
235
     * Adds a column and alias to the columns to be selected.
236
     *
237
     * @param mixed $key If an integer, ignored. Otherwise, the column to be
238
     * added.
239
     *
240
     * @param mixed $val If $key was an integer, the column to be added;
241
     * otherwise, the column alias.
242
     *
243
     * @return null
244
     *
245
     */
246 224
    protected function addCol($key, $val)
247
    {
248 224
        if (is_string($key)) {
249
            // [col => alias]
250 25
            $this->cols[$val] = $key;
251 25
        } else {
252 214
            $this->addColWithAlias($val);
253
        }
254 224
    }
255
256
    /**
257
     *
258
     * Adds a column with an alias to the columns to be selected.
259
     *
260
     * @param string $spec The column specification: "col alias",
261
     * "col AS alias", or something else entirely.
262
     *
263
     * @return null
264
     *
265
     */
266 214
    protected function addColWithAlias($spec)
267
    {
268 214
        $parts = explode(' ', $spec);
269 214
        $count = count($parts);
270 214
        if ($count == 2) {
271
            // "col alias"
272 5
            $this->cols[$parts[1]] = $parts[0];
273 214
        } elseif ($count == 3 && strtoupper($parts[1]) == 'AS') {
274
            // "col AS alias"
275 5
            $this->cols[$parts[2]] = $parts[0];
276 5
        } else {
277
            // no recognized alias
278 214
            $this->cols[] = $spec;
279
        }
280 214
    }
281
282
    /**
283
     *
284
     * Remove a column via its alias.
285
     *
286
     * @param string $alias The column to remove
287
     *
288
     * @return bool
289
     *
290
     */
291 15
    public function removeCol($alias)
292
    {
293 15
        if (isset($this->cols[$alias])) {
294 5
            unset($this->cols[$alias]);
295
296 5
            return true;
297
        }
298
299 10
        $index = array_search($alias, $this->cols);
300 10
        if ($index !== false) {
301 5
            unset($this->cols[$index]);
302 5
            return true;
303
        }
304
305 5
        return false;
306
    }
307
308
    /**
309
     *
310
     * Has the column or alias been added to the query?
311
     *
312
     * @param string $alias The column or alias to look for
313
     *
314
     * @return bool
315
     *
316
     */
317 5
    public function hasCol($alias)
318
    {
319 5
        return isset($this->cols[$alias]) || array_search($alias, $this->cols) !== false;
320
    }
321
322
    /**
323
     *
324
     * Does the query have any columns in it?
325
     *
326
     * @return bool
327
     *
328
     */
329 5
    public function hasCols()
330
    {
331 5
        return (bool) $this->cols;
332
    }
333
334
    /**
335
     *
336
     * Returns a list of columns.
337
     *
338
     * @return array
339
     *
340
     */
341 15
    public function getCols()
342
    {
343 15
        return $this->cols;
344
    }
345
346
    /**
347
     *
348
     * Tracks table references.
349
     *
350
     * @param string $type FROM, JOIN, etc.
351
     *
352
     * @param string $spec The table and alias name.
353
     *
354
     * @return null
355
     *
356
     * @throws Exception when the reference has already been used.
357
     *
358
     */
359 149
    protected function addTableRef($type, $spec)
360
    {
361 149
        $name = $spec;
362
363 149
        $pos = strripos($name, ' AS ');
364 149
        if ($pos !== false) {
365 35
            $name = trim(substr($name, $pos + 4));
366 35
        }
367
368 149
        if (isset($this->table_refs[$name])) {
369 25
            $used = $this->table_refs[$name];
370 25
            throw new Exception("Cannot reference '$type $spec' after '$used'");
371
        }
372
373 149
        $this->table_refs[$name] = "$type $spec";
374 149
    }
375
376
    /**
377
     *
378
     * Adds a FROM element to the query; quotes the table name automatically.
379
     *
380
     * @param string $spec The table specification; "foo" or "foo AS bar".
381
     *
382
     * @return $this
383
     *
384
     */
385 139
    public function from($spec)
386
    {
387 139
        $this->addTableRef('FROM', $spec);
388 139
        return $this->addFrom($this->quoter->quoteName($spec));
0 ignored issues
show
Bug introduced by
It seems like $this->quoter->quoteName($spec) targeting Aura\SqlQuery\Quoter::quoteName() can also be of type array; however, Aura\SqlQuery\Common\Select::addFrom() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
389
    }
390
391
    /**
392
     *
393
     * Adds a raw unquoted FROM element to the query; useful for adding FROM
394
     * elements that are functions.
395
     *
396
     * @param string $spec The table specification, e.g. "function_name()".
397
     *
398
     * @return $this
399
     *
400
     */
401 5
    public function fromRaw($spec)
402
    {
403 5
        $this->addTableRef('FROM', $spec);
404 5
        return $this->addFrom($spec);
405
    }
406
407
    /**
408
     *
409
     * Adds to the $from property and increments the key count.
410
     *
411
     * @param string $spec The table specification.
412
     *
413
     * @return $this
414
     *
415
     */
416 149
    protected function addFrom($spec)
417
    {
418 149
        $this->from[] = array($spec);
419 149
        $this->from_key ++;
420 149
        return $this;
421
    }
422
423
    /**
424
     *
425
     * Adds an aliased sub-select to the query.
426
     *
427
     * @param string|Select $spec If a Select object, use as the sub-select;
428
     * if a string, the sub-select string.
429
     *
430
     * @param string $name The alias name for the sub-select.
431
     *
432
     * @return $this
433
     *
434
     */
435 15
    public function fromSubSelect($spec, $name)
436
    {
437 15
        $this->addTableRef('FROM (SELECT ...) AS', $name);
438 10
        $spec = $this->subSelect($spec, '        ');
439 10
        $name = $this->quoter->quoteName($name);
440 10
        return $this->addFrom("({$spec}    ) AS $name");
441
    }
442
443
    /**
444
     *
445
     * Formats a sub-SELECT statement, binding values from a Select object as
446
     * needed.
447
     *
448
     * @param string|SelectInterface $spec A sub-SELECT specification.
449
     *
450
     * @param string $indent Indent each line with this string.
451
     *
452
     * @return string The sub-SELECT string.
453
     *
454
     */
455 25
    protected function subSelect($spec, $indent)
456
    {
457 25
        if ($spec instanceof SelectInterface) {
458 10
            $this->bindValues($spec->getBindValues());
459 10
        }
460
461 25
        return PHP_EOL . $indent
462 25
            . ltrim(preg_replace('/^/m', $indent, (string) $spec))
463 25
            . PHP_EOL;
464
    }
465
466
    /**
467
     *
468
     * Adds a JOIN table and columns to the query.
469
     *
470
     * @param string $join The join type: inner, left, natural, etc.
471
     *
472
     * @param string $spec The table specification; "foo" or "foo AS bar".
473
     *
474
     * @param string $cond Join on this condition.
475
     *
476
     * @param array $bind Values to bind to ?-placeholders in the condition.
477
     *
478
     * @return $this
479
     *
480
     * @throws Exception
481
     *
482
     */
483 45
    public function join($join, $spec, $cond = null, array $bind = array())
484
    {
485 45
        $join = strtoupper(ltrim("$join JOIN"));
486 45
        $this->addTableRef($join, $spec);
487
488 40
        $spec = $this->quoter->quoteName($spec);
489 40
        $cond = $this->fixJoinCondition($cond, $bind);
490 40
        return $this->addJoin(rtrim("$join $spec $cond"));
491
    }
492
493
    /**
494
     *
495
     * Fixes a JOIN condition to quote names in the condition and prefix it
496
     * with a condition type ('ON' is the default and 'USING' is recognized).
497
     *
498
     * @param string $cond Join on this condition.
499
     *
500
     * @param array $bind Values to bind to ?-placeholders in the condition.
501
     *
502
     * @return string
503
     *
504
     */
505 55
    protected function fixJoinCondition($cond, array $bind)
506
    {
507 55
        if (! $cond) {
508 25
            return '';
509
        }
510
511 55
        $cond = $this->quoter->quoteNamesIn($cond);
512 55
        $cond = $this->rebuildCondAndBindValues($cond, $bind);
513
514 55
        if (strtoupper(substr(ltrim($cond), 0, 3)) == 'ON ') {
515 5
            return $cond;
516
        }
517
518 55
        if (strtoupper(substr(ltrim($cond), 0, 6)) == 'USING ') {
519 5
            return $cond;
520
        }
521
522 50
        return 'ON ' . $cond;
523
    }
524
525
    /**
526
     *
527
     * Adds a INNER JOIN table and columns to the query.
528
     *
529
     * @param string $spec The table specification; "foo" or "foo AS bar".
530
     *
531
     * @param string $cond Join on this condition.
532
     *
533
     * @param array $bind Values to bind to ?-placeholders in the condition.
534
     *
535
     * @return $this
536
     *
537
     * @throws Exception
538
     *
539
     */
540 10
    public function innerJoin($spec, $cond = null, array $bind = array())
541
    {
542 10
        return $this->join('INNER', $spec, $cond, $bind);
543
    }
544
545
    /**
546
     *
547
     * Adds a LEFT JOIN table and columns to the query.
548
     *
549
     * @param string $spec The table specification; "foo" or "foo AS bar".
550
     *
551
     * @param string $cond Join on this condition.
552
     *
553
     * @param array $bind Values to bind to ?-placeholders in the condition.
554
     *
555
     * @return $this
556
     *
557
     * @throws Exception
558
     *
559
     */
560 10
    public function leftJoin($spec, $cond = null, array $bind = array())
561
    {
562 10
        return $this->join('LEFT', $spec, $cond, $bind);
563
    }
564
565
    /**
566
     *
567
     * Adds a JOIN to an aliased subselect and columns to the query.
568
     *
569
     * @param string $join The join type: inner, left, natural, etc.
570
     *
571
     * @param string|Select $spec If a Select
572
     * object, use as the sub-select; if a string, the sub-select
573
     * command string.
574
     *
575
     * @param string $name The alias name for the sub-select.
576
     *
577
     * @param string $cond Join on this condition.
578
     *
579
     * @param array $bind Values to bind to ?-placeholders in the condition.
580
     *
581
     * @return $this
582
     *
583
     * @throws Exception
584
     *
585
     */
586 20
    public function joinSubSelect($join, $spec, $name, $cond = null, array $bind = array())
587
    {
588 20
        $join = strtoupper(ltrim("$join JOIN"));
589 20
        $this->addTableRef("$join (SELECT ...) AS", $name);
590
591 15
        $spec = $this->subSelect($spec, '            ');
592 15
        $name = $this->quoter->quoteName($name);
593 15
        $cond = $this->fixJoinCondition($cond, $bind);
594
595 15
        $text = rtrim("$join ($spec        ) AS $name $cond");
596 15
        return $this->addJoin('        ' . $text);
597
    }
598
599
    /**
600
     *
601
     * Adds the JOIN to the right place, given whether or not a FROM has been
602
     * specified yet.
603
     *
604
     * @param string $spec The JOIN clause.
605
     *
606
     * @return $this
607
     *
608
     */
609 55
    protected function addJoin($spec)
610
    {
611 55
        $from_key = ($this->from_key == -1) ? 0 : $this->from_key;
612 55
        $this->join[$from_key][] = $spec;
613 55
        return $this;
614
    }
615
616
    /**
617
     *
618
     * Adds grouping to the query.
619
     *
620
     * @param array $spec The column(s) to group by.
621
     *
622
     * @return $this
623
     *
624
     */
625 5
    public function groupBy(array $spec)
626
    {
627 5
        foreach ($spec as $col) {
628 5
            $this->group_by[] = $this->quoter->quoteNamesIn($col);
629 5
        }
630 5
        return $this;
631
    }
632
633
    /**
634
     *
635
     * Adds a HAVING condition to the query by AND. If the condition has
636
     * ?-placeholders, additional arguments to the method will be bound to
637
     * those placeholders sequentially.
638
     *
639
     * @param string $cond The HAVING condition.
640
     *
641
     * @param array ...$bind arguments to bind to placeholders
642
     *
643
     * @return $this
644
     *
645
     */
646 10
    public function having($cond, ...$bind)
647
    {
648 10
        $this->addClauseCondWithBind('having', 'AND', $cond, $bind);
649 10
        return $this;
650
    }
651
652
    /**
653
     *
654
     * Adds a HAVING condition to the query by AND. If the condition has
655
     * ?-placeholders, additional arguments to the method will be bound to
656
     * those placeholders sequentially.
657
     *
658
     * @param string $cond The HAVING condition.
659
     *
660
     * @param array ...$bind arguments to bind to placeholders
661
     *
662
     * @return $this
663
     *
664
     * @see having()
665
     *
666
     */
667 5
    public function orHaving($cond, ...$bind)
668
    {
669 5
        $this->addClauseCondWithBind('having', 'OR', $cond, $bind);
670 5
        return $this;
671
    }
672
673
    /**
674
     *
675
     * Sets the limit and count by page number.
676
     *
677
     * @param int $page Limit results to this page number.
678
     *
679
     * @return $this
680
     *
681
     */
682 25
    public function page($page)
683
    {
684 25
        $this->page = (int) $page;
685 25
        $this->setPagingLimitOffset();
686 25
        return $this;
687
    }
688
689
    /**
690
     *
691
     * Updates the limit and offset values when changing pagination.
692
     *
693
     * @return null
694
     *
695
     */
696 25
    protected function setPagingLimitOffset()
697
    {
698 25
        $this->limit  = 0;
699 25
        $this->offset = 0;
700 25
        if ($this->page) {
701 10
            $this->limit  = $this->paging;
702 10
            $this->offset = $this->paging * ($this->page - 1);
703 10
        }
704 25
    }
705
706
    /**
707
     *
708
     * Returns the page number being selected.
709
     *
710
     * @return int
711
     *
712
     */
713 5
    public function getPage()
714
    {
715 5
        return $this->page;
716
    }
717
718
    /**
719
     *
720
     * Takes the current select properties and retains them, then sets
721
     * UNION for the next set of properties.
722
     *
723
     * @return $this
724
     *
725
     */
726 10
    public function union()
727
    {
728 10
        $this->union[] = $this->build() . PHP_EOL . 'UNION';
729 10
        $this->reset();
730 10
        return $this;
731
    }
732
733
    /**
734
     *
735
     * Takes the current select properties and retains them, then sets
736
     * UNION ALL for the next set of properties.
737
     *
738
     * @return $this
739
     *
740
     */
741 5
    public function unionAll()
742
    {
743 5
        $this->union[] = $this->build() . PHP_EOL . 'UNION ALL';
744 5
        $this->reset();
745 5
        return $this;
746
    }
747
748
    /**
749
     *
750
     * Returns the LIMIT value.
751
     *
752
     * @return int
753
     *
754
     */
755 10
    public function getLimit()
756
    {
757 10
        return $this->limit;
758
    }
759
760
    /**
761
     *
762
     * Returns the OFFSET value.
763
     *
764
     * @return int
765
     *
766
     */
767 10
    public function getOffset()
768
    {
769 10
        return $this->offset;
770
    }
771
772
    /**
773
     *
774
     * Clears the current select properties; generally used after adding a
775
     * union.
776
     *
777
     * @return null
778
     *
779
     */
780 15
    public function reset()
781
    {
782 15
        $this->resetFlags();
783 15
        $this->resetCols();
784 15
        $this->resetTables();
785 15
        $this->resetWhere();
786 15
        $this->resetGroupBy();
787 15
        $this->resetHaving();
788 15
        $this->resetOrderBy();
789 15
        $this->limit(0);
790 15
        $this->offset(0);
791 15
        $this->page(0);
792 15
        $this->forUpdate(false);
793 15
    }
794
795
    /**
796
     *
797
     * Resets the columns on the SELECT.
798
     *
799
     * @return $this
800
     *
801
     */
802 15
    public function resetCols()
803
    {
804 15
        $this->cols = array();
805 15
        return $this;
806
    }
807
808
    /**
809
     *
810
     * Resets the FROM and JOIN clauses on the SELECT.
811
     *
812
     * @return $this
813
     *
814
     */
815 15
    public function resetTables()
816
    {
817 15
        $this->from = array();
818 15
        $this->from_key = -1;
819 15
        $this->join = array();
820 15
        $this->table_refs = array();
821 15
        return $this;
822
    }
823
824
    /**
825
     *
826
     * Resets the WHERE clause on the SELECT.
827
     *
828
     * @return $this
829
     *
830
     */
831 15
    public function resetWhere()
832
    {
833 15
        $this->where = array();
834 15
        return $this;
835
    }
836
837
    /**
838
     *
839
     * Resets the GROUP BY clause on the SELECT.
840
     *
841
     * @return $this
842
     *
843
     */
844 15
    public function resetGroupBy()
845
    {
846 15
        $this->group_by = array();
847 15
        return $this;
848
    }
849
850
    /**
851
     *
852
     * Resets the HAVING clause on the SELECT.
853
     *
854
     * @return $this
855
     *
856
     */
857 15
    public function resetHaving()
858
    {
859 15
        $this->having = array();
860 15
        return $this;
861
    }
862
863
    /**
864
     *
865
     * Resets the ORDER BY clause on the SELECT.
866
     *
867
     * @return $this
868
     *
869
     */
870 15
    public function resetOrderBy()
871
    {
872 15
        $this->order_by = array();
873 15
        return $this;
874
    }
875
876
    /**
877
     *
878
     * Resets the UNION and UNION ALL clauses on the SELECT.
879
     *
880
     * @return $this
881
     *
882
     */
883
    public function resetUnions()
884
    {
885
        $this->union = array();
886
        return $this;
887
    }
888
889
    /**
890
     *
891
     * Builds this query object into a string.
892
     *
893
     * @return string
894
     *
895
     */
896 184
    protected function build()
897
    {
898
        return 'SELECT'
899 184
            . $this->buildFlags()
900 184
            . $this->buildCols()
901 179
            . $this->buildFrom() // includes JOIN
902 179
            . $this->buildWhere()
903 179
            . $this->buildGroupBy()
904 179
            . $this->buildHaving()
905 179
            . $this->buildOrderBy()
906 179
            . $this->buildLimit()
907 179
            . $this->buildForUpdate();
908
    }
909
910
    /**
911
     *
912
     * Builds the columns clause.
913
     *
914
     * @return string
915
     *
916
     * @throws Exception when there are no columns in the SELECT.
917
     *
918
     */
919 184
    protected function buildCols()
920
    {
921 184
        if (! $this->cols) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->cols of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
922 5
            throw new Exception('No columns in the SELECT.');
923
        }
924
925 179
        $cols = array();
926 179
        foreach ($this->cols as $key => $val) {
927 179
            if (is_int($key)) {
928 179
                $cols[] = $this->quoter->quoteNamesIn($val);
929 179
            } else {
930 10
                $cols[] = $this->quoter->quoteNamesIn("$val AS $key");
931
            }
932 179
        }
933
934 179
        return $this->indentCsv($cols);
935
    }
936
937
    /**
938
     *
939
     * Builds the FROM clause.
940
     *
941
     * @return string
942
     *
943
     */
944 179
    protected function buildFrom()
945
    {
946 179
        if (! $this->from) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->from of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
947 60
            return ''; // not applicable
948
        }
949
950 119
        $refs = array();
951 119
        foreach ($this->from as $from_key => $from) {
952 119
            if (isset($this->join[$from_key])) {
953 55
                $from = array_merge($from, $this->join[$from_key]);
954 55
            }
955 119
            $refs[] = implode(PHP_EOL, $from);
956 119
        }
957 119
        return PHP_EOL . 'FROM' . $this->indentCsv($refs);
958
    }
959
960
    /**
961
     *
962
     * Builds the GROUP BY clause.
963
     *
964
     * @return string
965
     *
966
     */
967 179
    protected function buildGroupBy()
968
    {
969 179
        if (! $this->group_by) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->group_by of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
970 174
            return ''; // not applicable
971
        }
972
973 5
        return PHP_EOL . 'GROUP BY' . $this->indentCsv($this->group_by);
974
    }
975
976
    /**
977
     *
978
     * Builds the HAVING clause.
979
     *
980
     * @return string
981
     *
982
     */
983 179
    protected function buildHaving()
984
    {
985 179
        if (! $this->having) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->having of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
986 164
            return ''; // not applicable
987
        }
988
989 15
        return PHP_EOL . 'HAVING' . $this->indent($this->having);
990
    }
991
992
    /**
993
     *
994
     * Builds the FOR UPDATE clause.
995
     *
996
     * @return string
997
     *
998
     */
999 179
    protected function buildForUpdate()
1000
    {
1001 179
        if (! $this->for_update) {
1002 174
            return ''; // not applicable
1003
        }
1004
1005 5
        return PHP_EOL . 'FOR UPDATE';
1006
    }
1007
1008
    /**
1009
     *
1010
     * Adds a WHERE condition to the query by AND. If the condition has
1011
     * ?-placeholders, additional arguments to the method will be bound to
1012
     * those placeholders sequentially.
1013
     *
1014
     * @param string $cond The WHERE condition.
1015
     * @param mixed ...$bind arguments to bind to placeholders
1016
     *
1017
     * @return $this
1018
     *
1019
     */
1020 30
    public function where($cond, ...$bind)
1021
    {
1022 30
        $this->addWhere('AND', $cond, ...$bind);
1023 30
        return $this;
1024
    }
1025
1026
    /**
1027
     *
1028
     * Adds a WHERE condition to the query by OR. If the condition has
1029
     * ?-placeholders, additional arguments to the method will be bound to
1030
     * those placeholders sequentially.
1031
     *
1032
     * @param string $cond The WHERE condition.
1033
     * @param mixed ...$bind arguments to bind to placeholders
1034
     *
1035
     * @return $this
1036
     *
1037
     * @see where()
1038
     *
1039
     */
1040 5
    public function orWhere($cond, ...$bind)
1041
    {
1042 5
        $this->addWhere('OR', $cond, ...$bind);
1043 5
        return $this;
1044
    }
1045
1046
    /**
1047
     *
1048
     * Sets a limit count on the query.
1049
     *
1050
     * @param int $limit The number of rows to select.
1051
     *
1052
     * @return $this
1053
     *
1054
     */
1055 30
    public function limit($limit)
1056
    {
1057 30
        $this->limit = (int) $limit;
1058 30
        if ($this->page) {
1059 5
            $this->page = 0;
1060 5
            $this->offset = 0;
1061 5
        }
1062 30
        return $this;
1063
    }
1064
1065
    /**
1066
     *
1067
     * Sets a limit offset on the query.
1068
     *
1069
     * @param int $offset Start returning after this many rows.
1070
     *
1071
     * @return $this
1072
     *
1073
     */
1074 30
    public function offset($offset)
1075
    {
1076 30
        $this->offset = (int) $offset;
1077 30
        if ($this->page) {
1078 5
            $this->page = 0;
1079 5
            $this->limit = 0;
1080 5
        }
1081 30
        return $this;
1082
    }
1083
1084
    /**
1085
     *
1086
     * Adds a column order to the query.
1087
     *
1088
     * @param array $spec The columns and direction to order by.
1089
     *
1090
     * @return $this
1091
     *
1092
     */
1093 5
    public function orderBy(array $spec)
1094
    {
1095 5
        return $this->addOrderBy($spec);
1096
    }
1097
}
1098