Completed
Pull Request — 3.x (#110)
by
unknown
02:29 queued 01:01
created

Select::buildFrom()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4

Importance

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