Completed
Push — master ( c26f0f...c7af1e )
by Konstantinos
18:52
created

QueryBuilder::isOneOf()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 12
ccs 6
cts 6
cp 1
rs 9.4285
cc 1
eloc 6
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * This file contains a class to quickly generate database queries for models
4
 *
5
 * @package    BZiON\Models\QueryBuilder
6
 * @license    https://github.com/allejo/bzion/blob/master/LICENSE.md GNU General Public License Version 3
7
 */
8
9
/**
10
 * This class can be used to search for models with specific characteristics in
11
 * the database.
12
 *
13
 * Note that most methods of this class return itself, so that you can easily
14
 * add a number of different filters.
15
 *
16
 * <code>
17
 *     return Team::getQueryBuilder()
18
 *     ->active()
19
 *     ->where('name')->startsWith('a')
20
 *     ->sortBy('name')->reverse()
21
 *     ->getModels();
22
 * </code>
23
 *
24
 * @package    BZiON\Models\QueryBuilder
25
 */
26
class QueryBuilder implements Countable
27
{
28
    /**
29
     * The type of the model we're building a query for
30
     * @var string
31
     */
32
    protected $type;
33
34
    /**
35
     * The columns that the model provided us
36
     * @var array
37
     */
38
    protected $columns = array('id' => 'id');
39
40
    /**
41
     * The conditions to include in WHERE
42
     * @var string[]
43
     */
44
    protected $conditions = array();
45
46
    /**
47
     * The MySQL value parameters
48
     * @var array
49
     */
50
    protected $parameters = array();
51
52
    /**
53
     * The MySQL value parameters for pagination
54
     * @var array
55
     */
56
    protected $paginationParameters = array();
57
58
    /**
59
     * Extra MySQL query string to pass
60
     * @var string
61
     */
62
    protected $extras = '';
63
64
    /**
65
     * Extra MySQL query groupby string to pass
66
     * @var string
67
     */
68
    protected $groupQuery = '';
69
70
    /**
71
     * A column based on which we should sort the results
72
     * @var string|null
73
     */
74
    private $sortBy = null;
75
76
    /**
77
     * Whether to reverse the results
78
     * @var bool
79
     */
80
    private $reverseSort = false;
81
82
    /**
83
     * The currently selected column
84
     * @var string|null
85
     */
86
    private $currentColumn = null;
87
88
    /**
89
     * The currently selected column without the table name (unless it was
90
     * explicitly provided)
91
     * @var string|null
92
     */
93
    protected $currentColumnRaw = null;
94
95
    /**
96
     * A column to consider the name of the model
97
     * @var string|null
98
     */
99
    private $nameColumn = null;
100
101
    /**
102
     * The page to return
103
     * @var int|null
104
     */
105
    private $page = null;
106
107
    /**
108
     * Whether the ID of the first/last element has been provided
109
     * @var bool
110
     */
111
    private $limited = false;
112
113
    /**
114
     * The number of elements on every page
115
     * @var int
116
     */
117
    protected $resultsPerPage = 30;
118
119
    /**
120
     * Create a new QueryBuilder
121
     *
122
     * A new query builder should be created on a static getQueryBuilder()
123
     * method on each model. The options array can contain the following
124
     * properties:
125
     *
126
     * - `columns`: An associative array - the key of each entry is the column
127
     *   name that will be used by other methods, while the value is
128
     *   is the column name that is used in the database structure
129
     *
130
     * - `activeStatuses`: If the model has a status column, this should be
131
     *                     a list of values that make the entry be considered
132
     *                     "active"
133
     *
134
     * - `name`: The name of the column which represents the name of the object
135
     *
136
     * @param string $type    The type of the Model (e.g. "Player" or "Match")
137
     * @param array  $options The options to pass to the builder (see above)
138
     */
139 3
    public function __construct($type, $options = array())
140
    {
141 3
        $this->type = $type;
142
143 3
        if (isset($options['columns'])) {
144 3
            $this->columns += $options['columns'];
145 3
        }
146
147 3
        if (isset($options['name'])) {
148 1
            $this->nameColumn = $options['name'];
149 1
        }
150 3
    }
151
152
    /**
153
     * Select a column
154
     *
155
     * `$queryBuilder->where('username')->equals('administrator');`
156
     *
157
     * @param  string $column The column to select
158
     * @return self
159
     */
160 3
    public function where($column)
161
    {
162 3
        if (!isset($this->columns[$column])) {
163
            throw new InvalidArgumentException("Unknown column '$column'");
164
        }
165
166 3
        $this->column($this->columns[$column]);
167
168 3
        return $this;
169
    }
170
171
    /**
172
     * Request that a column equals a string (case-insensitive)
173
     *
174
     * @param  string $string The string that the column's value should equal to
175
     * @return self
176
     */
177 1
    public function equals($string)
178
    {
179 1
        $this->addColumnCondition("= ?", $string);
180
181 1
        return $this;
182
    }
183
184
    /**
185
     * Request that a column doesNOT equals a string (case-insensitive)
186
     *
187
     * @param  string $string The string that the column's value should equal to
188
     * @return self
189
     */
190
    public function notEquals($string)
191
    {
192
        $this->addColumnCondition("!= ?", $string);
193
194
        return $this;
195
    }
196
197
    /**
198
     * Request that a column is greater than a quantity
199
     *
200
     * @param  string $quantity The quantity to test against
201
     * @return self
202
     */
203
    public function greaterThan($quantity)
204
    {
205
        $this->addColumnCondition("> ?", $quantity);
206
207
        return $this;
208
    }
209
210
    /**
211
     * Request that a column is less than a quantity
212
     *
213
     * @param  string $quantity The quantity to test against
214
     * @return self
215
     */
216
    public function lessThan($quantity)
217
    {
218
        $this->addColumnCondition("< ?", $quantity);
219
220
        return $this;
221
    }
222
223
    /**
224
     * Request that a timestamp is before the specified time
225
     *
226
     * @param string|TimeDate $time      The timestamp to compare to
227
     * @param bool            $inclusive Whether to include the given timestamp
228
     * @param bool            $reverse   Whether to reverse the results
229
     *
230
     * @return self
231
     */
232
    public function isBefore($time, $inclusive = false, $reverse = false)
233
    {
234
        return $this->isAfter($time, $inclusive, !$reverse);
235
    }
236
237
    /**
238
     * Request that a timestamp is after the specified time
239
     *
240
     * @param string|TimeDate $time      The timestamp to compare to
241
     * @param bool            $inclusive Whether to include the given timestamp
242
     * @param bool            $reverse   Whether to reverse the results
243
     *
244
     * @return self
245
     */
246
    public function isAfter($time, $inclusive = false, $reverse = false)
247
    {
248
        if ($time instanceof TimeDate) {
249
            $time = $time->toMysql();
250
        }
251
252
        $comparison  = ($reverse)   ? '<' : '>';
253
        $comparison .= ($inclusive) ? '=' : '';
254
255
        $this->addColumnCondition("$comparison ?",  $time);
256
257
        return $this;
258
    }
259
260
    /**
261
     * Request that a column equals a number
262
     *
263
     * @param  int|Model|null $number The number that the column's value should
264
     *                                equal to. If a Model is provided, use the
265
     *                                model's ID, while null values are ignored.
266
     * @return self
267
     */
268 View Code Duplication
    public function is($number)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
269
    {
270
        if ($number === null) {
271
            return $this;
272
        }
273
274
        if ($number instanceof Model) {
275
            $number = $number->getId();
276
        }
277
278
        $this->addColumnCondition("= ?", $number);
279
280
        return $this;
281
    }
282
283
    /**
284
     * Request that a column equals one of some strings
285
     *
286
     * @todo   Improve for PDO
287
     * @param  string[] $strings The list of accepted values for the column
288
     * @return self
289
     */
290 2
    public function isOneOf($strings)
291
    {
292 2
        $count = count($strings);
293 2
        $questionMarks = str_repeat(',?', $count);
294
295
        // Remove first comma from questionMarks so that MySQL can read our query
296 2
        $questionMarks = ltrim($questionMarks, ',');
297
298 2
        $this->addColumnCondition("IN ($questionMarks)", $strings);
299
300 2
        return $this;
301
    }
302
303
    /**
304
     * Request that a column value starts with a string (case-insensitive)
305
     *
306
     * @param  string $string The substring that the column's value should start with
307
     * @return self
308
     */
309
    public function startsWith($string)
310
    {
311
        $this->addColumnCondition("LIKE CONCAT(?, '%')", $string);
312
313
        return $this;
314
    }
315
316
    /**
317
     * Request that a specific model is not returned
318
     *
319
     * @param  Model|int $model The ID or model you don't want to get
320
     * @return self
321
     */
322 View Code Duplication
    public function except($model)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
323
    {
324
        if ($model instanceof Model) {
325
            $model = $model->getId();
326
        }
327
328
        $this->where('id');
329
        $this->addColumnCondition("!= ?", $model);
330
331
        return $this;
332
    }
333
334
    /**
335
     * Return the results sorted by the value of a column
336
     *
337
     * @param  string $column The column based on which the results should be ordered
338
     * @return self
339
     */
340 1
    public function sortBy($column)
341
    {
342 1
        if (!isset($this->columns[$column])) {
343
            throw new Exception("Unknown column");
344
        }
345
346 1
        $this->sortBy = $this->columns[$column];
347
348 1
        return $this;
349
    }
350
351
    /**
352
     * Reverse the order
353
     *
354
     * Note: This only works if you have specified a column in the sortBy() method
355
     *
356
     * @return self
357
     */
358 1
    public function reverse()
359
    {
360 1
        $this->reverseSort = !$this->reverseSort;
361
362 1
        return $this;
363
    }
364
365
    /**
366
     * Specify the number of results per page
367
     *
368
     * @param  int  $count The number of results
369
     * @return self
370
     */
371 1
    public function limit($count)
372
    {
373 1
        $this->resultsPerPage = $count;
374
375 1
        return $this;
376
    }
377
378
    /**
379
     * Only show results from a specific page
380
     *
381
     * @param  int|null $page The page number (or null to show all pages - counting starts from 0)
382
     * @return self
383
     */
384 1
    public function fromPage($page)
385
    {
386 1
        $this->page = $page;
387
388 1
        return $this;
389
    }
390
391
    /**
392
     * End with a specific result
393
     *
394
     * @param  int|Model $model     The model (or database ID) after the first result
395
     * @param  bool   $inclusive Whether to include the provided model
396
     * @param  bool   $reverse   Whether to reverse the results
397
     * @return self
398
     */
399
    public function endAt($model, $inclusive = false, $reverse = false)
400
    {
401
        return $this->startAt($model, $inclusive, !$reverse);
402
    }
403
404
    /**
405
     * Start with a specific result
406
     *
407
     * @param  int|Model $model     The model (or database ID) before the first result
408
     * @param  bool   $inclusive Whether to include the provided model
409
     * @param  bool   $reverse   Whether to reverse the results
410
     * @return self
411
     */
412
    public function startAt($model, $inclusive = false, $reverse = false)
413
    {
414
        if (!$model) {
415
            return $this;
416
        } elseif ($model instanceof Model && !$model->isValid()) {
417
            return $this;
418
        }
419
420
        $this->column($this->sortBy);
421
        $this->limited = true;
422
        $column = $this->currentColumn;
423
        $table  = $this->getTable();
424
425
        $comparison  = $this->reverseSort ^ $reverse;
426
        $comparison  = ($comparison) ? '>' : '<';
427
        $comparison .= ($inclusive)  ? '=' : '';
428
        $id = ($model instanceof Model) ? $model->getId() : $model;
429
430
        // Compare an element's timestamp to the timestamp of $model; if it's the
431
        // same, perform the comparison using IDs
432
        $this->addColumnCondition(
433
            "$comparison (SELECT $column FROM $table WHERE id = ?) OR ($column = (SELECT $column FROM $table WHERE id = ?) AND id $comparison ?)",
434
            array($id, $id, $id)
435
        );
436
437
        return $this;
438
    }
439
440
    /**
441
     * Request that only "active" Models should be returned
442
     *
443
     * @return self
444
     */
445 2
    public function active()
446
    {
447 2
        if (!isset($this->columns['status'])) {
448
            return $this;
449
        }
450
451 2
        $type = $this->type;
452
453 2
        return $this->where('status')->isOneOf($type::getActiveStatuses());
454
    }
455
456
    /**
457
     * Make sure that Models invisible to a player are not returned
458
     *
459
     * Note that this method does not take PermissionModel::canBeSeenBy() into
460
     * consideration for performance purposes, so you will have to override this
461
     * in your query builder if necessary.
462
     *
463
     * @param  Player  $player      The player in question
464
     * @param  bool $showDeleted false to hide deleted models even from admins
465
     * @return self
466
     */
467
    public function visibleTo($player, $showDeleted = false)
468
    {
469
        $type = $this->type;
470
471
        if (is_subclass_of($type, "PermissionModel")
472
         && $player->hasPermission($type::EDIT_PERMISSION)) {
473
            // The player is an admin who can see hidden models
474
            if (!$showDeleted) {
475
                if (isset($this->columns['status'])) {
476
                    return $this->where('status')->notEquals('deleted');
477
                }
478
            }
479
        } else {
480
            return $this->active();
481
        }
482
483
        return $this;
484
    }
485
486
    /**
487
     * Perform the query and get back the results in an array of names
488
     *
489
     * @return string[] An array of the type $id => $name
490
     */
491
    public function getNames()
492
    {
493
        if (!$this->nameColumn) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->nameColumn of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
494
            throw new Exception("You haven't specified a name column");
495
        }
496
497
        $results = $this->getArray($this->nameColumn);
498
499
        return array_column($results, $this->nameColumn, 'id');
500
    }
