Completed
Push — 3.x ( bfc500...4ede47 )
by Hari
9s
created

Select::build()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 13
c 0
b 0
f 0
ccs 10
cts 10
cp 1
rs 9.4285
cc 1
eloc 11
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
     * Adds columns to the query.
203
     *
204
     * Multiple calls to cols() will append to the list of columns, not
205
     * overwrite the previous columns.
206
     *
207
     * @param array $cols The column(s) to add to the query. The elements can be
208
     * any mix of these: `array("col", "col AS alias", "col" => "alias")`
209
     *
210
     * @return $this
211
     *
212
     */
213 224
    public function cols(array $cols)
214
    {
215 224
        foreach ($cols as $key => $val) {
216 224
            $this->addCol($key, $val);
217 224
        }
218 224
        return $this;
219
    }
220
221
    /**
222
     *
223
     * Adds a column and alias to the columns to be selected.
224
     *
225
     * @param mixed $key If an integer, ignored. Otherwise, the column to be
226
     * added.
227
     *
228
     * @param mixed $val If $key was an integer, the column to be added;
229
     * otherwise, the column alias.
230
     *
231
     * @return null
232
     *
233
     */
234 224
    protected function addCol($key, $val)
235
    {
236 224
        if (is_string($key)) {
237
            // [col => alias]
238 25
            $this->cols[$val] = $key;
239 25
        } else {
240 214
            $this->addColWithAlias($val);
241
        }
242 224
    }
243
244
    /**
245
     *
246
     * Adds a column with an alias to the columns to be selected.
247
     *
248
     * @param string $spec The column specification: "col alias",
249
     * "col AS alias", or something else entirely.
250
     *
251
     * @return null
252
     *
253
     */
254 214
    protected function addColWithAlias($spec)
255
    {
256 214
        $parts = explode(' ', $spec);
257 214
        $count = count($parts);
258 214
        if ($count == 2) {
259
            // "col alias"
260 5
            $this->cols[$parts[1]] = $parts[0];
261 214
        } elseif ($count == 3 && strtoupper($parts[1]) == 'AS') {
262
            // "col AS alias"
263 5
            $this->cols[$parts[2]] = $parts[0];
264 5
        } else {
265
            // no recognized alias
266 214
            $this->cols[] = $spec;
267
        }
268 214
    }
269
270
    /**
271
     *
272
     * Remove a column via its alias.
273
     *
274
     * @param string $alias The column to remove
275
     *
276
     * @return bool
277
     *
278
     */
279 15
    public function removeCol($alias)
280
    {
281 15
        if (isset($this->cols[$alias])) {
282 5
            unset($this->cols[$alias]);
283
284 5
            return true;
285
        }
286
287 10
        $index = array_search($alias, $this->cols);
288 10
        if ($index !== false) {
289 5
            unset($this->cols[$index]);
290 5
            return true;
291
        }
292
293 5
        return false;
294
    }
295
296
    /**
297
     *
298
     * Does the query have any columns in it?
299
     *
300
     * @return bool
301
     *
302
     */
303 5
    public function hasCols()
304
    {
305 5
        return (bool) $this->cols;
306
    }
307
308
    /**
309
     *
310
     * Returns a list of columns.
311
     *
312
     * @return array
313
     *
314
     */
315 15
    public function getCols()
316
    {
317 15
        return $this->cols;
318
    }
319
320
    /**
321
     *
322
     * Tracks table references.
323
     *
324
     * @param string $type FROM, JOIN, etc.
325
     *
326
     * @param string $spec The table and alias name.
327
     *
328
     * @return null
329
     *
330
     * @throws Exception when the reference has already been used.
331
     *
332
     */
333 149
    protected function addTableRef($type, $spec)
