Skip to content

Commit 466422f

Browse files
committed
Refactor and harden code and tests, PHP 7.4+.
1 parent 20513d3 commit 466422f

File tree

5 files changed

+158
-157
lines changed

5 files changed

+158
-157
lines changed

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@
2424
}
2525
],
2626
"require": {
27-
"php": "^7.3|^8.0"
27+
"php": "^7.4|^8.0"
2828
},
2929
"require-dev": {
30-
"php-parallel-lint/php-parallel-lint": "v1.2",
30+
"php-parallel-lint/php-parallel-lint": "^1.3",
3131
"phpunit/phpunit": "^9.5",
32-
"squizlabs/php_codesniffer": "^3.5"
32+
"squizlabs/php_codesniffer": "^3.6"
3333
},
3434
"autoload": {
3535
"psr-4": {

composer.lock

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Exceptions/InlineKeyboardPaginationException.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,23 @@
1111
*/
1212
final class InlineKeyboardPaginationException extends Exception
1313
{
14+
public static function invalidMaxButtons(): self
15+
{
16+
return new self('Invalid max buttons, must be between 5 and 8.');
17+
}
18+
19+
public static function pageMustBeBetween(int $minPage, int $maxPage): self
20+
{
21+
return new self("Invalid page selected, must be between {$minPage} and {$maxPage}.");
22+
}
23+
24+
public static function invalidItemsPerPage(): self
25+
{
26+
return new self('Invalid number of items per page, must be at least 1.');
27+
}
28+
29+
public static function noItems(): self
30+
{
31+
return new self('Items list empty.');
32+
}
1433
}

src/InlineKeyboardPagination.php

Lines changed: 64 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -11,45 +11,14 @@
1111
*/
1212
class InlineKeyboardPagination implements InlineKeyboardPaginator
1313
{
14-
/**
15-
* @var int
16-
*/
17-
private $itemsPerPage;
18-
19-
/**
20-
* @var int
21-
*/
22-
private $maxButtons = 5;
23-
24-
/**
25-
* @var bool
26-
*/
27-
private $forceButtonCount = false;
28-
29-
/**
30-
* @var int
31-
*/
32-
private $selectedPage;
33-
34-
/**
35-
* @var array
36-
*/
37-
private $items;
38-
39-
/**
40-
* @var string
41-
*/
42-
private $command;
43-
44-
/**
45-
* @var string
46-
*/
47-
private $callbackDataFormat = 'command={COMMAND}&oldPage={OLD_PAGE}&newPage={NEW_PAGE}';
48-
49-
/**
50-
* @var array
51-
*/
52-
private $labels = [
14+
private array $items;
15+
private int $itemsPerPage;
16+
private int $selectedPage;
17+
private int $maxButtons = 5;
18+
private bool $forceButtonCount = false;
19+
private string $command;
20+
private string $callbackDataFormat = 'command={COMMAND}&oldPage={OLD_PAGE}&newPage={NEW_PAGE}';
21+
private array $labels = [
5322
'default' => '%d',
5423
'first' => '« %d',
5524
'previous' => '‹ %d',
@@ -68,7 +37,7 @@ class InlineKeyboardPagination implements InlineKeyboardPaginator
6837
public function setMaxButtons(int $maxButtons = 5, bool $forceButtonCount = false): InlineKeyboardPagination
6938
{
7039
if ($maxButtons < 5 || $maxButtons > 8) {
71-
throw new InlineKeyboardPaginationException('Invalid max buttons, must be between 5 and 8.');
40+
throw InlineKeyboardPaginationException::invalidMaxButtons();
7241
}
7342

7443
$this->maxButtons = $maxButtons;
@@ -143,10 +112,9 @@ public function setSelectedPage(int $selectedPage): InlineKeyboardPagination
143112
{
144113
$numberOfPages = $this->getNumberOfPages();
145114
if ($selectedPage < 1 || $selectedPage > $numberOfPages) {
146-
throw new InlineKeyboardPaginationException(
147-
'Invalid selected page, must be between 1 and ' . $numberOfPages
148-
);
115+
throw InlineKeyboardPaginationException::pageMustBeBetween(1, $numberOfPages);
149116
}
117+
150118
$this->selectedPage = $selectedPage;
151119

152120
return $this;
@@ -173,8 +141,9 @@ public function getItemsPerPage(): int
173141
public function setItemsPerPage(int $itemsPerPage): InlineKeyboardPagination
174142
{
175143
if ($itemsPerPage <= 0) {
176-
throw new InlineKeyboardPaginationException('Invalid number of items per page, must be at least 1');
144+
throw InlineKeyboardPaginationException::invalidItemsPerPage();
177145
}
146+
178147
$this->itemsPerPage = $itemsPerPage;
179148

180149
return $this;
@@ -191,7 +160,7 @@ public function setItemsPerPage(int $itemsPerPage): InlineKeyboardPagination
191160
public function setItems(array $items): InlineKeyboardPagination
192161
{
193162
if (empty($items)) {
194-
throw new InlineKeyboardPaginationException('Items list empty.');
163+
throw InlineKeyboardPaginationException::noItems();
195164
}
196165

197166
$this->items = $items;
@@ -250,28 +219,50 @@ public function getPagination(int $selectedPage = null): array
250219
*/
251220
protected function generateKeyboard(): array
252221
{
253-
$buttons = [];
222+
$buttons = $this->generateButtons();
223+
$buttons = $this->applyButtonLabels($buttons);
224+
225+
return array_values(array_filter($buttons));
226+
}
227+
228+
/**
229+
* Generate all buttons for this inline keyboard.
230+
*
231+
* @return array
232+
*/
233+
protected function generateButtons(): array
234+
{
254235
$numberOfPages = $this->getNumberOfPages();
255236

256-
if ($numberOfPages > $this->maxButtons) {
257-
$buttons[1] = $this->generateButton(1);
237+
$range = ['from' => 2, 'to' => $numberOfPages - 1];
258238

239+
if ($numberOfPages > $this->maxButtons) {
259240
$range = $this->generateRange();
260-
for ($i = $range['from']; $i < $range['to']; $i++) {
261-
$buttons[$i] = $this->generateButton($i);
262-
}
241+
}
263242

264-
$buttons[$numberOfPages] = $this->generateButton($numberOfPages);
265-
} else {
266-
for ($i = 1; $i <= $numberOfPages; $i++) {
267-
$buttons[$i] = $this->generateButton($i);
268-
}
243+
$buttons[1] = $this->generateButton(1);
244+
for ($i = $range['from']; $i <= $range['to']; $i++) {
245+
$buttons[$i] = $this->generateButton($i);
269246
}
247+
$buttons[$numberOfPages] = $this->generateButton($numberOfPages);
248+
249+
return $buttons;
250+
}
251+
252+
/**
253+
* Apply correct text labels to the keyboard buttons.
254+
*
255+
* @param array $buttons
256+
*
257+
* @return array
258+
*/
259+
protected function applyButtonLabels(array $buttons): array
260+
{
261+
$numberOfPages = $this->getNumberOfPages();
270262

271-
// Set the correct labels.
272263
foreach ($buttons as $page => &$button) {
273-
$inFirstBlock = $this->selectedPage <= 3 && $page <= 3;
274-
$inLastBlock = $this->selectedPage >= $numberOfPages - 2 && $page >= $numberOfPages - 2;
264+
$inFirstBlock = max($this->selectedPage, $page) <= 3;
265+
$inLastBlock = min($this->selectedPage, $page) >= $numberOfPages - 2;
275266

276267
$labelKey = 'next';
277268
if ($page === $this->selectedPage) {
@@ -288,6 +279,7 @@ protected function generateKeyboard(): array
288279

289280
$label = $this->labels[$labelKey] ?? '';
290281

282+
// Remove button for undefined labels.
291283
if ($label === '') {
292284
$button = null;
293285
continue;
@@ -296,7 +288,7 @@ protected function generateKeyboard(): array
296288
$button['text'] = sprintf($label, $page);
297289
}
298290

299-
return array_values(array_filter($buttons));
291+
return $buttons;
300292
}
301293

302294
/**
@@ -306,30 +298,32 @@ protected function generateKeyboard(): array
306298
*/
307299
protected function generateRange(): array
308300
{
309-
$numberOfIntermediateButtons = $this->maxButtons - 2;
301+
$numberOfIntermediateButtons = $this->maxButtons - 2; // Minus first and last buttons.
310302
$numberOfPages = $this->getNumberOfPages();
311303

312-
// @todo: Find a nicer solution for page 3
313304
$from = $this->selectedPage - 1;
314-
$to = $this->selectedPage + ($this->selectedPage === 3 ? $numberOfIntermediateButtons - 1 : 2);
305+
$to = $this->selectedPage + 1;
315306

316307
if ($this->selectedPage === 1) {
317308
$from = 2;
318-
$to = $this->maxButtons;
309+
$to = $this->maxButtons - 1;
319310
} elseif ($this->selectedPage === $numberOfPages) {
320311
$from = $numberOfPages - $numberOfIntermediateButtons;
321-
$to = $numberOfPages;
312+
$to = $numberOfPages - 1;
313+
} elseif ($this->selectedPage === 3) {
314+
// Special case because this button is in the center of a flexible pagination.
315+
$to += $numberOfIntermediateButtons - 3;
322316
} elseif ($this->selectedPage < 3) {
323317
// First half.
324318
$from = $this->selectedPage;
325-
$to = $this->selectedPage + $numberOfIntermediateButtons;
319+
$to = $this->selectedPage + $numberOfIntermediateButtons - 1;
326320
} elseif (($numberOfPages - $this->selectedPage) < 3) {
327321
// Last half.
328322
$from = $numberOfPages - $numberOfIntermediateButtons;
329-
$to = $numberOfPages;
323+
$to = $numberOfPages - 1;
330324
} elseif ($this->forceButtonCount) {
331-
$from = max(2, $this->selectedPage - floor($numberOfIntermediateButtons / 2));
332-
$to = $from + $numberOfIntermediateButtons;
325+
$from = (int) max(2, $this->selectedPage - floor($numberOfIntermediateButtons / 2));
326+
$to = $from + $numberOfIntermediateButtons - 1;
333327
}
334328

335329
return compact('from', 'to');
@@ -345,7 +339,7 @@ protected function generateRange(): array
345339
protected function generateButton(int $page): array
346340
{
347341
return [
348-
'text' => (string) $page,
342+
'text' => $page,
349343
'callback_data' => $this->generateCallbackData($page),
350344
];
351345
}

0 commit comments

Comments
 (0)