501
502
    /**
503
     * Perform the query and get back the results in a list of arrays
504
     *
505
     * @todo   Play with partial models?
506
     * @param  string|string[] $columns The column(s) that should be returned
507
     * @return array[]
508
     */
509
    public function getArray($columns)
510
    {
511
        if (!is_array($columns)) {
512
            $columns = array($columns);
513
        }
514
515
        $db = Database::getInstance();
516
517
        return $db->query($this->createQuery($columns), $this->getParameters());
518
    }
519
520
    /**
521
     * An alias for QueryBuilder::getModels(), with fast fetching on by default
522
     * and no return of results
523
     *
524
     * @param  bool $fastFetch Whether to perform one query to load all
525
     *                            the model data instead of fetching them
526
     *                            one by one
527
     * @return void
528
     */
529
    public function addToCache($fastFetch = true)
530
    {
531
        $this->getModels($fastFetch);
532
    }
533
534
    /**
535
     * Perform the query and get the results as Models
536
     *
537
     * @todo Fix fast fetch for queries with multiple tables
538
     * @param  bool $fastFetch Whether to perform one query to load all
539
     *                            the model data instead of fetching them
540
     *                            one by one (ignores cache)
541
     * @return array
542
     */
543 3
    public function getModels($fastFetch = false)