334
    {
335 149
        $name = $spec;
336
337 149
        $pos = strripos($name, ' AS ');
338 149
        if ($pos !== false) {
339 35
            $name = trim(substr($name, $pos + 4));
340 35
        }
341
342 149
        if (isset($this->table_refs[$name])) {
343 25
            $used = $this->table_refs[$name];
344 25
            throw new Exception("Cannot reference '$type $spec' after '$used'");
345
        }
346
347 149
        $this->table_refs[$name] = "$type $spec";
348 149
    }
349
350
    /**
351
     *
352
     * Adds a FROM element to the query; quotes the table name automatically.
353
     *
354
     * @param string $spec The table specification; "foo" or "foo AS bar".
355
     *
356
     * @return $this
357
     *
358
     */
359 139
    public function from($spec)
360
    {
361 139
        $this->addTableRef('FROM', $spec);
362 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...
363
    }
364
365
    /**
366
     *
367
     * Adds a raw unquoted FROM element to the query; useful for adding FROM
368
     * elements that are functions.
369
     *
370
     * @param string $spec The table specification, e.g. "function_name()".
371
     *
372
     * @return $this
373
     *
374
     */
375 5
    public function fromRaw($spec)
376
    {
377 5
        $this->addTableRef('FROM', $spec);
378 5
        return $this->addFrom($spec);
379
    }
380
381
    /**
382
     *
383
     * Adds to the $from property and increments the key count.
384
     *
385
     * @param string $spec The table specification.
386
     *
387
     * @return $this
388
     *
389
     */
390 149
    protected function addFrom($spec)
391
    {
392 149
        $this->from[] = array($spec);
393 149
        $this->from_key ++;
394 149
        return $this;
395
    }
396
397
    /**
398
     *
399
     * Adds an aliased sub-select to the query.
400
     *
401
     * @param string|Select $spec If a Select object, use as the sub-select;
402
     * if a string, the sub-select string.
403
     *
404
     * @param string $name The alias name for the sub-select.
405
     *
406
     * @return $this
407
     *
408
     */
409 15
    public function fromSubSelect($spec, $name)
410
    {
411 15
        $this->addTableRef('FROM (SELECT ...) AS', $name);
412 10
        $spec = $this->subSelect($spec, '        ');
413 10
        $name = $this->quoter->quoteName($name);
414 10
        return $this->addFrom("({$spec}    ) AS $name");
415
    }
416
417
    /**
418
     *
419
     * Formats a sub-SELECT statement, binding values from a Select object as
420
     * needed.
421
     *
422
     * @param string|SelectInterface $spec A sub-SELECT specification.
423
     *
424
     * @param string $indent Indent each line with this string.
425
     *
426
     * @return string The sub-SELECT string.
427
     *
428
     */
429 25
    protected function subSelect($spec, $indent)
430
    {
431 25
        if ($spec instanceof SelectInterface) {
432 10
            $this->bindValues($spec->getBindValues());
433 10
        }
434
435 25
        return PHP_EOL . $indent
436 25
            . ltrim(preg_replace('/^/m', $indent, (string) $spec))
437 25
            . PHP_EOL;
438
    }
439
440
    /**
441
     *
442
     * Adds a JOIN table and columns to the query.
443
     *
444
     * @param string $join The join type: inner, left, natural, etc.
445
     *
446
     * @param string $spec The table specification; "foo" or "foo AS bar".
447
     *
448
     * @param string $cond Join on this condition.
449
     *
450
     * @param array $bind Values to bind to ?-placeholders in the condition.
451
     *
452
     * @return $this
453
     *
454
     * @throws Exception
455
     *
456
     */
457 45
    public function join($join, $spec, $cond = null, array $bind = array())
458
    {
459 45
        $join = strtoupper(ltrim("$join JOIN"));
460 45
        $this->addTableRef($join, $spec);
461
462 40
        $spec = $this->quoter->quoteName($spec);
463 40
        $cond = $this->fixJoinCondition($cond, $bind);
464 40
        return $this->addJoin(rtrim("$join $spec $cond"));
465
    }
