Completed
Pull Request — master (#183)
by Konstantin
04:23
created

Pagerfanta::getNbResults()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
c 0
b 0
f 0
ccs 5
cts 5
cp 1
rs 9.4285
cc 2
eloc 4
nc 2
nop 0
crap 2
1
<?php
2
3
/*
4
 * This file is part of the Pagerfanta package.
5
 *
6
 * (c) Pablo Díez <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Pagerfanta;
13
14
use Pagerfanta\Adapter\AdapterInterface;
15
use Pagerfanta\Adapter\LazyAdapterInterface;
16
use Pagerfanta\Exception\LogicException;
17
use Pagerfanta\Exception\NotBooleanException;
18
use Pagerfanta\Exception\NotIntegerMaxPerPageException;
19
use Pagerfanta\Exception\LessThan1MaxPerPageException;
20
use Pagerfanta\Exception\NotIntegerCurrentPageException;
21
use Pagerfanta\Exception\LessThan1CurrentPageException;
22
use Pagerfanta\Exception\OutOfRangeCurrentPageException;
23
24
/**
25
 * Represents a paginator.
26
 *
27
 * @author Pablo Díez <[email protected]>
28
 */
29
class Pagerfanta implements \Countable, \IteratorAggregate, PagerfantaInterface
0 ignored issues
show
Deprecated Code introduced by
The interface Pagerfanta\PagerfantaInterface has been deprecated.

This class, trait or interface has been deprecated.

Loading history...
30
{
31
    private $adapter;
32
    private $allowOutOfRangePages;
33
    private $normalizeOutOfRangePages;
34
    private $maxPerPage;
35
    private $currentPage;
36
    private $nbResults;
37
    private $currentPageResults;
38
39
    /**
40
     * @param AdapterInterface $adapter An adapter.
41
     */
42 125
    public function __construct(AdapterInterface $adapter)
43
    {
44 125
        $this->adapter = $adapter;
45 125
        $this->allowOutOfRangePages = false;
46 125
        $this->normalizeOutOfRangePages = false;
47 125
        $this->maxPerPage = 10;
48 125
        $this->currentPage = 1;
49 125
    }
50
51
    /**
52
     * Returns the adapter.
53
     *
54
     * @return AdapterInterface The adapter.
55
     */
56 74
    public function getAdapter()
57
    {
58 74
        return $this->adapter;
59
    }
60
61
    /**
62
     * Sets whether or not allow out of range pages.
63
     *
64
     * @param Boolean $value
65
     *
66
     * @return self
67
     */
68 10
    public function setAllowOutOfRangePages($value)
69
    {
70 10
        $this->allowOutOfRangePages = $this->filterBoolean($value);
71
72 7
        return $this;
73
    }
74
75
    /**
76
     * Returns whether or not allow out of range pages.
77
     *
78
     * @return Boolean
79
     */
80 75
    public function getAllowOutOfRangePages()
81
    {
82 75
        return $this->allowOutOfRangePages;
83
    }
84
85
    /**
86
     * Sets whether or not normalize out of range pages.
87
     *
88
     * @param Boolean $value
89
     *
90
     * @return self
91
     */
92 8
    public function setNormalizeOutOfRangePages($value)
93
    {
94 8
        $this->normalizeOutOfRangePages = $this->filterBoolean($value);
95
96 5
        return $this;
97
    }
98
99
    /**
100
     * Returns whether or not normalize out of range pages.
101
     *
102
     * @return Boolean
103
     */
104 7
    public function getNormalizeOutOfRangePages()
105
    {
106 7
        return $this->normalizeOutOfRangePages;
107
    }
108
109 16
    private function filterBoolean($value)
110
    {
111 16
        if (!is_bool($value)) {
112 6
            throw new NotBooleanException();
113
        }
114
115 10
        return $value;
116
    }
117
118
    /**
119
     * Sets the max per page.
120
     *
121
     * Tries to convert from string and float.
122
     *
123
     * @param integer $maxPerPage
124
     *
125
     * @return self
126
     *
127
     * @throws NotIntegerMaxPerPageException If the max per page is not an integer even converting.
128
     * @throws LessThan1MaxPerPageException  If the max per page is less than 1.
129
     */
130 50
    public function setMaxPerPage($maxPerPage)
131
    {
132 50
        $this->maxPerPage = $this->filterMaxPerPage($maxPerPage);
133 44
        $this->resetForMaxPerPageChange();
134
135 44
        return $this;
136
    }
137
138 50
    private function filterMaxPerPage($maxPerPage)
139
    {
140 50
        $maxPerPage = $this->toInteger($maxPerPage);
141 50
        $this->checkMaxPerPage($maxPerPage);
142
143 44
        return $maxPerPage;
144
    }
145
146 50
    private function checkMaxPerPage($maxPerPage)
147
    {
148 50
        if (!is_int($maxPerPage)) {
149 4
            throw new NotIntegerMaxPerPageException();
150
        }
151
152 46
        if ($maxPerPage < 1) {
153 2
            throw new LessThan1MaxPerPageException();
154
        }
155 44
    }
156
157 44
    private function resetForMaxPerPageChange()
158
    {
159 44
        $this->currentPageResults = null;
160 44
        $this->nbResults = null;
161 44
    }
162
163
    /**
164
     * Returns the max per page.
165
     *
166
     * @return integer
167
     */
168 78
    public function getMaxPerPage()
169
    {
170 78
        return $this->maxPerPage;
171
    }
172
173
    /**
174
     * Sets the current page.
175
     *
176
     * Tries to convert from string and float.
177
     *
178
     * @param integer $currentPage
179
     *
180
     * @return self
181
     *
182
     * @throws NotIntegerCurrentPageException If the current page is not an integer even converting.
183
     * @throws LessThan1CurrentPageException  If the current page is less than 1.
184
     * @throws OutOfRangeCurrentPageException If It is not allowed out of range pages and they are not normalized.
185
     */
186 79
    public function setCurrentPage($currentPage)
187
    {
188 79
        $this->useDeprecatedCurrentPageBooleanArguments(func_get_args());
189
190 79
        $this->currentPage = $this->filterCurrentPage($currentPage);
191 72
        $this->resetForCurrentPageChange();
192
193 72
        return $this;
194
    }
195
196 79
    private function useDeprecatedCurrentPageBooleanArguments($arguments)
197
    {
198 79
        $this->useDeprecatedCurrentPageAllowOutOfRangePagesBooleanArgument($arguments);
199 79
        $this->useDeprecatedCurrentPageNormalizeOutOfRangePagesBooleanArgument($arguments);
200 79
    }
201
202 79
    private function useDeprecatedCurrentPageAllowOutOfRangePagesBooleanArgument($arguments)
203
    {
204 79
        $index = 1;
205 79
        $method = 'setAllowOutOfRangePages';
206
207 79
        $this->useDeprecatedBooleanArgument($arguments, $index, $method);
208 79
    }
209
210 79
    private function useDeprecatedCurrentPageNormalizeOutOfRangePagesBooleanArgument($arguments)
211
    {
212 79
        $index = 2;
213 79
        $method = 'setNormalizeOutOfRangePages';
214
215 79
        $this->useDeprecatedBooleanArgument($arguments, $index, $method);
216 79
    }
217
218 79
    private function useDeprecatedBooleanArgument($arguments, $index, $method)
219
    {
220 79
        if (isset($arguments[$index])) {
221 2
            $this->$method($arguments[$index]);
222 2
        }
223 79
    }
224
225 79
    private function filterCurrentPage($currentPage)
226
    {
227 79
        $currentPage = $this->toInteger($currentPage);
228 79
        $this->checkCurrentPage($currentPage);
229 73
        if (!$this->adapter instanceof LazyAdapterInterface) {
230 71
            $currentPage = $this->filterOutOfRangeCurrentPage($currentPage);
231 70
        }
232
233 72
        return $currentPage;
234
    }
235
236 79
    private function checkCurrentPage($currentPage)
237
    {
238 79
        if (!is_int($currentPage)) {
239 4
            throw new NotIntegerCurrentPageException();
240
        }
241
242 75
        if ($currentPage < 1) {
243 2
            throw new LessThan1CurrentPageException();
244
        }
245 73
    }
246
247 72
    private function filterOutOfRangeCurrentPage($currentPage)
248
    {
249 72
        if ($this->notAllowedCurrentPageOutOfRange($currentPage)) {
250 4
            return $this->normalizeOutOfRangeCurrentPage($currentPage);
251
        }
252
253 68
        return $currentPage;
254
    }
255
256 72
    private function notAllowedCurrentPageOutOfRange($currentPage)
257
    {
258 72
        return !$this->getAllowOutOfRangePages() &&
259 72
               $this->currentPageOutOfRange($currentPage);
260
    }
261
262 70
    private function currentPageOutOfRange($currentPage)
263
    {
264 70
        return $currentPage > 1 && $currentPage > $this->getNbPages();
265
    }
266
267
    /**
268
     * @param int $currentPage
269
     *
270
     * @return int
271
     *
272
     * @throws OutOfRangeCurrentPageException If the page should not be normalized
273
     */
274 4
    private function normalizeOutOfRangeCurrentPage($currentPage)
275
    {
276 4
        if ($this->getNormalizeOutOfRangePages()) {
277 2
            return $this->getNbPages();
278
        }
279
280 2
        throw new OutOfRangeCurrentPageException(sprintf('Page "%d" does not exist. The currentPage must be inferior to "%d"', $currentPage, $this->getNbPages()));
281
    }
282
283 72
    private function resetForCurrentPageChange()
284
    {
285 72
        $this->currentPageResults = null;
286 72
    }
287
288
    /**
289
     * Returns the current page.
290
     *
291
     * @return integer
292
     */
293 64
    public function getCurrentPage()
294
    {
295 64
        return $this->currentPage;
296
    }
297
298
    /**
299
     * Returns the results for the current page.
300
     *
301
     * @return array|\Traversable
302
     */
303 10
    public function getCurrentPageResults()
304
    {
305 10
        if ($this->notCachedCurrentPageResults()) {
306 10
            $this->currentPageResults = $this->getCurrentPageResultsFromAdapter();
307 9
        }
308
309 9
        return $this->currentPageResults;
310
    }
311
312 10
    private function notCachedCurrentPageResults()
313
    {
314 10
        return $this->currentPageResults === null;
315
    }
316
317 10
    private function getCurrentPageResultsFromAdapter()
318
    {
319 10
        $offset = $this->calculateOffsetForCurrentPageResults();
320 10
        $length = $this->getMaxPerPage();
321
322 10
        $slice = $this->adapter->getSlice($offset, $length);
323
324 10
        if ($this->adapter instanceof LazyAdapterInterface) {
325 1
            $page = $this->filterOutOfRangeCurrentPage($this->getCurrentPage());
326
            if ($page != $this->getCurrentPage()) {
327
                $this->setCurrentPage($page);
328
329
                return $this->getCurrentPageResultsFromAdapter();
330
            }
331
        }
332
333 9
        return $slice;
334
    }
335
336 11
    private function calculateOffsetForCurrentPageResults()
337
    {
338 11
        return ($this->getCurrentPage() - 1) * $this->getMaxPerPage();
339
    }
340
341
    /**
342
     * Calculates the current page offset start
343
     *
344
     * @return int
345
     */
346 2
    public function getCurrentPageOffsetStart()
347
    {
348 2
        return $this->getNbResults() ?
349 2
               $this->calculateOffsetForCurrentPageResults() + 1 :
350 2
               0;
351
    }
352
353
    /**
354
     * Calculates the current page offset end
355
     *
356
     * @return int
357
     */
358 2
    public function getCurrentPageOffsetEnd()
359
    {
360 2
        return $this->hasNextPage() ?
361 2
               $this->getCurrentPage() * $this->getMaxPerPage() :
362 2
               $this->getNbResults();
363
    }
364
365
    /**
366
     * Returns the number of results.
367
     *
368
     * @return integer
369
     */
370 73
    public function getNbResults()
371
    {
372 73
        if ($this->notCachedNbResults()) {
373 73
            $this->nbResults = $this->getAdapter()->getNbResults();
374 73
        }
375
376 73
        return $this->nbResults;
377
    }
378
379 73
    private function notCachedNbResults()
380
    {
381 73
        return $this->nbResults === null;
382
    }
383
384
    /**
385
     * Returns the number of pages.
386
     *
387
     * @return integer
388
     */
389 65
    public function getNbPages()
390
    {
391 65
        $nbPages = $this->calculateNbPages();
392
393 65
        if ($nbPages == 0) {
394 1
            return $this->minimumNbPages();
395
        }
396
397 64
        return $nbPages;
398
    }
399
400 65
    private function calculateNbPages()
401
    {
402 65
        return (int) ceil($this->getNbResults() / $this->getMaxPerPage());
403
    }
404
405 1
    private function minimumNbPages()
406
    {
407 1
        return 1;
408
    }
409
410
    /**
411
     * Returns if the number of results is higher than the max per page.
412
     *
413
     * @return Boolean
414
     */
415 3
    public function haveToPaginate()
416
    {
417 3
        return $this->getNbResults() > $this->maxPerPage;
418
    }
419
420
    /**
421
     * Returns whether there is previous page or not.
422
     *
423
     * @return Boolean
424
     */
425 46
    public function hasPreviousPage()
426
    {
427 46
        return $this->currentPage > 1;
428
    }
429
430
    /**
431
     * Returns the previous page.
432
     *
433
     * @return integer
434
     *
435
     * @throws LogicException If there is no previous page.
436
     */
437 35
    public function getPreviousPage()
438
    {
439 35
        if (!$this->hasPreviousPage()) {
440 1
            throw new LogicException('There is no previous page.');
441
        }
442
443 34
        return $this->currentPage - 1;
444
    }
445
446
    /**
447
     * Returns whether there is next page or not.
448
     *
449
     * @return Boolean
450
     */
451 48
    public function hasNextPage()
452
    {
453 48
        return $this->currentPage < $this->getNbPages();
454
    }
455
456
    /**
457
     * Returns the next page.
458
     *
459
     * @return integer
460
     *
461
     * @throws LogicException If there is no next page.
462
     */
463 40
    public function getNextPage()
464
    {
465 40
        if (!$this->hasNextPage()) {
466 1
            throw new LogicException('There is no next page.');
467
        }
468
469 39
        return $this->currentPage + 1;
470
    }
471
472
    /**
473
     * Implements the \Countable interface.
474
     *
475
     * Return integer The number of results.
476
     */
477 1
    public function count()
478
    {
479 1
        return $this->getNbResults();
480
    }
481
482
    /**
483
     * Implements the \IteratorAggregate interface.
484
     *
485
     * Returns an \ArrayIterator instance with the current results.
486
     */
487 3
    public function getIterator()
488
    {
489 3
        $results = $this->getCurrentPageResults();
490
491 3
        if ($results instanceof \Iterator) {
492 1
            return $results;
493
        }
494
495 2
        if ($results instanceof \IteratorAggregate) {
496 1
            return $results->getIterator();
497
        }
498
499 1
        return new \ArrayIterator($results);
500
    }
501
502 100
    private function toInteger($value)
503
    {
504 100
        if ($this->needsToIntegerConversion($value)) {
505 6
            return (int) $value;
506
        }
507
508 97
        return $value;
509
    }
510
511 100
    private function needsToIntegerConversion($value)
512
    {
513 100
        return (is_string($value) || is_float($value)) && (int) $value == $value;
514
    }
515
}
516