544
    {
545 3
        $db   = Database::getInstance();
546 3
        $type = $this->type;
547
548 3
        $columns = ($fastFetch) ? $type::getEagerColumns() : array();
549
550 3
        $results = $db->query($this->createQuery($columns), $this->getParameters());
551
552 3
        if ($fastFetch) {
553 1
            return $type::createFromDatabaseResults($results);
554
        } else {
555 2
            return $type::arrayIdToModel(array_column($results, 'id'));
556
        }
557
    }
558
559
    /**
560
     * Count the results
561
     *
562
     * @return int
563
     */
564
    public function count()
565
    {
566
        $table  = $this->getTable();
567
        $params = $this->createQueryParams(false);
568
        $db     = Database::getInstance();
569
        $query  = "SELECT COUNT(*) FROM $table $params";
570
571
        // We don't want pagination to affect our results so don't use the functions that combine
572
        // pagination results
573
        $results = $db->query($query, $this->parameters);
574
575
        return $results[0]['COUNT(*)'];
576
    }
577
578
    /**
579
     * Count the number of pages that all the models could be separated into
580
     */
581
    public function countPages()
582
    {
583
        return ceil($this->count() / $this->getResultsPerPage());
584
    }
585
586
    /**
587
     * Find if there is any result
588
     *
589
     * @return bool
590
     */