466
467
    /**
468
     *
469
     * Fixes a JOIN condition to quote names in the condition and prefix it
470
     * with a condition type ('ON' is the default and 'USING' is recognized).
471
     *
472
     * @param string $cond Join on this condition.
473
     *
474
     * @param array $bind Values to bind to ?-placeholders in the condition.
475
     *
476
     * @return string
477
     *
478
     */
479 55
    protected function fixJoinCondition($cond, array $bind)
480
    {
481 55
        if (! $cond) {
482 25
            return '';
483
        }
484
485 55
        $cond = $this->quoter->quoteNamesIn($cond);
486 55
        $cond = $this->rebuildCondAndBindValues($cond, $bind);
487
488 55
        if (strtoupper(substr(ltrim($cond), 0, 3)) == 'ON ') {
489 5
            return $cond;
490
        }
491
492 55
        if (strtoupper(substr(ltrim($cond), 0, 6)) == 'USING ') {
493 5
            return $cond;
494
        }
495
496 50
        return 'ON ' . $cond;
497
    }
498
499
    /**
500
     *
501
     * Adds a INNER JOIN table and columns to the query.
502
     *
503
     * @param string $spec The table specification; "foo" or "foo AS bar".
504
     *
505
     * @param string $cond Join on this condition.
506
     *
507
     * @param array $bind Values to bind to ?-placeholders in the condition.
508
     *
509
     * @return $this
510
     *
511
     * @throws Exception
512
     *
513
     */
514 10
    public function innerJoin($spec, $cond = null, array $bind = array())
515
    {
516 10
        return $this->join('INNER', $spec, $cond, $bind);
517
    }
518
519
    /**
520
     *
521
     * Adds a LEFT JOIN table and columns to the query.
522
     *
523
     * @param string $spec The table specification; "foo" or "foo AS bar".
524
     *
525
     * @param string $cond Join on this condition.
526
     *
527
     * @param array $bind Values to bind to ?-placeholders in the condition.
528
     *
529
     * @return $this
530
     *
531
     * @throws Exception
532
     *
533
     */
534 10
    public function leftJoin($spec, $cond = null, array $bind = array())
535
    {
536 10
        return $this->join('LEFT', $spec, $cond, $bind);
537
    }
538
539
    /**
540
     *
541
     * Adds a JOIN to an aliased subselect and columns to the query.
542
     *
543
     * @param string $join The join type: inner, left, natural, etc.
544
     *
545
     * @param string|Select $spec If a Select
546
     * object, use as the sub-select; if a string, the sub-select
547
     * command string.
548
     *
549
     * @param string $name The alias name for the sub-select.
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 20
    public function joinSubSelect($join, $spec, $name, $cond = null, array $bind = array())
561
    {
562 20
        $join = strtoupper(ltrim("$join JOIN"));
563 20
        $this->addTableRef("$join (SELECT ...) AS", $name);
564
565 15
        $spec = $this->subSelect($spec, '            ');
566 15
        $name = $this->quoter->quoteName($name);
567 15
        $cond = $this->fixJoinCondition($cond, $bind);
568
569 15
        $text = rtrim("$join ($spec        ) AS $name $cond");
570 15
        return $this->addJoin('        ' . $text);
571
    }
572
573
    /**
574
     *
575
     * Adds the JOIN to the right place, given whether or not a FROM has been
576
     * specified yet.
577
     *
578
     * @param string $spec The JOIN clause.
579
     *
580
     * @return $this
581
     *
582
     */
583 55
    protected function addJoin($spec)
584
    {
585 55
        $from_key = ($this->from_key == -1) ? 0 : $this->from_key;
586 55
        $this->join[$from_key][] = $spec;
587 55
        return $this;
588
    }
589
590
    /**
591
     *
592
     * Adds grouping to the query.
593
     *
594
     * @param array $spec The column(s) to group by.
595
     *
596
     * @return $this
597
     *
598
     */
599 5
    public function groupBy(array $spec)
600
    {
601 5
        foreach ($spec as $col) {
602 5
            $this->group_by[] = $this->quoter->quoteNamesIn($col);
603 5
        }
604 5
        return $this;
605
    }
