PagerfantaPaginator   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 125
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
wmc 16
lcom 1
cbo 2
dl 0
loc 125
rs 10
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A getIterator() 0 6 1
A count() 0 4 1
A totalCount() 0 6 1
A haveToPaginate() 0 6 1
A getCurrentPage() 0 6 1
A getNbPages() 0 6 1
A hasPreviousPage() 0 6 1
A hasNextPage() 0 6 1
A getNextPage() 0 6 1
A getPreviousPage() 0 6 1
A setCurrentPage() 0 6 1
A paginate() 0 8 2
A calculateMaxPerPageForArray() 0 8 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Explicit Architecture POC,
7
 * which is created on top of the Symfony Demo application.
8
 *
9
 * (c) Herberto Graça <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Acme\App\Presentation\Web\Infrastructure\Paginator\Pagerfanta;
16
17
use Acme\App\Presentation\Web\Core\Port\Paginator\PaginatorInterface;
18
use ArrayIterator;
19
use IteratorAggregate;
20
use Pagerfanta\Adapter\ArrayAdapter;
21
use Pagerfanta\Pagerfanta;
22
23
final class PagerfantaPaginator implements PaginatorInterface, PagerfantaInterface, IteratorAggregate
24
{
25
    /**
26
     * @var Pagerfanta
27
     */
28
    private $pagerfanta;
29
30
    /**
31
     * @var array
32
     */
33
    private $data;
34
35
    /**
36
     * @var int
37
     */
38
    private $page;
39
40
    /**
41
     * @var int
42
     */
43
    private $maxItemsPerPage;
44
45
    public function __construct(
46
        array $data,
47
        int $page = self::DEFAULT_PAGE,
48
        int $maxItemsPerPage = self::DEFAULT_MAX_ITEMS_PER_PAGE
49
    ) {
50
        $this->data = $data;
51
        $this->page = $page;
52
        $this->maxItemsPerPage = $maxItemsPerPage;
53
    }
54
55
    public function getIterator(): ArrayIterator
56
    {
57
        $this->paginate($this->data);
58
59
        return $this->pagerfanta->getIterator();
60
    }
61
62
    public function count(): int
63
    {
64
        return $this->getIterator()->count();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Traversable as the method count() does only exist in the following implementations of said interface: Acme\App\Core\Port\Persistence\ResultCollection, Acme\App\Presentation\We...nta\PagerfantaPaginator, ArrayIterator, ArrayObject, CachingIterator, Codeception\Example, Codeception\Suite, Doctrine\Common\Collections\AbstractLazyCollection, Doctrine\Common\Collections\ArrayCollection, Doctrine\ORM\LazyCriteriaCollection, Doctrine\ORM\PersistentCollection, Doctrine\ORM\Tools\Console\MetadataFilter, Doctrine\ORM\Tools\Pagination\Paginator, GlobIterator, GuzzleHttp\Cookie\CookieJar, GuzzleHttp\Cookie\FileCookieJar, GuzzleHttp\Cookie\SessionCookieJar, HTMLPurifier_StringHash, HttpMessage, HttpRequestPool, Issue523, MongoCursor, MongoGridFSCursor, PHPUnit\Framework\DataProviderTestSuite, PHPUnit\Framework\TestSuite, PHP_Token_Stream, Pagerfanta\Pagerfanta, Phar, PharData, PharIo\Manifest\AuthorCollection, PharIo\Manifest\BundledComponentCollection, PharIo\Manifest\RequirementCollection, PhpCsFixer\Doctrine\Annotation\Tokens, PhpCsFixer\Finder, PhpCsFixer\Runner\FileCachingLintingIterator, PhpCsFixer\Tokenizer\Tokens, RecursiveArrayIterator, RecursiveCachingIterator, SQLiteResult, SebastianBergmann\CodeCoverage\Node\Directory, SimpleXMLElement, SimpleXMLIterator, SplDoublyLinkedList, SplFixedArray, SplHeap, SplMaxHeap, SplMinHeap, SplObjectStorage, SplPriorityQueue, SplQueue, SplStack, Symfony\Bridge\Twig\Node\DumpNode, Symfony\Bridge\Twig\Node\FormThemeNode, Symfony\Bridge\Twig\Node\RenderBlockNode, Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode, Symfony\Bridge\Twig\Node\StopwatchNode, Symfony\Bridge\Twig\Node\TransDefaultDomainNode, Symfony\Bridge\Twig\Node\TransNode, Symfony\Component\Depend...ent\RewindableGenerator, Symfony\Component\DomCrawler\Crawler, Symfony\Component\Finder\Finder, Symfony\Component\Form\Button, Symfony\Component\Form\ButtonBuilder, Symfony\Component\Form\E...r\ViolationPathIterator, Symfony\Component\Form\Form, Symfony\Component\Form\FormBuilder, Symfony\Component\Form\FormErrorIterator, Symfony\Component\Form\FormView, Symfony\Component\Form\SubmitButton, Symfony\Component\Form\SubmitButtonBuilder, Symfony\Component\Form\Util\OrderedHashMap, Symfony\Component\HttpFoundation\FileBag, Symfony\Component\HttpFoundation\HeaderBag, Symfony\Component\HttpFoundation\ParameterBag, Symfony\Component\HttpFoundation\ResponseHeaderBag, Symfony\Component\HttpFoundation\ServerBag, Symfony\Component\HttpFo...\Attribute\AttributeBag, Symfony\Component\HttpFo...\NamespacedAttributeBag, Symfony\Component\HttpFoundation\Session\Session, Symfony\Component\HttpKe.../HttpClientKernel.php$0, Symfony\Component\Proper...ss\PropertyPathIterator, Symfony\Component\Routing\RouteCollection, Symfony\Component\Valida...ConstraintViolationList, Symfony\Component\VarDumper\Cloner\Data, TheSeer\Tokenizer\TokenCollection, Twig\Extensions\Node\TransNode, Twig\Node\AutoEscapeNode, Twig\Node\BlockNode, Twig\Node\BlockReferenceNode, Twig\Node\BodyNode, Twig\Node\CheckSecurityNode, Twig\Node\CheckToStringNode, Twig\Node\DeprecatedNode, Twig\Node\DoNode, Twig\Node\EmbedNode, Twig\Node\Expression\AbstractExpression, Twig\Node\Expression\ArrayExpression, Twig\Node\Expression\ArrowFunctionExpression, Twig\Node\Expression\AssignNameExpression, Twig\Node\Expression\Binary\AbstractBinary, Twig\Node\Expression\Binary\AddBinary, Twig\Node\Expression\Binary\AndBinary, Twig\Node\Expression\Binary\BitwiseAndBinary, Twig\Node\Expression\Binary\BitwiseOrBinary, Twig\Node\Expression\Binary\BitwiseXorBinary, Twig\Node\Expression\Binary\ConcatBinary, Twig\Node\Expression\Binary\DivBinary, Twig\Node\Expression\Binary\EndsWithBinary, Twig\Node\Expression\Binary\EqualBinary, Twig\Node\Expression\Binary\FloorDivBinary, Twig\Node\Expression\Binary\GreaterBinary, Twig\Node\Expression\Binary\GreaterEqualBinary, Twig\Node\Expression\Binary\InBinary, Twig\Node\Expression\Binary\LessBinary, Twig\Node\Expression\Binary\LessEqualBinary, Twig\Node\Expression\Binary\MatchesBinary, Twig\Node\Expression\Binary\ModBinary, Twig\Node\Expression\Binary\MulBinary, Twig\Node\Expression\Binary\NotEqualBinary, Twig\Node\Expression\Binary\NotInBinary, Twig\Node\Expression\Binary\OrBinary, Twig\Node\Expression\Binary\PowerBinary, Twig\Node\Expression\Binary\RangeBinary, Twig\Node\Expression\Binary\SpaceshipBinary, Twig\Node\Expression\Binary\StartsWithBinary, Twig\Node\Expression\Binary\SubBinary, Twig\Node\Expression\BlockReferenceExpression, Twig\Node\Expression\CallExpression, Twig\Node\Expression\ConditionalExpression, Twig\Node\Expression\ConstantExpression, Twig\Node\Expression\FilterExpression, Twig\Node\Expression\Filter\DefaultFilter, Twig\Node\Expression\FunctionExpression, Twig\Node\Expression\GetAttrExpression, Twig\Node\Expression\InlinePrint, Twig\Node\Expression\MethodCallExpression, Twig\Node\Expression\NameExpression, Twig\Node\Expression\NullCoalesceExpression, Twig\Node\Expression\ParentExpression, Twig\Node\Expression\TempNameExpression, Twig\Node\Expression\TestExpression, Twig\Node\Expression\Test\ConstantTest, Twig\Node\Expression\Test\DefinedTest, Twig\Node\Expression\Test\DivisiblebyTest, Twig\Node\Expression\Test\EvenTest, Twig\Node\Expression\Test\NullTest, Twig\Node\Expression\Test\OddTest, Twig\Node\Expression\Test\SameasTest, Twig\Node\Expression\Unary\AbstractUnary, Twig\Node\Expression\Unary\NegUnary, Twig\Node\Expression\Unary\NotUnary, Twig\Node\Expression\Unary\PosUnary, Twig\Node\Expression\VariadicExpression, Twig\Node\FlushNode, Twig\Node\ForLoopNode, Twig\Node\ForNode, Twig\Node\IfNode, Twig\Node\ImportNode, Twig\Node\IncludeNode, Twig\Node\MacroNode, Twig\Node\ModuleNode, Twig\Node\Node, Twig\Node\PrintNode, Twig\Node\SandboxNode, Twig\Node\SandboxedPrintNode, Twig\Node\SetNode, Twig\Node\SpacelessNode, Twig\Node\TextNode, Twig\Node\WithNode, Twig\Profiler\Node\EnterProfileNode, Twig\Profiler\Node\LeaveProfileNode, Twig_Extensions_Node_Trans, Twig_Node, Twig_Node_AutoEscape, Twig_Node_Block, Twig_Node_BlockReference, Twig_Node_Body, Twig_Node_CheckSecurity, Twig_Node_Deprecated, Twig_Node_Do, Twig_Node_Embed, Twig_Node_Expression, Twig_Node_Expression_Array, Twig_Node_Expression_AssignName, Twig_Node_Expression_Binary, Twig_Node_Expression_Binary_Add, Twig_Node_Expression_Binary_And, Twig_Node_Expression_Binary_BitwiseAnd, Twig_Node_Expression_Binary_BitwiseOr, Twig_Node_Expression_Binary_BitwiseXor, Twig_Node_Expression_Binary_Concat, Twig_Node_Expression_Binary_Div, Twig_Node_Expression_Binary_EndsWith, Twig_Node_Expression_Binary_Equal, Twig_Node_Expression_Binary_FloorDiv, Twig_Node_Expression_Binary_Greater, Twig_Node_Expression_Binary_GreaterEqual, Twig_Node_Expression_Binary_In, Twig_Node_Expression_Binary_Less, Twig_Node_Expression_Binary_LessEqual, Twig_Node_Expression_Binary_Matches, Twig_Node_Expression_Binary_Mod, Twig_Node_Expression_Binary_Mul, Twig_Node_Expression_Binary_NotEqual, Twig_Node_Expression_Binary_NotIn, Twig_Node_Expression_Binary_Or, Twig_Node_Expression_Binary_Power, Twig_Node_Expression_Binary_Range, Twig_Node_Expression_Binary_StartsWith, Twig_Node_Expression_Binary_Sub, Twig_Node_Expression_BlockReference, Twig_Node_Expression_Call, Twig_Node_Expression_Conditional, Twig_Node_Expression_Constant, Twig_Node_Expression_Filter, Twig_Node_Expression_Filter_Default, Twig_Node_Expression_Function, Twig_Node_Expression_GetAttr, Twig_Node_Expression_MethodCall, Twig_Node_Expression_Name, Twig_Node_Expression_NullCoalesce, Twig_Node_Expression_Parent, Twig_Node_Expression_TempName, Twig_Node_Expression_Test, Twig_Node_Expression_Test_Constant, Twig_Node_Expression_Test_Defined, Twig_Node_Expression_Test_Divisibleby, Twig_Node_Expression_Test_Even, Twig_Node_Expression_Test_Null, Twig_Node_Expression_Test_Odd, Twig_Node_Expression_Test_Sameas, Twig_Node_Expression_Unary, Twig_Node_Expression_Unary_Neg, Twig_Node_Expression_Unary_Not, Twig_Node_Expression_Unary_Pos, Twig_Node_Flush, Twig_Node_For, Twig_Node_ForLoop, Twig_Node_If, Twig_Node_Import, Twig_Node_Include, Twig_Node_Macro, Twig_Node_Module, Twig_Node_Print, Twig_Node_Sandbox, Twig_Node_SandboxedPrint, Twig_Node_Set, Twig_Node_Spaceless, Twig_Node_Text, Twig_Node_With, Twig_Profiler_Node_EnterProfile, Twig_Profiler_Node_LeaveProfile, Zend\Code\Annotation\AnnotationCollection, Zend\Code\Scanner\AnnotationScanner, Zend\EventManager\ResponseCollection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
65
    }
66
67
    public function totalCount(): int
68
    {
69
        $this->paginate($this->data);
70
71
        return $this->pagerfanta->count();
72
    }
73
74
    public function haveToPaginate(): bool
75
    {
76
        $this->paginate($this->data);
77
78
        return $this->pagerfanta->haveToPaginate();
79
    }
80
81
    public function getCurrentPage(): int
82
    {
83
        $this->paginate($this->data);
84
85
        return $this->pagerfanta->getCurrentPage();
86
    }
87
88
    public function getNbPages(): int
89
    {
90
        $this->paginate($this->data);
91
92
        return $this->pagerfanta->getNbPages();
93
    }
94
95
    public function hasPreviousPage(): bool
96
    {
97
        $this->paginate($this->data);
98
99
        return $this->pagerfanta->hasPreviousPage();
100
    }
101
102
    public function hasNextPage(): bool
103
    {
104
        $this->paginate($this->data);
105
106
        return $this->pagerfanta->hasNextPage();
107
    }
108
109
    public function getNextPage(): int
110
    {
111
        $this->paginate($this->data);
112
113
        return $this->pagerfanta->getNextPage();
114
    }
115
116
    public function getPreviousPage(): int
117
    {
118
        $this->paginate($this->data);
119
120
        return $this->pagerfanta->getPreviousPage();
121
    }
122
123
    public function setCurrentPage(int $page): void
124
    {
125
        $this->paginate($this->data);
126
127
        $this->pagerfanta->setCurrentPage($page);
128
    }
129
130
    private function paginate(array $data): void
131
    {
132
        if (!$this->pagerfanta) {
133
            $this->pagerfanta = new Pagerfanta(new ArrayAdapter($data));
134
            $this->pagerfanta->setMaxPerPage($this->calculateMaxPerPageForArray($data));
135
            $this->pagerfanta->setCurrentPage($this->page);
136
        }
137
    }
138
139
    private function calculateMaxPerPageForArray(array $result): int
140
    {
141
        if (\count($result) === 0) {
142
            return $this->maxItemsPerPage;
143
        }
144
145
        return min([\count($result), $this->maxItemsPerPage]);
146
    }
147
}
148