591
    public function any()
592
    {
593
        // Make sure that we don't mess with the user's options
594
        $query = clone $this;
595
596
        $query->limit(1);
597
598
        return $query->count() > 0;
599
    }
600
601
    /**
602
     * Get the amount of results that are returned per page
603
     * @return int
604
     */
605
    public function getResultsPerPage()
606
    {
607
        return $this->resultsPerPage;
608
    }
609
610
    /**
611
     * Select a column to perform opeations on
612
     *
613
     * This is identical to the `where()` method, except that the column is
614
     * specified as a MySQL column and not as a column name given by the model
615
     *
616
     * @param  string $column The column to select
617
     * @return self
618
     */
619 3
    protected function column($column)
620
    {
621 3
        if (strpos($column, '.') === false) {
622
            // Add the table name to the column if it isn't there already so that
623
            // MySQL knows what to do when handling multiple tables
624 3
            $table = $this->getTable();
625 3
            $this->currentColumn = "`$table`.`$column`";
626 3
        } else {
627
            $this->currentColumn = $column;
628
        }
629
630 3
        $this->currentColumnRaw = $column;
631
632 3
        return $this;
633
    }
634
635
    /**
636
     * Add a condition for the column
637
     * @param  string $condition The MySQL condition
638
     * @param  mixed  $value     Value(s) to pass to MySQL
639
     * @return void
640
     */
