1111 */
1212class 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