PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/phpmyfaq/src/phpMyFAQ/Pagination.php

http://github.com/thorsten/phpMyFAQ
PHP | 379 lines | 164 code | 52 blank | 163 comment | 28 complexity | f24456349e67bfb44fb7e82d0e56e896 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, LGPL-3.0
  1. <?php
  2. /**
  3. * Pagination handler class.
  4. *
  5. * This Source Code Form is subject to the terms of the Mozilla Public License,
  6. * v. 2.0. If a copy of the MPL was not distributed with this file, You can
  7. * obtain one at http://mozilla.org/MPL/2.0/.
  8. *
  9. * @package phpMyFAQ
  10. * @author Anatoliy Belsky <ab@php.net>
  11. * @author Thorsten Rinne <thorsten@phpmyfaq.de>
  12. * @copyright 2009-2021 phpMyFAQ Team
  13. * @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
  14. * @link https://www.phpmyfaq.de
  15. * @since 2009-09-27
  16. */
  17. namespace phpMyFAQ;
  18. /**
  19. * Class Pagination
  20. *
  21. * @package phpMyFAQ
  22. */
  23. class Pagination
  24. {
  25. /**
  26. * Template vars.
  27. */
  28. private const TPL_VAR_LINK_URL = '{LINK_URL}';
  29. private const TPL_VAR_LINK_TEXT = '{LINK_TEXT}';
  30. private const TPL_VAR_LAYOUT_CONTENT = '{LAYOUT_CONTENT}';
  31. /**
  32. * Base url used for links.
  33. *
  34. * @var string
  35. */
  36. protected $baseUrl = '';
  37. /**
  38. * Total items count.
  39. *
  40. * @var int
  41. */
  42. protected $total = 0;
  43. /**
  44. * Items per page count.
  45. *
  46. * @var int
  47. */
  48. protected $perPage = 0;
  49. /**
  50. * Number of adjacent links.
  51. *
  52. * @var int
  53. */
  54. protected $adjacents = 4;
  55. /**
  56. * Default link template.
  57. * Possible variables are {LINK}, {TITLE}, {TEXT}.
  58. *
  59. * @var string
  60. */
  61. protected $linkTpl = '<li class="page-item"><a class="page-link" href="{LINK_URL}">{LINK_TEXT}</a></li>';
  62. /**
  63. * Current page link template.
  64. *
  65. * @var string
  66. */
  67. protected $currentPageLinkTpl =
  68. '<li class="page-item active"><a class="page-link" href="{LINK_URL}">{LINK_TEXT}</a></li>';
  69. /**
  70. * Next page link template.
  71. *
  72. * @var string
  73. */
  74. protected $nextPageLinkTpl = '<li class="page-item"><a class="page-link" href="{LINK_URL}">&rarr;</a></li>';
  75. /**
  76. * Previous page link template.
  77. *
  78. * @var string
  79. */
  80. protected $prevPageLinkTpl = '<li class="page-item"><a class="page-link" href="{LINK_URL}">&larr;</a></li>';
  81. /**
  82. * First page link template.
  83. *
  84. * @var string
  85. */
  86. protected $firstPageLinkTpl = '<li class="page-item"><a class="page-link" href="{LINK_URL}">&#8676;</a></li>';
  87. /**
  88. * Last page link template.
  89. *
  90. * @var string
  91. */
  92. protected $lastPageLinkTpl = '<li class="page-item"><a class="page-link" href="{LINK_URL}">&#8677;</a></li>';
  93. /**
  94. * Layout template.
  95. *
  96. * @var string
  97. */
  98. protected $layoutTpl = '<ul class="pagination justify-content-center">{LAYOUT_CONTENT}</ul>';
  99. /**
  100. * Current page index.
  101. *
  102. * @var int
  103. */
  104. protected $currentPage = 0;
  105. /**
  106. * Param name to associate the page numbers to.
  107. *
  108. * @var string
  109. */
  110. protected $pageParamName = 'page';
  111. /**
  112. * SEO name.
  113. *
  114. * @var string
  115. */
  116. protected $seoName = '';
  117. /**
  118. * Use rewritten URLs without GET variables.
  119. *
  120. * @var bool
  121. */
  122. protected $useRewrite = false;
  123. /**
  124. * Rewritten URL format for page param.
  125. *
  126. * @var string
  127. */
  128. protected $rewriteUrl = '';
  129. /**
  130. * @var Configuration
  131. */
  132. private $config;
  133. /**
  134. * Constructor.
  135. *
  136. * We read in the current page from the baseUrl, so if it contains
  137. * no pageParamName, first page is assumed
  138. *
  139. * @param Configuration $config
  140. * @param array<string|int|bool> $options initialization options,
  141. * possible options: -
  142. * baseUrl (default "") -
  143. * total - perPage -
  144. * linkTpl -
  145. * currentPageLinkTpl -
  146. * nextPageLinkTpl -
  147. * prevPageLinkTpl -
  148. * firstPageLinkTpl -
  149. * lastPageLinkTpl -
  150. * layoutTpl -
  151. * pageParamName (default
  152. * "page") - useRewrite
  153. */
  154. public function __construct(Configuration $config, array $options = null)
  155. {
  156. $this->config = $config;
  157. if (isset($options['baseUrl'])) {
  158. $this->baseUrl = $options['baseUrl'];
  159. }
  160. if (isset($options['total'])) {
  161. $this->total = $options['total'];
  162. }
  163. if (isset($options['perPage'])) {
  164. $this->perPage = $options['perPage'];
  165. }
  166. if (isset($options['linkTpl'])) {
  167. $this->linkTpl = $options['linkTpl'];
  168. }
  169. if (isset($options['currentPageLinkTpl'])) {
  170. $this->currentPageLinkTpl = $options['currentPageLinkTpl'];
  171. }
  172. if (isset($options['nextPageLinkTpl'])) {
  173. $this->nextPageLinkTpl = $options['nextPageLinkTpl'];
  174. }
  175. if (isset($options['prevPageLinkTpl'])) {
  176. $this->prevPageLinkTpl = $options['prevPageLinkTpl'];
  177. }
  178. if (isset($options['firstPageLinkTpl'])) {
  179. $this->firstPageLinkTpl = $options['firstPageLinkTpl'];
  180. }
  181. if (isset($options['lastPageLinkTpl'])) {
  182. $this->lastPageLinkTpl = $options['lastPageLinkTpl'];
  183. }
  184. if (isset($options['layoutTpl'])) {
  185. $this->layoutTpl = $options['layoutTpl'];
  186. }
  187. if (isset($options['pageParamName'])) {
  188. $this->pageParamName = $options['pageParamName'];
  189. }
  190. if (isset($options['seoName'])) {
  191. $this->seoName = $options['seoName'];
  192. }
  193. if (isset($options['useRewrite']) && isset($options['rewriteUrl'])) {
  194. $this->useRewrite = $options['useRewrite'];
  195. $this->rewriteUrl = $options['rewriteUrl'];
  196. }
  197. // Let this call to be last cuz it needs some options to be set before
  198. $this->currentPage = $this->getCurrentPageFromUrl($this->baseUrl);
  199. }
  200. /**
  201. * Returns the current page URL.
  202. *
  203. * @param string $url URL
  204. *
  205. * @return int
  206. */
  207. protected function getCurrentPageFromUrl($url): int
  208. {
  209. $page = 1;
  210. if (!empty($url)) {
  211. $match = [];
  212. if (Strings::preg_match('$&(amp;|)' . $this->pageParamName . '=(\d+)$', $url, $match)) {
  213. $page = isset($match[2]) ? $match[2] : $page;
  214. }
  215. }
  216. return $page;
  217. }
  218. /**
  219. * Render full pagination string.
  220. *
  221. * @return string
  222. */
  223. public function render(): string
  224. {
  225. $content = [];
  226. $pages = ceil($this->total / $this->perPage);
  227. $adjacents = floor($this->adjacents / 2) >= 1 ? floor($this->adjacents / 2) : 1;
  228. for ($page = 1; $page <= $pages; ++$page) {
  229. if ($page > $this->adjacents && $page < $this->currentPage - $adjacents) {
  230. $content[] = '<li class="disabled"><a>&hellip;</a></li>';
  231. $page = $this->currentPage - $adjacents - 1;
  232. continue;
  233. }
  234. if ($page > $this->currentPage + $adjacents && $page <= $pages - $this->adjacents) {
  235. $content[] = '<li class="disabled"><a>&hellip;</a></li>';
  236. $page = $pages - $this->adjacents;
  237. continue;
  238. }
  239. $link = $this->renderUrl($this->baseUrl, $page);
  240. if ($page == $this->currentPage) {
  241. $template = $this->currentPageLinkTpl;
  242. } else {
  243. $template = $this->linkTpl;
  244. }
  245. $content[] = $this->renderLink($template, $link, $page);
  246. }
  247. if (1 < $this->currentPage) {
  248. array_unshift(
  249. $content,
  250. $this->renderLink(
  251. $this->prevPageLinkTpl,
  252. $this->renderUrl($this->baseUrl, $this->currentPage - 1),
  253. $this->currentPage - 1
  254. )
  255. );
  256. array_unshift(
  257. $content,
  258. $this->renderLink(
  259. $this->firstPageLinkTpl,
  260. $this->renderUrl($this->baseUrl, 1),
  261. 1
  262. )
  263. );
  264. }
  265. if ($page - 1 > $this->currentPage) {
  266. array_push(
  267. $content,
  268. $this->renderLink(
  269. $this->nextPageLinkTpl,
  270. $this->renderUrl($this->baseUrl, $this->currentPage + 1),
  271. $this->currentPage + 1
  272. )
  273. );
  274. array_push(
  275. $content,
  276. $this->renderLink(
  277. $this->lastPageLinkTpl,
  278. $this->renderUrl($this->baseUrl, $page - 1),
  279. $page - 1
  280. )
  281. );
  282. }
  283. return $this->renderLayout(implode('&nbsp;&nbsp;', $content));
  284. }
  285. /**
  286. * Render url for a given page.
  287. *
  288. * @param string $url url
  289. * @param int $page page number
  290. *
  291. * @return string
  292. */
  293. protected function renderUrl($url, $page)
  294. {
  295. if ($this->useRewrite) {
  296. $url = sprintf($this->rewriteUrl, $page);
  297. } else {
  298. $cleanedUrl = Strings::preg_replace(['$&(amp;|)' . $this->pageParamName . '=(\d+)$'], '', $url);
  299. $url = sprintf('%s&amp;%s=%d', $cleanedUrl, $this->pageParamName, $page);
  300. }
  301. return $url;
  302. }
  303. /**
  304. * Render a link.
  305. *
  306. * @param string $tpl link template
  307. * @param string $url url value for template container
  308. * @param string|int|float $linkText text value for template container
  309. * @return string
  310. */
  311. protected function renderLink(string $tpl, string $url, $linkText): string
  312. {
  313. $search = [self::TPL_VAR_LINK_URL, self::TPL_VAR_LINK_TEXT];
  314. $replace = [$url, $linkText];
  315. return str_replace($search, $replace, $tpl);
  316. }
  317. /**
  318. * Render the whole pagination layout.
  319. *
  320. * @param string $content layout contents
  321. * @return string
  322. */
  323. protected function renderLayout(string $content): string
  324. {
  325. return str_replace(self::TPL_VAR_LAYOUT_CONTENT, $content, $this->layoutTpl);
  326. }
  327. }