GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Paginator   B
last analyzed

Complexity

Total Complexity 43

Size/Duplication

Total Lines 292
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 43
lcom 1
cbo 0
dl 0
loc 292
ccs 106
cts 106
cp 1
rs 8.96
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 24 4
A setOptions() 0 18 5
F getHtml() 0 87 19
B getListItemHtml() 0 52 9
A getUrl() 0 8 2
A getRequestUrlWithNewPage() 0 10 2
A hasMultiplePages() 0 4 1
A __toString() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Paginator 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 Paginator, and based on these observations, apply Extract Interface, too.

1
<?php declare(strict_types=1);
2
/**
3
 * Paginator.
4
 *
5
 * @copyright Copyright (c) 2016 Starweb AB
6
 * @license   BSD 3-Clause
7
 */
8
9
namespace Starlit\Paginator;
10
11
use Symfony\Component\HttpFoundation\Request;
12
13
/**
14
 * @author Andreas Nilsson <http://github.com/jandreasn>
15
 * @author David Ajnered <https://github.com/davidajnered>
16
 */
17
class Paginator
18
{
19
    /**
20
     * @var int
21
     */
22
    protected $currentPageNo;
23
24
    /**
25
     * @var int
26
     */
27
    protected $pages;
28
29
    /**
30
     * @var int
31
     */
32
    protected $maxPagesToShow = 9; // Use odd number so that one number can be in the middle
33
34
    /**
35
     * @var callable
36
     */
37
    protected $urlGenerator;
38
39
    /**
40
     * @var Request|null
41
     */
42
    protected $request;
43
44
    /**
45
     * @var string
46
     */
47
    protected $containerCssClass = 'pagination';
48
49
    /**
50
     * @var string
51
     */
52
    protected $description;
53
54
    /**
55
     * If description should be shown regardless of number of pages.
56
     *
57
     * @var bool
58
     */
59
    protected $alwaysShowDescription = false;
60
61
    /**
62
     * Constructor.
63
     *
64
     * @param int $currentPageNo
65
     * @param int $rowsPerPage
66
     * @param int $totalRowCount
67
     * @param callable|Request $urlGeneratorOrRequest
68
     * @param array $options
69
     */
70 10
    public function __construct(
71
        int $currentPageNo,
72
        int $rowsPerPage,
73
        int $totalRowCount,
74
        $urlGeneratorOrRequest,
75
        array $options = []
76
    ) {
77 10
        $this->currentPageNo = $currentPageNo;
78
79 10
        $this->pages = (int) ceil($totalRowCount / $rowsPerPage);
80 10
        $this->pages = ($this->pages < 1) ? 1 : $this->pages; // Pages should never be less than 1
81
82 10
        if (is_callable($urlGeneratorOrRequest)) {
83 1
            $this->urlGenerator = $urlGeneratorOrRequest;
84 9
        } elseif ($urlGeneratorOrRequest instanceof Request) {
85 8
            $this->request = $urlGeneratorOrRequest;
86
        } else {
87 1
            throw new \InvalidArgumentException(
88 1
                'Argument must be either a Request or a callable url generator'
89
            );
90
        }
91
92 9
        $this->setOptions($options);
93 9
    }
94
95
    /**
96
     * @param array $options
97
     */
98 9
    public function setOptions(array $options = []): void
99
    {
100 9
        if (isset($options['maxPagesToShow'])) {
101 1
            $this->maxPagesToShow = (int) $options['maxPagesToShow'];
102
        }
103
104 9
        if (isset($options['containerCssClass'])) {
105 1
            $this->containerCssClass = $options['containerCssClass'];
106
        }
107
108 9
        if (isset($options['description'])) {
109 1
            $this->description = $options['description'];
110
        }
111
112 9
        if (isset($options['alwaysShowDescription'])) {
113 1
            $this->alwaysShowDescription = (bool) $options['alwaysShowDescription'];
114
        }
115 9
    }
116
117
    /**
118
     * @return string
119
     */
120 9
    public function getHtml(): string
121
    {
122 9
        $leftRightSpan = (($this->maxPagesToShow - 1) / 2);
123 9
        $showAll = false;
124
125 9
        if ($this->pages > $this->maxPagesToShow) {
126
            // Default start and end values. This works as long the selected page is not to close to the edges
127 4
            $paginationStart = $this->currentPageNo - $leftRightSpan + 1;
128 4
            $paginationStart = ($paginationStart < 2) ? 2 : $paginationStart;
129
130 4
            $paginationEnd = $this->currentPageNo + $leftRightSpan - 1;
131 4
            $paginationEnd = ($paginationEnd > ($this->pages - 1)) ? $this->pages - 1 : $paginationEnd;
132
133
            // If we're closer to the left side
134 4
            if ($this->currentPageNo <= $leftRightSpan) {
135 2
                $paginationEnd += $leftRightSpan - $this->currentPageNo + 1;
136
            }
137
138
            // If we're closer to the right side
139 4
            if ($this->currentPageNo >= $this->pages - $leftRightSpan) {
140 1
                $paginationStart -= $leftRightSpan - ($this->pages - $this->currentPageNo);
141
            }
142
143
            // If dots will be shown to the left, show one less list item so that the total list
144
            // item count won't be more than max
145 4
            if (($this->currentPageNo - 1) > $leftRightSpan) {
146 2
                $paginationStart++;
147
            }
148
149
            // If dots will be shown to the right, show one less list item
150 4
            if ($this->currentPageNo < ($this->pages - $leftRightSpan)) {
151 4
                $paginationEnd--;
152
            }
153
        } else {
154 5
            $paginationStart = 2;
155 5
            $paginationEnd = $this->pages - 1;
156 5
            $showAll = true;
157
        }
158
159
160 9
        $html = '<div class="'. $this->containerCssClass . ' ' . ($this->hasMultiplePages()
161 8
            ? 'multiple-pages'
162 9
            : 'single-page') . '">';
163
164 9
        if ($this->description && ($this->alwaysShowDescription || $this->hasMultiplePages())) {
165 1
            $html .= '<p>' . $this->description . '</p>';
166
        }
167
168 9
        if ($this->hasMultiplePages()) {
169 8
            $html .= '<ul>';
170
171
            // Prev link
172 8
            $html .= $this->getListItemHtml('prev');
173
174
            // Permanent first link
175 8
            $html .= $this->getListItemHtml(1);
176
177
            // If there are more than 4 links to the left of active link, show dots
178 8
            if (!$showAll && ($this->currentPageNo - 1) > $leftRightSpan) {
179 2
                $html .= '<li class="disabled gap"><span>...</span></li>';
180
            }
181
182
            // Loop and build links
183 8
            for ($page = $paginationStart; $page <= $paginationEnd; $page++) {
184 5
                $html .= $this->getListItemHtml($page);
185
            }
186
187
            // If there are more than 4 links to the right of active link, show dots
188 8
            if (!$showAll && $this->currentPageNo < ($this->pages - $leftRightSpan)) {
189 3
                $html .= '<li class="disabled gap"><span>...</span></li>';
190
            }
191
192
            // Permanent last link
193 8
            if ($this->pages > 1) {
194 8
                $html .= $this->getListItemHtml($this->pages);
195
            }
196
197
            // Next link
198 8
            $html .= $this->getListItemHtml('next');
199
200 8
            $html .= '</ul>';
201
        }
202
203 9
        $html .= '</div>';
204
205 9
        return $html;
206
    }
207
208
    /**
209
     * @param mixed $listItem Either a page number, or the strings 'prev' or 'next'
210
     * @return string
211
     */
212 8
    protected function getListItemHtml($listItem): string
213
    {
214 8
        $cssClass = '';
215 8
        $disabled = false;
216
        switch ($listItem) {
217 8
            case 'prev':
218 8
                $linkText = '&laquo;';
219 8
                $page = $this->currentPageNo - 1;
220 8
                $cssClass = 'previous';
221
222 8
                if ($this->currentPageNo === 1) {
223 5
                    $disabled = true;
224
                }
225
226 8
                break;
227 8
            case 'next':
228 8
                $linkText = '&raquo;';
229 8
                $page = $this->currentPageNo + 1;
230 8
                $cssClass = 'next';
231
232 8
                if ($this->currentPageNo === $this->pages) {
233 1
                    $disabled = true;
234
                }
235
236 8
                break;
237
            default:
238 8
                $linkText = $listItem;
239 8
                $page = (int) $listItem;
240
241 8
                if ($page === $this->currentPageNo) {
242 8
                    $cssClass = 'active';
243
                }
244 8
                break;
245
        }
246
247 8
        if ($disabled) {
248 6
            $cssClass .= ' disabled';
249
        }
250
251 8
        $html = '<li' . ($cssClass ? ' class="' . $cssClass . '"' : '') . '>';
252
253 8
        if ($disabled) {
254 6
            $html .= '<span>' . $linkText . '</span>';
255
        } else {
256 8
            $html .= '<a href="' . $this->getUrl($page) . '">' . $linkText . '</a>';
257
        }
258
259 8
        $html .= '</li>';
260
261
262 8
        return $html;
263
    }
264
265
    /**
266
     * @param int $page
267
     * @return string
268
     */
269 8
    protected function getUrl(int $page): string
270
    {
271 8
        if ($this->urlGenerator) {
272 1
            return call_user_func($this->urlGenerator, $page);
273
        }
274
275 7
        return $this->getRequestUrlWithNewPage($page);
276
    }
277
278
    /**
279
     * @param int $page
280
     * @return string
281
     */
282 7
    protected function getRequestUrlWithNewPage(int $page): string
283
    {
284
        // Make up paginated url from current url
285 7
        $url = $this->request->getRequestUri();
286 7
        $urlWithoutQueryString = (($pos = strpos($url, '?')) !== false) ? substr($url, 0, $pos) : $url;
287 7
        $parameters = array_merge($this->request->query->all(), ['page' => $page]);
288 7
        $newQueryString = http_build_query($parameters, '', '&amp;');
289
290 7
        return $urlWithoutQueryString . '?' . $newQueryString;
291
    }
292
293
    /**
294
     * @return bool
295
     */
296 9
    public function hasMultiplePages(): bool
297
    {
298 9
        return ($this->pages > 1);
299
    }
300
301
    /**
302
     * @return string
303
     */
304 1
    public function __toString(): string
305
    {
306 1
        return $this->getHtml();
307
    }
308
}
309