641 3
    protected function addColumnCondition($condition, $value)
642
    {
643 3
        if (!$this->currentColumn) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->currentColumn of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
644
            throw new Exception("You haven't selected a column!");
645
        }
646
647 3
        if (!is_array($value)) {
648 1
            $value = array($value);
649 1
        }
650
651 3
        $this->conditions[] = "{$this->currentColumn} $condition";
652 3
        $this->parameters   = array_merge($this->parameters, $value);
653
654 3
        $this->currentColumn = null;
655 3
        $this->currentColumnRaw = null;
656 3
    }
657
658
    /**
659
     * Get the MySQL extra parameters
660
     *
661
     * @param  bool $respectPagination Whether to respect pagination or not; useful for when pagination should be ignored such as count
662
     * @return string
663
     */
664 3
    protected function createQueryParams($respectPagination = true)
665
    {
666 3
        $extras     = $this->extras;
667 3
        $conditions = $this->createQueryConditions();
668 3
        $groupQuery = $this->groupQuery;
669 3
        $order      = $this->createQueryOrder();
670 3
        $pagination = "";
671
672 3
        if ($respectPagination) {
673 3
            $pagination = $this->createQueryPagination();
674 3
        }
675
676 3
        return "$extras $conditions $groupQuery $order $pagination";
677
    }
678
679
    /**
680
     * Get the query parameters
681
     *
682
     * @return array
683
     */
684 3
    protected function getParameters()
