For a recent project I’ve been working I needed an easy-to-use Pagination class but after a scour around I couldn’t find anything that would do the job in the way I wanted to (Zend’s came close, as I’ve used it before, but it’s not lightweight enough for this project). I decided to write my own one instead.

The constructor takes a minimum of two arguments – an array of the whole set of data to be paginated, and the page of results to display. Optional arguments that can also be passed include an array of data to be included in the query string for each navigation link (most likely the $_GET predefined variable), the number of results to display per-page and how many links to include in the navigation (this value doesn’t affect the First/Last, Previous/Next navigation links):

This class will most probably not be of use if you are trying to paginate more that 1000 or so rows of data to paginate as doing it this way is bad practice (if pulling the data from a database you should be selecting your subset there, much more efficient).

As ever, let me know your thoughts!

<?php
/**
 * Pagination Class
 *
 * Class that will take an array of data and split it into the subsection to be returned, based on the values passed
 */
 class Pagination {
	public $data; // Original Data
	public $query_data; // Data on current query string

	public $page; // Current page of results to display
	public $pages; // Pages to include, dependant upon $page_range value
	public $page_count; // Total Number of Pages
	public $row_count; // Number of rows passed to class in Constructor

	public $first_page; // Show first page link
	public $last_page; // Show last page link
	public $previous_page; // Show previous page link
	public $next_page; // Show next page link

	public $range_start; // The page number to start the range link at
	public $page_range; // Show number of page links to show
	public $query_string; // Query string to use for the links

	public $subsection_of_data;

	/**
	 * Contructor
	 * @param array An array of data to be paginated
	 * @param int Page of data to return as subsection
	 * @param array An array of key/value-paired data for the query string for the Pagination navigation
	 * @param int The number of results to show on one page
	 * @param int Number of pages to display in the Pagination Navigation (Excluding First/Last, Previous/Next links)
	 * @return boolean
	 */
	public function __construct($data, $page, $query_data = array(), $per_page = 25, $page_range = 5)
	{
		$this->data = $data;
		$this->query_data = $query_data;

		$this->subsection_of_data = array();

		$this->pages = array();
		$this->page = $page;
		$this->per_page = $per_page;
		$this->page_range = $page_range;

		$this->process_query_string();
		$this->process_number_of_rows();
		$this->process_page_count();

		$this->process_first_page();
		$this->process_last_page();
		$this->process_previous_page();
		$this->process_next_page();
		$this->process_page_range();

		$this->process_subsection_of_data();
	}

	/**
	 * Count total number of items
	 * @return void
	 */
	public function process_number_of_rows()
	{
		$this->row_count = count($this->data);
	}

	/**
	 * Count total number of pages on data based on $per_page variable
	 * @return void
	 */
	public function process_page_count()
	{
		$this->page_count = floor($this->row_count / $this->per_page) + ($this->row_count % $this->per_page > 0 ? 1 : 0); // Number of Pages in Total
	}

	/**
	 * Determining whether to include "First Page" link in navigation
	 * @return void
	 */
	public function process_first_page()
	{
		$this->first_page = false;

		if ($this->page > 1) :
			$this->first_page = 1;
		endif;
	}

	/**
	 * Determining whether to include "Last Page" link in navigation
	 * @return void
	 */
	public function process_last_page()
	{
		$this->last_page = false;

		if ($this->page < $this->page_count) :
			$this->last_page = $this->page_count;
		endif;
	}

	/**
	 * Determining whether to include "Previous Page" link in navigation
	 * @return void
	 */
	public function process_previous_page()
	{
		$this->previous_page = false;

		if ($this->page > 1) :
			$this->previous_page = $this->page - 1;
		endif;
	}

	/**
	 * Determining whether to include "Next Page" link in navigation
	 * @return void
	 */
	public function process_next_page()
	{
		$this->next_page = false;

		if ($this->page < $this->page_count) :
			$this->next_page = $this->page + 1;
		endif;
	}

	/**
	 * Determining what page links to include in "Page Range" navigation
	 * @return void
	 */
	public function process_page_range()
	{
		$this->range_split = floor(($this->page_range - 1) / 2);

		$this->range_start = $this->page - $this->range_split;

		if ($this->range_start < 1) :
			$this->range_start = 1;
		endif;

		$i = $this->range_start;

		while ($i < ($this->range_start + $this->page_range)) :
			if ($i > 0 && $i <= $this->page_count) :
				$this->pages[] = $i;
			endif;
			$i++;
		endwhile;
	}

	/**
	 * Process query string to use on navigation links (removing the currently selected page, if set)
	 * @return void
	 */
	public function process_query_string()
	{
		$query_data = $this->query_data;

		if (isset($query_data['page'])) {
			unset($query_data['page']);
		}

		$this->query_string = http_build_query($query_data);
	}

	/**
	 * Return set of data to display, based on Page Number and Per Page variable
	 * @return void
	 */
	public function process_subsection_of_data()
	{
		$this->subsection_of_data = $this->data;

		$offset = $this->per_page * ($this->page - 1);
		$limit = $this->per_page;

		$this->subsection_of_data = array_slice($this->data, $offset, $limit);
	}

	/**
	 * Render standard navigation (echoes immediately)
	 * @return void
	 */
	public function render()
	{
		if ($this->page_count > 0): ?>
        	<div class="pagination">
            <?php
			if ($this->page_count > 1): ?>
                <div class="navigation">
				<?php
                if ($this->first_page) : ?>
                    <span class="item"><a href="?<?php echo $this->query_string; ?>&page=<?php echo $this->first_page; ?>">First</a></span>
                <?php
                endif;

                if ($this->previous_page) : ?>
                    <span class="item"><a href="?<?php echo $this->query_string; ?>&page=<?php echo $this->previous_page; ?>">Previous</a></span>
                <?php
                endif; 

                foreach ($this->pages as $page_number) :
                    if ($page_number == $this->page) : ?>
                        <span class="item range current"><?php echo $page_number; ?></span>
                <?php
                    else : ?>
                        <span class="item range"><a href="?<?php echo $this->query_string; ?>&page=<?php echo $page_number; ?>"><?php echo $page_number; ?></a></span>
                <?php
                    endif;
                endforeach; 

                if ($this->next_page) : ?>
                    <span class="item"><a href="?<?php echo $this->query_string; ?>&page=<?php echo $this->next_page; ?>">Next</a></span>
                <?php
                endif;

                if ($this->last_page) : ?>
                    <span class="item"><a href="?<?php echo $this->query_string; ?>&page=<?php echo $this->last_page; ?>">Last</a></span>
                <?php
                endif; ?>
            	</div>
               	<?php
			endif; ?>
            	<span class="summary">Page <?php echo $this->page; ?> of <?php echo $this->page_count; ?></span>
            	<div class="clear"></div>
            </div>
        <?php
		endif;
	}

	/**
	 * Render naviation based on the specified template
	 * @return void
	 */
	public function render_by_template($path)
	{
		if (file_exists($path)) {
			include($path);
		} else {
			echo 'Error: Pagination template cannot be found';
		}
	}
}

Example of use:

<?php
$data = array('Apple', 'Banana', 'Blackberry', 'Blackcurrant', 'Cherry', 'Clementine', 'Elderberry', 'Fig', 'Grape', 'Gooseberry', 'Juniper', 'Kiwi');
$get_data = $_GET;
$page = (int) $get_data['page'];

$pagination = new Pagination($data, $page, $get_data, 5, 5);

$pagination->render(); ?>

<table><?php
foreach ($pagination->subsection_of_data as $row) { ?>
	<tr>
		<td><?php echo $row; ?></td>
	</tr>
} ?>
</table><?php
$pagination->render(); ?>