Pagerfanta   F
last analyzed

Complexity

Total Complexity 72

Size/Duplication

Total Lines 513
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 72
lcom 1
cbo 9
dl 0
loc 513
ccs 172
cts 172
cp 1
rs 2.64
c 0
b 0
f 0

47 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A getAdapter() 0 4 1
A setAllowOutOfRangePages() 0 6 1
A getAllowOutOfRangePages() 0 4 1
A setNormalizeOutOfRangePages() 0 6 1
A getNormalizeOutOfRangePages() 0 4 1
A filterBoolean() 0 8 2
A setMaxPerPage() 0 7 1
A filterMaxPerPage() 0 7 1
A checkMaxPerPage() 0 10 3
A resetForMaxPerPageChange() 0 5 1
A getMaxPerPage() 0 4 1
A setCurrentPage() 0 9 1
A useDeprecatedCurrentPageBooleanArguments() 0 5 1
A useDeprecatedCurrentPageAllowOutOfRangePagesBooleanArgument() 0 7 1
A useDeprecatedCurrentPageNormalizeOutOfRangePagesBooleanArgument() 0 7 1
A useDeprecatedBooleanArgument() 0 6 2
A filterCurrentPage() 0 8 1
A checkCurrentPage() 0 10 3
A filterOutOfRangeCurrentPage() 0 8 2
A notAllowedCurrentPageOutOfRange() 0 5 2
A currentPageOutOfRange() 0 4 2
A normalizeOutOfRangeCurrentPage() 0 8 2
A resetForCurrentPageChange() 0 4 1
A getCurrentPage() 0 4 1
A getCurrentPageResults() 0 8 2
A notCachedCurrentPageResults() 0 4 1
A getCurrentPageResultsFromAdapter() 0 7 1
A calculateOffsetForCurrentPageResults() 0 4 1
A getCurrentPageOffsetStart() 0 6 2
A getCurrentPageOffsetEnd() 0 6 2
A getNbResults() 0 8 2
A notCachedNbResults() 0 4 1
A getNbPages() 0 10 2
A calculateNbPages() 0 4 1
A minimumNbPages() 0 4 1
A haveToPaginate() 0 4 1
A hasPreviousPage() 0 4 1
A getPreviousPage() 0 8 2
A hasNextPage() 0 4 1
A getNextPage() 0 8 2
A count() 0 4 1
A getIterator() 0 14 3
A toInteger() 0 8 2
A needsToIntegerConversion() 0 4 3
A jsonSerialize() 0 9 2
A getPageNumberForItemAtPosition() 0 16 3

How to fix   Complexity   

Complex Class

Complex classes like Pagerfanta often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Pagerfanta, and based on these observations, apply Extract Interface, too.

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