606
607
    /**
608
     *
609
     * Adds a HAVING condition to the query by AND. If the condition has
610
     * ?-placeholders, additional arguments to the method will be bound to
611
     * those placeholders sequentially.
612
     *
613
     * @param string $cond The HAVING condition.
614
     *
615
     * @param array ...$bind arguments to bind to placeholders
616
     *
617
     * @return $this
618
     *
619
     */
620 10
    public function having($cond, ...$bind)
621
    {
622 10
        $this->addClauseCondWithBind('having', 'AND', $cond, $bind);
623 10
        return $this;
624
    }
625
626
    /**
627
     *
628
     * Adds a HAVING condition to the query by AND. If the condition has
629
     * ?-placeholders, additional arguments to the method will be bound to
630
     * those placeholders sequentially.
631
     *
632
     * @param string $cond The HAVING condition.
633
     *
634
     * @param array ...$bind arguments to bind to placeholders
635
     *
636
     * @return $this
637
     *
638
     * @see having()
639
     *
640
     */
641 5
    public function orHaving($cond, ...$bind)
642
    {
643 5
        $this->addClauseCondWithBind('having', 'OR', $cond, $bind);
644 5
        return $this;
645
    }
646
647
    /**
648
     *
649
     * Sets the limit and count by page number.
650
     *
651
     * @param int $page Limit results to this page number.
652
     *
653
     * @return $this
654
     *
655
     */
656 25
    public function page($page)
657
    {
658 25
        $this->page = (int) $page;
659 25
        $this->setPagingLimitOffset();
660 25
        return $this;
661
    }
662
663
    /**
664
     *
665
     * Updates the limit and offset values when changing pagination.
666
     *
667
     * @return null
668
     *
669
     */
670 25
    protected function setPagingLimitOffset()
671
    {
672 25
        $this->limit  = 0;
673 25
        $this->offset = 0;
674 25
        if ($this->page) {
675 10
            $this->limit  = $this->paging;
676 10
            $this->offset = $this->paging * ($this->page - 1);
677 10
        }
678 25
    }
679
680
    /**
681
     *
682
     * Returns the page number being selected.
683
     *
684
     * @return int
685
     *
686
     */
687 5
    public function getPage()
688
    {
689 5
        return $this->page;
690
    }
691
692
    /**
693
     *
694
     * Takes the current select properties and retains them, then sets
695
     * UNION for the next set of properties.
696
     *
697
     * @return $this
698
     *
699
     */
700 10
    public function union()
701
    {
702 10
        $this->union[] = $this->build() . PHP_EOL . 'UNION';
703 10
        $this->reset();
704 10
        return $this;
705
    }
706
707
    /**
708
     *
709
     * Takes the current select properties and retains them, then sets
710
     * UNION ALL for the next set of properties.
711
     *
712
     * @return $this
713
     *
714
     */
715 5
    public function unionAll()
716
    {
717 5
        $this->union[] = $this->build() . PHP_EOL . 'UNION ALL';
718 5
        $this->reset();
719 5
        return $this;
720
    }
721
722
    /**
723
     *
724
     * Returns the LIMIT value.
725
     *
726
     * @return int
727
     *
728
     */
729 10
    public function getLimit()
730
    {
731 10
        return $this->limit;
732
    }
733
734
    /**
735
     *
736
     * Returns the OFFSET value.
737
     *
738
     * @return int
739
     *
740
     */
741 10
    public function getOffset()
742
    {
743 10
        return $this->offset;
744
    }
745
746
    /**
747
     *
748
     * Clears the current select properties; generally used after adding a
749
     * union.
750
     *
751
     * @return null
752
     *
753
     */
754 15
    protected function reset()
755
    {
756 15
        $this->resetFlags();
757 15
        $this->resetCols();
758 15
        $this->resetTables();
759 15
        $this->resetWhere();
760 15
        $this->resetGroupBy();
761 15
        $this->resetHaving();
762 15
        $this->resetOrderBy();
763 15
        $this->limit(0);
764 15
        $this->offset(0);
765 15
        $this->page(0);
766 15
        $this->forUpdate(false);
767 15
    }
