File: /var/www/html/phpmyfaq/src/phpMyFAQ/Pagination.php
<?php
/**
* Pagination handler class.
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/.
*
* @package phpMyFAQ
* @author Anatoliy Belsky <ab@php.net>
* @author Thorsten Rinne <thorsten@phpmyfaq.de>
* @copyright 2009-2022 phpMyFAQ Team
* @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
* @link https://www.phpmyfaq.de
* @since 2009-09-27
*/
namespace phpMyFAQ;
/**
* Class Pagination
*
* @package phpMyFAQ
*/
class Pagination
{
/**
* Template vars.
*/
private const TPL_VAR_LINK_URL = '{LINK_URL}';
private const TPL_VAR_LINK_TEXT = '{LINK_TEXT}';
private const TPL_VAR_LAYOUT_CONTENT = '{LAYOUT_CONTENT}';
/**
* Base url used for links.
*
* @var string
*/
protected $baseUrl = '';
/**
* Total items count.
*
* @var int
*/
protected $total = 0;
/**
* Items per page count.
*
* @var int
*/
protected $perPage = 0;
/**
* Number of adjacent links.
*
* @var int
*/
protected $adjacents = 4;
/**
* Default link template.
* Possible variables are {LINK}, {TITLE}, {TEXT}.
*
* @var string
*/
protected $linkTpl = '<li class="page-item"><a class="page-link" href="{LINK_URL}">{LINK_TEXT}</a></li>';
/**
* Current page link template.
*
* @var string
*/
protected $currentPageLinkTpl =
'<li class="page-item active"><a class="page-link" href="{LINK_URL}">{LINK_TEXT}</a></li>';
/**
* Next page link template.
*
* @var string
*/
protected $nextPageLinkTpl = '<li class="page-item"><a class="page-link" href="{LINK_URL}">→</a></li>';
/**
* Previous page link template.
*
* @var string
*/
protected $prevPageLinkTpl = '<li class="page-item"><a class="page-link" href="{LINK_URL}">←</a></li>';
/**
* First page link template.
*
* @var string
*/
protected $firstPageLinkTpl = '<li class="page-item"><a class="page-link" href="{LINK_URL}">⇤</a></li>';
/**
* Last page link template.
*
* @var string
*/
protected $lastPageLinkTpl = '<li class="page-item"><a class="page-link" href="{LINK_URL}">⇥</a></li>';
/**
* Layout template.
*
* @var string
*/
protected $layoutTpl = '<ul class="pagination justify-content-center">{LAYOUT_CONTENT}</ul>';
/**
* Current page index.
*
* @var int
*/
protected $currentPage = 0;
/**
* Param name to associate the page numbers to.
*
* @var string
*/
protected $pageParamName = 'page';
/**
* SEO name.
*
* @var string
*/
protected $seoName = '';
/**
* Use rewritten URLs without GET variables.
*
* @var bool
*/
protected $useRewrite = false;
/**
* Rewritten URL format for page param.
*
* @var string
*/
protected $rewriteUrl = '';
/**
* @var Configuration
*/
private $config;
/**
* Constructor.
*
* We read in the current page from the baseUrl, so if it contains
* no pageParamName, first page is assumed
*
* @param Configuration $config
* @param array<string|int|bool> $options initialization options,
* possible options: -
* baseUrl (default "") -
* total - perPage -
* linkTpl -
* currentPageLinkTpl -
* nextPageLinkTpl -
* prevPageLinkTpl -
* firstPageLinkTpl -
* lastPageLinkTpl -
* layoutTpl -
* pageParamName (default
* "page") - useRewrite
*/
public function __construct(Configuration $config, array $options = null)
{
$this->config = $config;
if (isset($options['baseUrl'])) {
$this->baseUrl = $options['baseUrl'];
}
if (isset($options['total'])) {
$this->total = $options['total'];
}
if (isset($options['perPage'])) {
$this->perPage = $options['perPage'];
}
if (isset($options['linkTpl'])) {
$this->linkTpl = $options['linkTpl'];
}
if (isset($options['currentPageLinkTpl'])) {
$this->currentPageLinkTpl = $options['currentPageLinkTpl'];
}
if (isset($options['nextPageLinkTpl'])) {
$this->nextPageLinkTpl = $options['nextPageLinkTpl'];
}
if (isset($options['prevPageLinkTpl'])) {
$this->prevPageLinkTpl = $options['prevPageLinkTpl'];
}
if (isset($options['firstPageLinkTpl'])) {
$this->firstPageLinkTpl = $options['firstPageLinkTpl'];
}
if (isset($options['lastPageLinkTpl'])) {
$this->lastPageLinkTpl = $options['lastPageLinkTpl'];
}
if (isset($options['layoutTpl'])) {
$this->layoutTpl = $options['layoutTpl'];
}
if (isset($options['pageParamName'])) {
$this->pageParamName = $options['pageParamName'];
}
if (isset($options['seoName'])) {
$this->seoName = $options['seoName'];
}
if (isset($options['useRewrite']) && isset($options['rewriteUrl'])) {
$this->useRewrite = $options['useRewrite'];
$this->rewriteUrl = $options['rewriteUrl'];
}
// Let this call to be last cuz it needs some options to be set before
$this->currentPage = $this->getCurrentPageFromUrl($this->baseUrl);
}
/**
* Returns the current page URL.
*
* @param string $url URL
*
* @return int
*/
protected function getCurrentPageFromUrl($url): int
{
$page = 1;
if (!empty($url)) {
$match = [];
if (Strings::preg_match('$&(amp;|)' . $this->pageParamName . '=(\d+)$', $url, $match)) {
$page = isset($match[2]) ? $match[2] : $page;
}
}
return $page;
}
/**
* Render full pagination string.
*
* @return string
*/
public function render(): string
{
$content = [];
$pages = ceil($this->total / $this->perPage);
$adjacents = floor($this->adjacents / 2) >= 1 ? floor($this->adjacents / 2) : 1;
for ($page = 1; $page <= $pages; ++$page) {
if ($page > $this->adjacents && $page < $this->currentPage - $adjacents) {
$content[] = '<li class="disabled"><a>…</a></li>';
$page = $this->currentPage - $adjacents - 1;
continue;
}
if ($page > $this->currentPage + $adjacents && $page <= $pages - $this->adjacents) {
$content[] = '<li class="disabled"><a>…</a></li>';
$page = $pages - $this->adjacents;
continue;
}
$link = $this->renderUrl($this->baseUrl, $page);
if ($page == $this->currentPage) {
$template = $this->currentPageLinkTpl;
} else {
$template = $this->linkTpl;
}
$content[] = $this->renderLink($template, $link, $page);
}
if (1 < $this->currentPage) {
array_unshift(
$content,
$this->renderLink(
$this->prevPageLinkTpl,
$this->renderUrl($this->baseUrl, $this->currentPage - 1),
$this->currentPage - 1
)
);
array_unshift(
$content,
$this->renderLink(
$this->firstPageLinkTpl,
$this->renderUrl($this->baseUrl, 1),
1
)
);
}
if ($page - 1 > $this->currentPage) {
array_push(
$content,
$this->renderLink(
$this->nextPageLinkTpl,
$this->renderUrl($this->baseUrl, $this->currentPage + 1),
$this->currentPage + 1
)
);
array_push(
$content,
$this->renderLink(
$this->lastPageLinkTpl,
$this->renderUrl($this->baseUrl, $page - 1),
$page - 1
)
);
}
return $this->renderLayout(implode(' ', $content));
}
/**
* Render url for a given page.
*
* @param string $url url
* @param int $page page number
*
* @return string
*/
protected function renderUrl($url, $page)
{
if ($this->useRewrite) {
$url = sprintf($this->rewriteUrl, $page);
} else {
$cleanedUrl = Strings::preg_replace(['$&(amp;|)' . $this->pageParamName . '=(\d+)$'], '', $url);
$url = sprintf('%s&%s=%d', $cleanedUrl, $this->pageParamName, $page);
}
return $url;
}
/**
* Render a link.
*
* @param string $tpl link template
* @param string $url url value for template container
* @param string|int|float $linkText text value for template container
* @return string
*/
protected function renderLink(string $tpl, string $url, $linkText): string
{
$search = [self::TPL_VAR_LINK_URL, self::TPL_VAR_LINK_TEXT];
$replace = [$url, $linkText];
return str_replace($search, $replace, $tpl);
}
/**
* Render the whole pagination layout.
*
* @param string $content layout contents
* @return string
*/
protected function renderLayout(string $content): string
{
return str_replace(self::TPL_VAR_LAYOUT_CONTENT, $content, $this->layoutTpl);
}
}