Pager::current()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 1
eloc 1
c 2
b 1
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
/**
3
 * @author Gerard van Helden <[email protected]>
4
 * @copyright Zicht Online <http://zicht.nl>
5
 */
6
namespace Zicht\Bundle\FrameworkExtraBundle\Pager;
7
8
/**
9
 * Pager implementation to handle paging over a countable set of elements
10
 */
11
class Pager implements \Iterator, \ArrayAccess, \Countable
12
{
13
    private $currentPage;
14
    private $total;
15
    private $numPages;
16
    private $offset;
17
    private $lengthOfRange;
18
    private $itemsPerPage;
19
    private $results;
20
21
    /**
22
     * Used for iterator implementation
23
     *
24
     * @var null
25
     */
26
    private $ptr = null;
27
28
    /**
29
     * Constructs the pager with the given set of elements to page over, and the given amount of items per page.
30
     *
31
     * @param \Zicht\Bundle\FrameworkExtraBundle\Pager\Pageable $pagable
32
     * @param int                                               $itemsPerPage
33
     */
34
    public function __construct(Pageable $pagable, $itemsPerPage)
35
    {
36
        $this->currentPage = -1;
37
        $this->total = null;
38
        $this->numPages = -1;
39
        $this->offset = -1;
40
        $this->lengthOfRange = -1;
41
        $this->itemsPerPage = -1;
42
        $this->results = null;
43
44
        $this->results = $pagable;
45
        $this->setItemsPerPage($itemsPerPage);
46
    }
47
48
49
    /**
50
     * Sets the maximum number of items per page
51
     *
52
     * @param int $itemsPerPage
53
     * @return void
54
     * @throws \InvalidArgumentException if the number of items is not a valid non-negative integer
55
     */
56
    public function setItemsPerPage($itemsPerPage)
57
    {
58
        if ($itemsPerPage <= 0) {
59
            throw new \InvalidArgumentException("Number of items per page must be positive integer");
60
        }
61
        $this->itemsPerPage = $itemsPerPage;
62
    }
63
64
65
    /**
66
     * Set the current page index, which is 0-index based.
67
     * (The first page is 0)
68
     * 
69
     * If the page index format is invalid, an InvalidArgumentException is thrown.
70
     * If the page index is out of range, it is trimmed to the nearest logical value; e.g. -1 is interpreted as 0,
71
     * 15 is interpreted as 7 if the number of pages is 8.
72
     *
73
     * @param int $page
74
     * @return void
75
     *
76
     * @throws \InvalidArgumentException
77
     */
78
    public function setCurrentPage($page)
79
    {
80
        if (is_null($this->total)) {
81
            $this->total = (int)$this->results->getTotal();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->results->getTotal() targeting Zicht\Bundle\FrameworkEx...er\Pageable::getTotal() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
82
        }
83
        if ((int)$page != $page) {
84
            throw new \InvalidArgumentException(
85
                "Invalid argument \$page, expected integer number, got " . gettype($page)
86
            );
87
        }
88
        $this->numPages = (int)ceil($this->total / $this->itemsPerPage);
89
        $this->currentPage = min(max(0, $this->getLast()), max($this->getFirst(), $page));
90
91
        $this->offset = $this->itemsPerPage * $this->currentPage;
92
        $this->lengthOfRange = $this->itemsPerPage;
93
94
        if ($this->offset + $this->lengthOfRange > $this->total) {
95
            $this->lengthOfRange = max(0, $this->total - $this->offset);
96
        }
97
98
        $this->results->setRange($this->offset, $this->lengthOfRange);
99
    }
100
101
102
    /**
103
     * Returns the first page index
104
     *
105
     * @return int
106
     */
107
    public function getFirst()
108
    {
109
        return 0;
110
    }
111
112
113
    /**
114
     * Returns the last page index
115
     *
116
     * @return int
117
     */
118
    public function getLast()
119
    {
120
        return $this->numPages - 1;
121
    }
122
123
    /**
124
     * Returns the 1-indexed start of the displayed range, used for displaying in templates
125
     *
126
     * @return int
127
     */
128
    public function getRangeStart()
129
    {
130
        return $this->offset + 1;
131
    }
132
133
134
    /**
135
     * Returns the 1-indexed end of the displayed range, used for displaying in templates
136
     *
137
     * @return int
138
     */
139
    public function getRangeEnd()
140
    {
141
        return $this->offset + $this->lengthOfRange;
142
    }
143
144
145
    /**
146
     * Combines the getRangeStart() and getRangeEnd() in one array
147
     *
148
     * @return array
149
     */
150
    public function getRange()
151
    {
152
        return array($this->getRangeStart(), $this->getRangeEnd());
153
    }
154
155
156
    /**
157
     * Returns the total of entire pageable set
158
     *
159
     * @return null
160
     */
161
    public function getItemTotal()
162
    {
163
        return $this->total;
164
    }
165
166
167
    /**
168
     * Returns whether the current page has a previous. This is only true for pages past the first.
169
     *
170
     * @return bool
171
     */
172
    public function hasPrevious()
173
    {
174
        return $this->currentPage > $this->getFirst();
175
    }
176
177
178
    /**
179
     * Returns whether the current page has a next. This is only true for pages before the last
180
     *
181
     * @return bool
182
     */
183
    public function hasNext()
184
    {
185
        return $this->offsetExists($this->currentPage + 1);
186
    }
187
188
189
    /**
190
     * Returns a set of meta information on the current page.
191
     * See itemAt() for the available information
192
     *
193
     * @return array
194
     */
195
    public function getCurrent()
196
    {
197
        return $this->itemAt($this->currentPage);
198
    }
199
200
201
    /**
202
     * Returns the meta data for the next page, and 'null' if there is none
203
     *
204
     * @return array|null
205
     */
206
    public function getNext()
207
    {
208
        if ($this->hasNext()) {
209
            return $this->itemAt($this->currentPage + 1);
210
        }
211
        return null;
212
    }
213
214
215
    /**
216
     * Returns the meta data for the previous page, and 'null' if there is none
217
     *
218
     * @return array|null
219
     */
220
    public function getPrevious()
221
    {
222
        if ($this->hasPrevious()) {
223
            return $this->itemAt($this->currentPage - 1);
224
        }
225
        return null;
226
    }
227
228
229
    /**
230
     * Meta data helper function, returns the following meta data for each of the requested pages.
231
     *
232
     * - title: The displayable title for the current page (e.g. "1" for page 0)
233
     * - is_previous: Whether the page is the previous page
234
     * - is_current
235
     * - is_next: Whether the page is the next page
236
     *
237
     * @param int $i
238
     * @return array
239
     */
240
    private function itemAt($i)
241
    {
242
        return array(
243
            'index' => $i,
244
            'title' => $i + 1,
245
            'is_previous' => $i == ($this->currentPage - 1),
246
            'is_current' => $i == $this->currentPage,
247
            'is_next' => $i == ($this->currentPage + 1)
248
        );
249
    }
250
251
    /**
252
     * Iterator::current() implementation
253
     * Returns the meta data for the current item in the iterator.
254
     *
255
     * @return array
256
     */
257
    public function current()
258
    {
259
        return $this->itemAt($this->ptr);
260
    }
261
262
263
    /**
264
     * Iterator::next() implementation, advances the iterator one item.
265
     *
266
     * @return void
267
     */
268
    public function next()
269
    {
270
        $this->ptr++;
271
    }
272
273
274
    /**
275
     * Returns the key of the current Iterator item, which is the page index.
276
     *
277
     * @return int
278
     */
279
    public function key()
280
    {
281
        return $this->ptr;
282
    }
283
284
285
    /**
286
     * Iterator::valid() implementation, checks if the current iterator index is valid
287
     *
288
     * @return bool
289
     */
290
    public function valid()
291
    {
292
        return $this->offsetExists($this->ptr);
293
    }
294
295
296
    /**
297
     * Iterator::rewind() implementation; rewinds the iterator to the start of the range
298
     *
299
     * @return void
300
     */
301
    public function rewind()
302
    {
303
        $this->ptr = $this->getFirst();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getFirst() of type integer is incompatible with the declared type null of property $ptr.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
304
    }
305
306
307
    /**
308
     * ArrayAccess:offsetExists() implementation, checks if the given page index is valid.
309
     *
310
     * @param int $offset
311
     * @return bool
312
     */
313
    public function offsetExists($offset)
314
    {
315
        return is_int($offset) && $offset >= $this->getFirst() && $offset <= $this->getLast();
316
    }
317
318
319
    /**
320
     * ArrayAccess::offsetGet() implementation; returns the meta data for the given page index, and null
321
     * if it does not exist.
322
     *
323
     * @param int $offset
324
     * @return array
325
     */
326
    public function offsetGet($offset)
327
    {
328
        if ($this->offsetExists($offset)) {
329
            return $this->itemAt($offset);
330
        }
331
        return null;
332
    }
333
334
335
    /**
336
     * ArrayAccess::offsetSet() implementation, throws an exception as the page set is read only
337
     *
338
     * @param int $offset
339
     * @param mixed $value
340
     * @return void
341
     *
342
     * @throws \BadMethodCallException
343
     */
344
    public function offsetSet($offset, $value)
345
    {
346
        throw new \BadMethodCallException(__CLASS__ . ' is read only');
347
    }
348
349
350
    /**
351
     * ArrayAccess::offsetUnset() implementation, throws an exception as the page set is read only
352
     *
353
     * @param int $offset
354
     * @return void
355
     *
356
     * @throws \BadMethodCallException
357
     */
358
    public function offsetUnset($offset)
359
    {
360
        throw new \BadMethodCallException(__CLASS__ . ' is read only');
361
    }
362
363
364
    /**
365
     * Countable::count() implementation; Returns the number of pages in the page set.
366
     * 
367
     * @return int
368
     */
369
    public function count()
370
    {
371
        return $this->numPages;
372
    }
373
374
    /**
375
     * @return int
376
     */
377
    public function getItemsPerPage()
378
    {
379
        return $this->itemsPerPage;
380
    }
381
382
    /**
383
     * With gaps
384
     *
385
     * @param int $surround
386
     * @return array
387
     */
388
    public function withGaps($surround = 2)
389
    {
390
        $ret = [];
391
        $isPreviousGap = false;
392
        for ($i = 0; $i < $this->numPages; $i++) {
393
            if (($i >= $this->currentPage - $surround && $i <= $this->currentPage + $surround)
394
                || ($i < $this->getFirst() + $surround)
395
                || ($i > $this->getLast() - $surround)
396
            ) {
397
                $ret[$i] = $this->itemAt($i);
398
                $isPreviousGap = false;
399
            } elseif (!$isPreviousGap) {
400
                $isPreviousGap = true;
401
                $ret[$i]= null;
402
            }
403
        }
404
405
        return $ret;
406
    }
407
408
    /**
409
     * @return int
410
     */
411
    public function getCurrentPageIndex()
412
    {
413
        return $this->currentPage;
414
    }
415
}
416