768
769
    /**
770
     *
771
     * Resets the columns on the SELECT.
772
     *
773
     * @return $this
774
     *
775
     */
776 15
    public function resetCols()
777
    {
778 15
        $this->cols = array();
779 15
        return $this;
780
    }
781
782
    /**
783
     *
784
     * Resets the FROM and JOIN clauses on the SELECT.
785
     *
786
     * @return $this
787
     *
788
     */
789 15
    public function resetTables()
790
    {
791 15
        $this->from = array();
792 15
        $this->from_key = -1;
793 15
        $this->join = array();
794 15
        $this->table_refs = array();
795 15
        return $this;
796
    }
797
798
    /**
799
     *
800
     * Resets the WHERE clause on the SELECT.
801
     *
802
     * @return $this
803
     *
804
     */
805 15
    public function resetWhere()
806
    {
807 15
        $this->where = array();
808 15
        return $this;
809
    }
810
811
    /**
812
     *
813
     * Resets the GROUP BY clause on the SELECT.
814
     *
815
     * @return $this
816
     *
817
     */
818 15
    public function resetGroupBy()
819
    {
820 15
        $this->group_by = array();
821 15
        return $this;
822
    }
823
824
    /**
825
     *
826
     * Resets the HAVING clause on the SELECT.
827
     *
828
     * @return $this
829
     *
830
     */
831 15
    public function resetHaving()
832
    {
833 15
        $this->having = array();
834 15
        return $this;
835
    }
836
837
    /**
838
     *
839
     * Resets the ORDER BY clause on the SELECT.
840
     *
841
     * @return $this
842
     *
843
     */
844 15
    public function resetOrderBy()
845
    {
846 15
        $this->order_by = array();
847 15
        return $this;
848
    }
849
850
    /**
851
     *
852
     * Resets the UNION and UNION ALL clauses on the SELECT.
853
     *
854
     * @return $this
855
     *
856
     */
857
    public function resetUnions()
858
    {
859
        $this->union = array();
860
        return $this;
861
    }
862
863
    /**
864
     *
865
     * Builds this query object into a string.
866
     *
867
     * @return string
868
     *
869
     */
870 184
    protected function build()
871
    {
872
        return 'SELECT'
873 184
            . $this->buildFlags()
874 184
            . $this->buildCols()
875 179
            . $this->buildFrom() // includes JOIN
876 179
            . $this->buildWhere()
877 179
            . $this->buildGroupBy()
878 179
            . $this->buildHaving()
879 179
            . $this->buildOrderBy()
880 179
            . $this->buildLimit()
881 179
            . $this->buildForUpdate();
882
    }
883
884
    /**
885
     *
886
     * Builds the columns clause.
887
     *
888
     * @return string
889
     *
890
     * @throws Exception when there are no columns in the SELECT.
891
     *
892
     */
893 184
    protected function buildCols()
894
    {
895 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...
896 5
            throw new Exception('No columns in the SELECT.');
897
        }
898
899 179
        $cols = array();
900 179
        foreach ($this->cols as $key => $val) {
901 179
            if (is_int($key)) {
902 179
                $cols[] = $this->quoter->quoteNamesIn($val);
903 179
            } else {
904 10
                $cols[] = $this->quoter->quoteNamesIn("$val AS $key");
905
            }
906 179
        }
907
908 179
        return $this->indentCsv($cols);
909
    }
910
911
    /**
912
     *
913
     * Builds the FROM clause.
914
     *
915
     * @return string
916
     *
917
     */
918 179
    protected function buildFrom()
919
    {
920 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...
921 60
            return ''; // not applicable
922
        }
923
924 119
        $refs = array();
