Managing Pagination in PHP

A ready-to-go example for displaying pagination links

Introduction

This page presents a way of displaying page links with PHP in a user-friendly way. You can define how many pages to show around the current page.

Demo

Click on the following pages to see how the pagination is displayed for each page.
1 2 3 ... 20

Display with code:
This box contains code once JavaScript has loaded...

Code

The following code allows you to create a list of pages based on the current page of a user. As shown above, this just requires a call to create_pagination() with the current page and the maximum page as argument.

The output of the page links can be customized in make_page_link(), e.g. to add CSS attributes or to change the page parameter (currently set to a simple p). Set $page_span in create_pagination() to another value if you want to show more or less pages next to the current page. Right now, set to 2, it means that two neighboring pages are shown on the left and the right of the current page.

Sometimes one more page is shown than given in $page_span, namely when we would typically display a "..." to hide pages but only one page is hidden between the first or the last page (e.g. "1 ... 3 4 5"). This is consciously avoided since we might as well display the page in these cases (e.g. "1 2 3 4 5" instead).

/**
 * Creates the HTML for a page link.
 * @param int $page The page to create a link for
 * @param int $current_page The page the user is currently on
 * @return string HTML link to the given page
 */
function make_page_link($page$current_page) {
  if (
$page === $current_page) {
    return 
"<b>$page</b>";
  } else {
    return 
"<a href=\"?p=$page\">$page</a>";
  }
}

/**
 * Takes an array of page numbers to create links for and fills it with
 * additional entries where necessary (adding "..." or one intermediate page).
 * @param int[] $pages The pages to create links for
 * @param int $current_page The page the user is currently on
 * @return string The generated HTML displaying all page links
 */
function page_list_to_links($pages$current_page) {
  
$output '';
  
$previous_page 0;
  foreach (
$pages as $page) {
    if (
$page $previous_page 2) {
      
$output .= '... ';
    } else if (
$page $previous_page === 2) {
      
// Show the page instead of "..." if we're only hiding one page
      
$output .= make_page_link($page 1$current_page) . ' ';
    }
    
$output .= make_page_link($page$current_page) . ' ';
    
$previous_page $page;
  }
  return 
$output;
}

/**
 * Creates the HTML to display pagination links.
 * @param int $current_page The page to create a link for
 * @param int $max_page The maximum page
 * @return string The generated HTML displaying all page links
 */
function create_pagination($current_page$max_page) {
  
// Number of pages to show left & right of the current page
  
$page_span 2;

  
// middle: [3] [4] *5* [6] [7]
  
$middle_min max($current_page $page_span1);
  
$middle_max min($current_page $page_span$max_page);
  
$pages range($middle_min$middle_max);
  
  if (
$middle_min !== 1) {
    
array_unshift($pages1);
  }
  if (
$middle_max !== $max_page) {
    
$pages[] = $max_page;
  }
  return 
page_list_to_links($pages$current_page);
}

Current Page and Max Page

If you are struggling to get the current page or the maximum page programmatically on your website, define how many entries you want to show per page and then divide your total entries by that number and be sure to use ceil() and not round() on the result. For instance:
$max_page ceil($total_items $items_per_page);
The current page of the user can be fetched securely in conjunction with the useful filter_input() command that was introduced with PHP 5. For instance, if the page is passed with ?p in the URL:
$current_page filter_input(INPUT_GET'p'FILTER_VALIDATE_INT);
if (!
$current_page || $current_page <= || $current_page $max_page) {
  
$current_page 1;
}
Once you have your current page, you can compute how many entries you have to offset to get to the entries of the current page:
$items_to_skip $items_per_page * ($current_page 1);