685
    {
686 3
        return array_merge($this->parameters, $this->paginationParameters);
687
    }
688
689
    /**
690
     * Get the table of the model
691
     *
692
     * @return string
693
     */
694 3
    protected function getTable()
695
    {
696 3
        $type = $this->type;
697
698 3
        return $type::TABLE;
699
    }
700
701
    /**
702
     * Get a MySQL query string in the requested format
703
     * @param  string|string[] $columns The columns that should be included
704
     *                                  (without the ID, if an array is provided)
705
     * @return string The query
706
     */
707 3
    protected function createQuery($columns = array())
708
    {
709 3
        $type     = $this->type;
710 3
        $table    = $type::TABLE;
711 3
        $params   = $this->createQueryParams();
712
713 3
        if (is_array($columns)) {
714 2
            $columns = $this->createQueryColumns($columns);
715 3
        } elseif (empty($columns)) {
716
            $columns = $this->createQueryColumns();
717
        }
718
719 3
        return "SELECT $columns FROM $table $params";
720
    }
721
722
    /**
723
     * Generate the columns for the query
724
     * @param  string[] $columns The columns that should be included (without the ID)
725
     * @return string
726
     */
727 2
    private function createQueryColumns($columns = array())
728
    {
729 2
        $type = $this->type;
730 2
        $table = $type::TABLE;
731 2
        $columnStrings = array("`$table`.id");
732
733 2
        foreach ($columns as $returnName) {
734
            if (strpos($returnName, ' ') === false) {
735
                $dbName = $this->columns[$returnName];
736
                $columnStrings[] = "`$table`.`$dbName` as `$returnName`";
737
            } else {
738
                // "Column" contains a space, pass it as is
739
                $columnStrings[] = $returnName;
740
            }
741 2
        }
742
743 2
        return implode(',', $columnStrings);
744
    }
745
746
    /**
747
     * Generates all the WHERE conditions for the query
748
     * @return string
749
     */
750 3
    private function createQueryConditions()
751
    {
752 3
        if ($this->conditions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->conditions of type string[] 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...
753
            // Add parentheses around the conditions to prevent conflicts due
754
            // to the order of operations
755
            $conditions = array_map(function ($value) { return "($value)"; }, $this->conditions);
756
757 3
            return 'WHERE ' . implode(' AND ', $conditions);
758
        }
759
760
        return '';
761
    }
762
763
    /**
764
     * Generates the sorting instructions for the query
765
     * @return string
766
     */
767 3
    private function createQueryOrder()
768
    {
769 3
        if ($this->sortBy) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->sortBy of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
770 1
            $order = 'ORDER BY ' . $this->sortBy;
771
772
            // Sort by ID if the sorting columns are equal
773 1
            $id = '`' . $this->getTable() . '`.`id`';
774 1
            if ($this->reverseSort) {
775 1
                $order .= " DESC, $id DESC";
776 1
            } else {
777
                $order .= ", $id";
778
            }
779 1
        } else {
780 2
            $order = '';
781
        }
782
783 3
        return $order;
784
    }
785
786
    /**
787
     * Generates the pagination instructions for the query
788
     * @return string
789
     */
790 3
    private function createQueryPagination()
791
    {
792
        // Reset mysqli params just in case createQueryParagination()
793
        // had been called earlier
794 3
        $this->paginationParameters = array();
795
796 3
        if (!$this->page && !$this->limited) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->page of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

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

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
797 2
            return '';
798
        }
799
800 1
        $offset = '';
801 1
        if ($this->page) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->page of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

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

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
802 1
            $firstElement = ($this->page - 1) * $this->resultsPerPage;
803 1
            $this->paginationParameters[] = $firstElement;
804
805 1
            $offset = '?,';
806 1
        }
807
808 1
        $this->paginationParameters[] = $this->resultsPerPage;
809
810 1
        return "LIMIT $offset ?";
811
    }
812
}
813