Completed
Pull Request — master (#183)
by Konstantin
02:30
created

Pagerfanta   C

Complexity

Total Complexity 73

Size/Duplication

Total Lines 511
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 97.79%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 73
lcom 1
cbo 9
dl 0
loc 511
ccs 177
cts 181
cp 0.9779
rs 5.5447
c 2
b 1
f 0

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