925 119
        foreach ($this->from as $from_key => $from) {
926 119
            if (isset($this->join[$from_key])) {
927 55
                $from = array_merge($from, $this->join[$from_key]);
928 55
            }
929 119
            $refs[] = implode(PHP_EOL, $from);
930 119
        }
931 119
        return PHP_EOL . 'FROM' . $this->indentCsv($refs);
932
    }
933
934
    /**
935
     *
936
     * Builds the GROUP BY clause.
937
     *
938
     * @return string
939
     *
940
     */
941 179
    protected function buildGroupBy()
942
    {
943 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...
944 174
            return ''; // not applicable
945
        }
946
947 5
        return PHP_EOL . 'GROUP BY' . $this->indentCsv($this->group_by);
948
    }
949
950
    /**
951
     *
952
     * Builds the HAVING clause.
953
     *
954
     * @return string
955
     *
956
     */
957 179
    protected function buildHaving()
958
    {
959 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...
960 164
            return ''; // not applicable
961
        }
962
963 15
        return PHP_EOL . 'HAVING' . $this->indent($this->having);
964
    }
965
966
    /**
967
     *
968
     * Builds the FOR UPDATE clause.
969
     *
970
     * @return string
971
     *
972
     */
973 179
    protected function buildForUpdate()
974
    {
975 179
        if (! $this->for_update) {
976 174
            return ''; // not applicable
977
        }
978
979 5
        return PHP_EOL . 'FOR UPDATE';
980
    }
981
982
    /**
983
     *
984
     * Adds a WHERE condition to the query by AND. If the condition has
985
     * ?-placeholders, additional arguments to the method will be bound to
986
     * those placeholders sequentially.
987
     *
988
     * @param string $cond The WHERE condition.
989
     * @param mixed ...$bind arguments to bind to placeholders
990
     *
991
     * @return $this
992
     *
993
     */
994 30
    public function where($cond, ...$bind)
995
    {
996 30
        $this->addWhere('AND', $cond, ...$bind);
997 30
        return $this;
998
    }
999
1000
    /**
1001
     *
1002
     * Adds a WHERE condition to the query by OR. If the condition has
1003
     * ?-placeholders, additional arguments to the method will be bound to
1004
     * those placeholders sequentially.
1005
     *
1006
     * @param string $cond The WHERE condition.
1007
     * @param mixed ...$bind arguments to bind to placeholders
1008
     *
1009
     * @return $this
1010
     *
1011
     * @see where()
1012
     *
1013
     */
1014 5
    public function orWhere($cond, ...$bind)
1015
    {
1016 5
        $this->addWhere('OR', $cond, ...$bind);
1017 5
        return $this;
1018
    }
1019
1020
    /**
1021
     *
1022
     * Sets a limit count on the query.
1023
     *
1024
     * @param int $limit The number of rows to select.
1025
     *
1026
     * @return $this
1027
     *
1028
     */
1029 30
    public function limit($limit)
1030
    {
1031 30
        $this->limit = (int) $limit;
1032 30
        if ($this->page) {
1033 5
            $this->page = 0;
1034 5
            $this->offset = 0;
1035 5
        }
1036 30
        return $this;
1037
    }
1038
1039
    /**
1040
     *
1041
     * Sets a limit offset on the query.
1042
     *
1043
     * @param int $offset Start returning after this many rows.
1044
     *
1045
     * @return $this
1046
     *
1047
     */
1048 30
    public function offset($offset)
1049
    {
1050 30
        $this->offset = (int) $offset;
1051 30
        if ($this->page) {
1052 5
            $this->page = 0;
1053 5
            $this->limit = 0;
1054 5
        }
1055 30
        return $this;
1056
    }
1057
1058
    /**
1059
     *
1060
     * Adds a column order to the query.
1061
     *
1062
     * @param array $spec The columns and direction to order by.
1063
     *
1064
     * @return $this
1065
     *
1066
     */
1067 5
    public function orderBy(array $spec)
1068
    {
1069 5
        return $this->addOrderBy($spec);
1070
    }
1071
}
1072