├── Adapter ├── AdapterInterface.php ├── ArrayAdapter.php ├── CallbackAdapter.php ├── ConcatenationAdapter.php ├── FixedAdapter.php └── NullAdapter.php ├── Exception ├── Exception.php ├── InvalidArgumentException.php ├── LessThan1CurrentPageException.php ├── LessThan1MaxPagesException.php ├── LessThan1MaxPerPageException.php ├── LogicException.php ├── NotBooleanException.php ├── NotIntegerCurrentPageException.php ├── NotIntegerException.php ├── NotIntegerMaxPerPageException.php ├── NotValidCurrentPageException.php ├── NotValidMaxPerPageException.php ├── OutOfBoundsException.php ├── OutOfRangeCurrentPageException.php ├── PagerfantaException.php └── RuntimeException.php ├── LICENSE ├── Pagerfanta.php ├── PagerfantaInterface.php ├── RouteGenerator ├── RouteGeneratorDecorator.php ├── RouteGeneratorFactoryInterface.php └── RouteGeneratorInterface.php ├── View ├── DefaultView.php ├── OptionableView.php ├── SemanticUiView.php ├── Template │ ├── DefaultTemplate.php │ ├── SemanticUiTemplate.php │ ├── Template.php │ ├── TemplateInterface.php │ ├── TwitterBootstrap3Template.php │ ├── TwitterBootstrap4Template.php │ ├── TwitterBootstrap5Template.php │ └── TwitterBootstrapTemplate.php ├── TemplateView.php ├── TwitterBootstrap3View.php ├── TwitterBootstrap4View.php ├── TwitterBootstrap5View.php ├── TwitterBootstrapView.php ├── View.php ├── ViewFactory.php ├── ViewFactoryInterface.php └── ViewInterface.php └── composer.json /Adapter/AdapterInterface.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | public function getSlice($offset, $length); 23 | } 24 | -------------------------------------------------------------------------------- /Adapter/ArrayAdapter.php: -------------------------------------------------------------------------------- 1 | array = $array; 18 | } 19 | 20 | /** 21 | * Retrieves the array of items. 22 | * 23 | * @return array 24 | */ 25 | public function getArray() 26 | { 27 | return $this->array; 28 | } 29 | 30 | /** 31 | * @return int 32 | */ 33 | public function getNbResults() 34 | { 35 | return \count($this->array); 36 | } 37 | 38 | /** 39 | * @param int $offset 40 | * @param int $length 41 | * 42 | * @return iterable 43 | */ 44 | public function getSlice($offset, $length) 45 | { 46 | return \array_slice($this->array, $offset, $length); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Adapter/CallbackAdapter.php: -------------------------------------------------------------------------------- 1 | nbResultsCallable = $nbResultsCallable; 41 | $this->sliceCallable = $sliceCallable; 42 | } 43 | 44 | /** 45 | * @return int 46 | */ 47 | public function getNbResults() 48 | { 49 | $callable = $this->nbResultsCallable; 50 | 51 | return $callable(); 52 | } 53 | 54 | /** 55 | * @param int $offset 56 | * @param int $length 57 | * 58 | * @return iterable 59 | */ 60 | public function getSlice($offset, $length) 61 | { 62 | $callable = $this->sliceCallable; 63 | 64 | return $callable($offset, $length); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Adapter/ConcatenationAdapter.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | protected $adapters; 16 | 17 | /** 18 | * Cache of the numbers of results of the adapters. The indexes correspond the indexes of the $adapters property. 19 | * 20 | * @var array|null 21 | */ 22 | protected $adaptersNbResultsCache = null; 23 | 24 | /** 25 | * @param array $adapters 26 | * 27 | * @throws InvalidArgumentException if an adapter is not a `Pagerfanta\Adapter\AdapterInterface` instance 28 | */ 29 | public function __construct(array $adapters) 30 | { 31 | foreach ($adapters as $adapter) { 32 | if (!($adapter instanceof AdapterInterface)) { 33 | throw new InvalidArgumentException(sprintf('The $adapters argument of the %s constructor expects all items to be an instance of %s.', self::class, AdapterInterface::class)); 34 | } 35 | } 36 | 37 | $this->adapters = $adapters; 38 | } 39 | 40 | /** 41 | * @return int 42 | */ 43 | public function getNbResults() 44 | { 45 | if (null === $this->adaptersNbResultsCache) { 46 | $this->refreshAdaptersNbResults(); 47 | } 48 | 49 | return array_sum($this->adaptersNbResultsCache); 50 | } 51 | 52 | /** 53 | * @param int $offset 54 | * @param int $length 55 | * 56 | * @return iterable 57 | */ 58 | public function getSlice($offset, $length) 59 | { 60 | if (null === $this->adaptersNbResultsCache) { 61 | $this->refreshAdaptersNbResults(); 62 | } 63 | 64 | $slice = []; 65 | $previousAdaptersNbResultsSum = 0; 66 | $requestFirstIndex = $offset; 67 | $requestLastIndex = $offset + $length - 1; 68 | 69 | foreach ($this->adapters as $index => $adapter) { 70 | $adapterNbResults = $this->adaptersNbResultsCache[$index]; 71 | $adapterFirstIndex = $previousAdaptersNbResultsSum; 72 | $adapterLastIndex = $adapterFirstIndex + $adapterNbResults - 1; 73 | 74 | $previousAdaptersNbResultsSum += $adapterNbResults; 75 | 76 | // The adapter is fully below the requested slice range — skip it 77 | if ($adapterLastIndex < $requestFirstIndex) { 78 | continue; 79 | } 80 | 81 | // The adapter is fully above the requested slice range — finish the gathering 82 | if ($adapterFirstIndex > $requestLastIndex) { 83 | break; 84 | } 85 | 86 | // Else the adapter range definitely intersects with the requested range 87 | $fetchOffset = $requestFirstIndex - $adapterFirstIndex; 88 | $fetchLength = $length; 89 | 90 | // The requested range start is below the adapter range start 91 | if ($fetchOffset < 0) { 92 | $fetchLength += $fetchOffset; 93 | $fetchOffset = 0; 94 | } 95 | 96 | // The requested range end is above the adapter range end 97 | if ($fetchOffset + $fetchLength > $adapterNbResults) { 98 | $fetchLength = $adapterNbResults - $fetchOffset; 99 | } 100 | 101 | // Getting the subslice from the adapter and adding it to the result slice 102 | $fetchSlice = $adapter->getSlice($fetchOffset, $fetchLength); 103 | 104 | foreach ($fetchSlice as $item) { 105 | $slice[] = $item; 106 | } 107 | } 108 | 109 | return $slice; 110 | } 111 | 112 | /** 113 | * Refreshes the cache of the numbers of results of the adapters. 114 | */ 115 | protected function refreshAdaptersNbResults(): void 116 | { 117 | if (null === $this->adaptersNbResultsCache) { 118 | $this->adaptersNbResultsCache = []; 119 | } 120 | 121 | foreach ($this->adapters as $index => $adapter) { 122 | $this->adaptersNbResultsCache[$index] = $adapter->getNbResults(); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Adapter/FixedAdapter.php: -------------------------------------------------------------------------------- 1 | nbResults = $nbResults; 29 | $this->results = $results; 30 | } 31 | 32 | /** 33 | * @return int 34 | */ 35 | public function getNbResults() 36 | { 37 | return $this->nbResults; 38 | } 39 | 40 | /** 41 | * @param int $offset 42 | * @param int $length 43 | * 44 | * @return iterable 45 | */ 46 | public function getSlice($offset, $length) 47 | { 48 | return $this->results; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Adapter/NullAdapter.php: -------------------------------------------------------------------------------- 1 | nbResults = (int) $nbResults; 21 | } 22 | 23 | /** 24 | * @return int 25 | */ 26 | public function getNbResults() 27 | { 28 | return $this->nbResults; 29 | } 30 | 31 | /** 32 | * The following methods are derived from code of the Zend Framework 33 | * Code subject to the new BSD license (http://framework.zend.com/license/new-bsd). 34 | * 35 | * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) 36 | * 37 | * @param int $offset 38 | * @param int $length 39 | * 40 | * @return iterable 41 | */ 42 | public function getSlice($offset, $length) 43 | { 44 | if ($offset >= $this->nbResults) { 45 | return []; 46 | } 47 | 48 | return $this->createNullArray($this->calculateNullArrayLength($offset, $length)); 49 | } 50 | 51 | /** 52 | * @param int $offset 53 | * @param int $length 54 | */ 55 | private function calculateNullArrayLength($offset, $length): int 56 | { 57 | $remainCount = $this->remainCount($offset); 58 | 59 | if ($length > $remainCount) { 60 | return $remainCount; 61 | } 62 | 63 | return $length; 64 | } 65 | 66 | /** 67 | * @param int $offset 68 | */ 69 | private function remainCount($offset): int 70 | { 71 | return $this->nbResults - $offset; 72 | } 73 | 74 | private function createNullArray(int $length): array 75 | { 76 | return array_fill(0, $length, null); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Exception/Exception.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | class Pagerfanta implements \Countable, \IteratorAggregate, \JsonSerializable, PagerfantaInterface 22 | { 23 | /** 24 | * @var AdapterInterface 25 | */ 26 | private $adapter; 27 | 28 | /** 29 | * @var bool 30 | */ 31 | private $allowOutOfRangePages = false; 32 | 33 | /** 34 | * @var bool 35 | */ 36 | private $normalizeOutOfRangePages = false; 37 | 38 | /** 39 | * @var int 40 | */ 41 | private $maxPerPage = 10; 42 | 43 | /** 44 | * @var int 45 | */ 46 | private $currentPage = 1; 47 | 48 | /** 49 | * @var int|null 50 | */ 51 | private $nbResults; 52 | 53 | /** 54 | * @var int|null 55 | */ 56 | private $maxNbPages; 57 | 58 | /** 59 | * @var iterable|null 60 | */ 61 | private $currentPageResults; 62 | 63 | public function __construct(AdapterInterface $adapter) 64 | { 65 | $this->adapter = $adapter; 66 | } 67 | 68 | /** 69 | * @return AdapterInterface 70 | */ 71 | public function getAdapter() 72 | { 73 | return $this->adapter; 74 | } 75 | 76 | /** 77 | * @param bool $allowOutOfRangePages 78 | * 79 | * @return $this 80 | * 81 | * @throws NotBooleanException if the value is not boolean 82 | */ 83 | public function setAllowOutOfRangePages($allowOutOfRangePages) 84 | { 85 | $this->allowOutOfRangePages = $this->filterBoolean($allowOutOfRangePages); 86 | 87 | return $this; 88 | } 89 | 90 | /** 91 | * @return bool 92 | */ 93 | public function getAllowOutOfRangePages() 94 | { 95 | return $this->allowOutOfRangePages; 96 | } 97 | 98 | /** 99 | * @param bool $normalizeOutOfRangePages 100 | * 101 | * @return $this 102 | * 103 | * @throws NotBooleanException if the value is not boolean 104 | */ 105 | public function setNormalizeOutOfRangePages($normalizeOutOfRangePages) 106 | { 107 | $this->normalizeOutOfRangePages = $this->filterBoolean($normalizeOutOfRangePages); 108 | 109 | return $this; 110 | } 111 | 112 | /** 113 | * @return bool 114 | */ 115 | public function getNormalizeOutOfRangePages() 116 | { 117 | return $this->normalizeOutOfRangePages; 118 | } 119 | 120 | /** 121 | * Sets the maximum number of items per page. 122 | * 123 | * Tries to convert from string and float. 124 | * 125 | * @param int $maxPerPage 126 | * 127 | * @return $this 128 | * 129 | * @throws NotIntegerMaxPerPageException if the max per page is not an integer even converting 130 | * @throws LessThan1MaxPerPageException if the max per page is less than 1 131 | */ 132 | public function setMaxPerPage($maxPerPage) 133 | { 134 | $this->maxPerPage = $this->filterMaxPerPage($maxPerPage); 135 | $this->resetForMaxPerPageChange(); 136 | 137 | return $this; 138 | } 139 | 140 | /** 141 | * @param int $maxPerPage 142 | */ 143 | private function filterMaxPerPage($maxPerPage): int 144 | { 145 | $maxPerPage = $this->toInteger($maxPerPage); 146 | $this->checkMaxPerPage($maxPerPage); 147 | 148 | return $maxPerPage; 149 | } 150 | 151 | /** 152 | * @param int $maxPerPage 153 | * 154 | * @throws NotIntegerMaxPerPageException if the max per page is not an integer even converting 155 | * @throws LessThan1MaxPerPageException if the max per page is less than 1 156 | */ 157 | private function checkMaxPerPage($maxPerPage): void 158 | { 159 | if (!\is_int($maxPerPage)) { 160 | throw new NotIntegerMaxPerPageException(); 161 | } 162 | 163 | if ($maxPerPage < 1) { 164 | throw new LessThan1MaxPerPageException(); 165 | } 166 | } 167 | 168 | private function resetForMaxPerPageChange(): void 169 | { 170 | $this->currentPageResults = null; 171 | } 172 | 173 | /** 174 | * @return int 175 | */ 176 | public function getMaxPerPage() 177 | { 178 | return $this->maxPerPage; 179 | } 180 | 181 | /** 182 | * Sets the current page. 183 | * 184 | * Tries to convert from string and float. 185 | * 186 | * @param int $currentPage 187 | * 188 | * @return $this 189 | * 190 | * @throws NotIntegerCurrentPageException if the current page is not an integer even converting 191 | * @throws LessThan1CurrentPageException if the current page is less than 1 192 | * @throws OutOfRangeCurrentPageException if It is not allowed out of range pages and they are not normalized 193 | */ 194 | public function setCurrentPage($currentPage) 195 | { 196 | if (\count(\func_get_args()) > 1) { 197 | $this->useDeprecatedCurrentPageBooleanArguments(\func_get_args()); 198 | } 199 | 200 | $this->currentPage = $this->filterCurrentPage($currentPage); 201 | $this->resetForCurrentPageChange(); 202 | 203 | return $this; 204 | } 205 | 206 | private function useDeprecatedCurrentPageBooleanArguments(array $arguments): void 207 | { 208 | $this->useDeprecatedCurrentPageAllowOutOfRangePagesBooleanArgument($arguments); 209 | $this->useDeprecatedCurrentPageNormalizeOutOfRangePagesBooleanArgument($arguments); 210 | } 211 | 212 | private function useDeprecatedCurrentPageAllowOutOfRangePagesBooleanArgument(array $arguments): void 213 | { 214 | $this->useDeprecatedBooleanArgument($arguments, 1, 'setAllowOutOfRangePages', '$allowOutOfRangePages'); 215 | } 216 | 217 | private function useDeprecatedCurrentPageNormalizeOutOfRangePagesBooleanArgument(array $arguments): void 218 | { 219 | $this->useDeprecatedBooleanArgument($arguments, 2, 'setNormalizeOutOfRangePages', '$normalizeOutOfRangePages'); 220 | } 221 | 222 | private function useDeprecatedBooleanArgument(array $arguments, int $index, string $method, string $oldArgument): void 223 | { 224 | if (isset($arguments[$index])) { 225 | trigger_deprecation( 226 | 'pagerfanta/pagerfanta', 227 | '2.2', 228 | 'The %1$s argument of %2$s::setCurrentPage() is deprecated and will no longer be supported in 3.0. Use the %2$s::%3$s() method instead.', 229 | $oldArgument, 230 | self::class, 231 | self::class, 232 | $method 233 | ); 234 | 235 | $this->$method($arguments[$index]); 236 | } 237 | } 238 | 239 | /** 240 | * @param int $currentPage 241 | */ 242 | private function filterCurrentPage($currentPage): int 243 | { 244 | $currentPage = $this->toInteger($currentPage); 245 | $this->checkCurrentPage($currentPage); 246 | $currentPage = $this->filterOutOfRangeCurrentPage($currentPage); 247 | 248 | return $currentPage; 249 | } 250 | 251 | /** 252 | * @param int $currentPage 253 | * 254 | * @throws NotIntegerCurrentPageException if the current page is not an integer even converting 255 | * @throws LessThan1CurrentPageException if the current page is less than 1 256 | */ 257 | private function checkCurrentPage($currentPage): void 258 | { 259 | if (!\is_int($currentPage)) { 260 | throw new NotIntegerCurrentPageException(); 261 | } 262 | 263 | if ($currentPage < 1) { 264 | throw new LessThan1CurrentPageException(); 265 | } 266 | } 267 | 268 | /** 269 | * @param int $currentPage 270 | */ 271 | private function filterOutOfRangeCurrentPage($currentPage): int 272 | { 273 | if ($this->notAllowedCurrentPageOutOfRange($currentPage)) { 274 | return $this->normalizeOutOfRangeCurrentPage($currentPage); 275 | } 276 | 277 | return $currentPage; 278 | } 279 | 280 | private function notAllowedCurrentPageOutOfRange(int $currentPage): bool 281 | { 282 | return !$this->getAllowOutOfRangePages() && $this->currentPageOutOfRange($currentPage); 283 | } 284 | 285 | private function currentPageOutOfRange(int $currentPage): bool 286 | { 287 | return $currentPage > 1 && $currentPage > $this->getNbPages(); 288 | } 289 | 290 | /** 291 | * @param int $currentPage 292 | * 293 | * @throws OutOfRangeCurrentPageException if the page should not be normalized 294 | */ 295 | private function normalizeOutOfRangeCurrentPage($currentPage): int 296 | { 297 | if ($this->getNormalizeOutOfRangePages()) { 298 | return $this->getNbPages(); 299 | } 300 | 301 | throw new OutOfRangeCurrentPageException(sprintf('Page "%d" does not exist. The currentPage must be inferior to "%d"', $currentPage, $this->getNbPages())); 302 | } 303 | 304 | private function resetForCurrentPageChange(): void 305 | { 306 | $this->currentPageResults = null; 307 | } 308 | 309 | /** 310 | * @return int 311 | */ 312 | public function getCurrentPage() 313 | { 314 | return $this->currentPage; 315 | } 316 | 317 | /** 318 | * @return iterable 319 | */ 320 | public function getCurrentPageResults() 321 | { 322 | if (null === $this->currentPageResults) { 323 | $this->currentPageResults = $this->getCurrentPageResultsFromAdapter(); 324 | } 325 | 326 | return $this->currentPageResults; 327 | } 328 | 329 | /** 330 | * @return iterable 331 | */ 332 | private function getCurrentPageResultsFromAdapter(): iterable 333 | { 334 | $offset = $this->calculateOffsetForCurrentPageResults(); 335 | $length = $this->getMaxPerPage(); 336 | 337 | return $this->adapter->getSlice($offset, $length); 338 | } 339 | 340 | private function calculateOffsetForCurrentPageResults(): int 341 | { 342 | return ($this->getCurrentPage() - 1) * $this->getMaxPerPage(); 343 | } 344 | 345 | /** 346 | * Calculates the current page offset start. 347 | * 348 | * @return int 349 | */ 350 | public function getCurrentPageOffsetStart() 351 | { 352 | return $this->getNbResults() ? $this->calculateOffsetForCurrentPageResults() + 1 : 0; 353 | } 354 | 355 | /** 356 | * Calculates the current page offset end. 357 | * 358 | * @return int 359 | */ 360 | public function getCurrentPageOffsetEnd() 361 | { 362 | return $this->hasNextPage() ? $this->getCurrentPage() * $this->getMaxPerPage() : $this->getNbResults(); 363 | } 364 | 365 | /** 366 | * @return int 367 | */ 368 | public function getNbResults() 369 | { 370 | if (null === $this->nbResults) { 371 | $this->nbResults = $this->getAdapter()->getNbResults(); 372 | } 373 | 374 | return $this->nbResults; 375 | } 376 | 377 | /** 378 | * @return int 379 | */ 380 | public function getNbPages() 381 | { 382 | $nbPages = $this->calculateNbPages(); 383 | 384 | if (0 === $nbPages) { 385 | return $this->minimumNbPages(); 386 | } 387 | 388 | if (null !== $this->maxNbPages && $this->maxNbPages < $nbPages) { 389 | return $this->maxNbPages; 390 | } 391 | 392 | return $nbPages; 393 | } 394 | 395 | private function calculateNbPages(): int 396 | { 397 | return (int) ceil($this->getNbResults() / $this->getMaxPerPage()); 398 | } 399 | 400 | private function minimumNbPages(): int 401 | { 402 | return 1; 403 | } 404 | 405 | /** 406 | * @return $this 407 | * 408 | * @throws LessThan1MaxPagesException if the max number of pages is less than 1 409 | */ 410 | public function setMaxNbPages(int $maxNbPages): self 411 | { 412 | if ($maxNbPages < 1) { 413 | throw new LessThan1MaxPagesException(); 414 | } 415 | 416 | $this->maxNbPages = $maxNbPages; 417 | 418 | return $this; 419 | } 420 | 421 | /** 422 | * @return $this 423 | */ 424 | public function resetMaxNbPages(): self 425 | { 426 | $this->maxNbPages = null; 427 | 428 | return $this; 429 | } 430 | 431 | /** 432 | * @return bool 433 | */ 434 | public function haveToPaginate() 435 | { 436 | return $this->getNbResults() > $this->maxPerPage; 437 | } 438 | 439 | /** 440 | * @return bool 441 | */ 442 | public function hasPreviousPage() 443 | { 444 | return $this->currentPage > 1; 445 | } 446 | 447 | /** 448 | * @return int 449 | * 450 | * @throws LogicException if there is no previous page 451 | */ 452 | public function getPreviousPage() 453 | { 454 | if (!$this->hasPreviousPage()) { 455 | throw new LogicException('There is no previous page.'); 456 | } 457 | 458 | return $this->currentPage - 1; 459 | } 460 | 461 | /** 462 | * @return bool 463 | */ 464 | public function hasNextPage() 465 | { 466 | return $this->currentPage < $this->getNbPages(); 467 | } 468 | 469 | /** 470 | * @return int 471 | * 472 | * @throws LogicException if there is no next page 473 | */ 474 | public function getNextPage() 475 | { 476 | if (!$this->hasNextPage()) { 477 | throw new LogicException('There is no next page.'); 478 | } 479 | 480 | return $this->currentPage + 1; 481 | } 482 | 483 | /** 484 | * @return int 485 | */ 486 | #[\ReturnTypeWillChange] 487 | public function count() 488 | { 489 | return $this->getNbResults(); 490 | } 491 | 492 | /** 493 | * @return \Traversable 494 | */ 495 | #[\ReturnTypeWillChange] 496 | public function getIterator() 497 | { 498 | $results = $this->getCurrentPageResults(); 499 | 500 | if ($results instanceof \Iterator) { 501 | return $results; 502 | } 503 | 504 | if ($results instanceof \IteratorAggregate) { 505 | return $results->getIterator(); 506 | } 507 | 508 | if (\is_array($results)) { 509 | return new \ArrayIterator($results); 510 | } 511 | 512 | throw new \InvalidArgumentException(sprintf('Cannot create iterator with page results of type "%s".', get_debug_type($results))); 513 | } 514 | 515 | /** 516 | * @return iterable 517 | */ 518 | #[\ReturnTypeWillChange] 519 | public function jsonSerialize() 520 | { 521 | $results = $this->getCurrentPageResults(); 522 | 523 | if ($results instanceof \Traversable) { 524 | return iterator_to_array($results); 525 | } 526 | 527 | return $results; 528 | } 529 | 530 | /** 531 | * Get page number of the item at specified position (1-based index). 532 | * 533 | * @param int $position 534 | * 535 | * @return int 536 | * 537 | * @throws NotIntegerException if the position is not an integer 538 | * @throws OutOfBoundsException if the item is outside the result set 539 | */ 540 | public function getPageNumberForItemAtPosition($position) 541 | { 542 | if (!\is_int($position)) { 543 | throw new NotIntegerException(); 544 | } 545 | 546 | if ($this->getNbResults() < $position) { 547 | throw new OutOfBoundsException(sprintf('Item requested at position %d, but there are only %d items.', $position, $this->getNbResults())); 548 | } 549 | 550 | return (int) ceil($position / $this->getMaxPerPage()); 551 | } 552 | 553 | /** 554 | * @param bool $value 555 | * 556 | * @throws NotBooleanException if the value is not boolean 557 | */ 558 | private function filterBoolean($value): bool 559 | { 560 | if (!\is_bool($value)) { 561 | throw new NotBooleanException(); 562 | } 563 | 564 | return $value; 565 | } 566 | 567 | /** 568 | * @param int|float|string $value 569 | * 570 | * @return int 571 | */ 572 | private function toInteger($value) 573 | { 574 | if ($this->needsToIntegerConversion($value)) { 575 | return (int) $value; 576 | } 577 | 578 | return $value; 579 | } 580 | 581 | /** 582 | * @param int|float|string $value 583 | */ 584 | private function needsToIntegerConversion($value): bool 585 | { 586 | return (\is_string($value) || \is_float($value)) && (int) $value == $value; 587 | } 588 | } 589 | -------------------------------------------------------------------------------- /PagerfantaInterface.php: -------------------------------------------------------------------------------- 1 | decorated = $decorated; 19 | } 20 | 21 | public function __invoke(int $page): string 22 | { 23 | return $this->route($page); 24 | } 25 | 26 | public function route(int $page): string 27 | { 28 | $decorated = $this->decorated; 29 | 30 | return $decorated($page); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /RouteGenerator/RouteGeneratorFactoryInterface.php: -------------------------------------------------------------------------------- 1 | view = $view; 26 | $this->defaultOptions = $defaultOptions; 27 | } 28 | 29 | /** 30 | * @throws InvalidArgumentException if the $routeGenerator is not a callable 31 | */ 32 | public function render(PagerfantaInterface $pagerfanta, $routeGenerator, array $options = []) 33 | { 34 | if (!\is_callable($routeGenerator)) { 35 | throw new InvalidArgumentException(sprintf('The $routeGenerator argument of %s() must be a callable, %s given.', __METHOD__, get_debug_type($routeGenerator))); 36 | } 37 | 38 | return $this->view->render($pagerfanta, $routeGenerator, array_merge($this->defaultOptions, $options)); 39 | } 40 | 41 | public function getName() 42 | { 43 | return 'optionable'; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /View/SemanticUiView.php: -------------------------------------------------------------------------------- 1 | 'Previous', 12 | 'next_message' => 'Next', 13 | 'css_disabled_class' => 'disabled', 14 | 'css_dots_class' => 'dots', 15 | 'css_current_class' => 'current', 16 | 'dots_text' => '...', 17 | 'container_template' => '', 18 | 'page_template' => '%text%', 19 | 'span_template' => '%text%', 20 | 'rel_previous' => 'prev', 21 | 'rel_next' => 'next', 22 | ]; 23 | 24 | public function container(): string 25 | { 26 | return $this->option('container_template'); 27 | } 28 | 29 | /** 30 | * @param int $page 31 | */ 32 | public function page($page): string 33 | { 34 | return $this->pageWithText($page, (string) $page); 35 | } 36 | 37 | /** 38 | * @param int $page 39 | * @param string $text 40 | * @param string|null $rel 41 | */ 42 | public function pageWithText($page, $text, $rel = null): string 43 | { 44 | $href = $this->generateRoute($page); 45 | $replace = $rel ? [$href, $text, ' rel="'.$rel.'"'] : [$href, $text, '']; 46 | 47 | return str_replace(['%href%', '%text%', '%rel%'], $replace, $this->option('page_template')); 48 | } 49 | 50 | public function previousDisabled(): string 51 | { 52 | return $this->generateSpan($this->option('css_disabled_class'), $this->option('prev_message')); 53 | } 54 | 55 | /** 56 | * @param int $page 57 | */ 58 | public function previousEnabled($page): string 59 | { 60 | return $this->pageWithText($page, $this->option('prev_message'), $this->option('rel_previous')); 61 | } 62 | 63 | public function nextDisabled(): string 64 | { 65 | return $this->generateSpan($this->option('css_disabled_class'), $this->option('next_message')); 66 | } 67 | 68 | /** 69 | * @param int $page 70 | */ 71 | public function nextEnabled($page): string 72 | { 73 | return $this->pageWithText($page, $this->option('next_message'), $this->option('rel_next')); 74 | } 75 | 76 | public function first(): string 77 | { 78 | return $this->page(1); 79 | } 80 | 81 | /** 82 | * @param int $page 83 | */ 84 | public function last($page): string 85 | { 86 | return $this->page($page); 87 | } 88 | 89 | /** 90 | * @param int $page 91 | */ 92 | public function current($page): string 93 | { 94 | return $this->generateSpan($this->option('css_current_class'), $page); 95 | } 96 | 97 | public function separator(): string 98 | { 99 | return $this->generateSpan($this->option('css_dots_class'), $this->option('dots_text')); 100 | } 101 | 102 | /** 103 | * @param int|string $page 104 | */ 105 | private function generateSpan(string $class, $page): string 106 | { 107 | return str_replace(['%class%', '%text%'], [$class, $page], $this->option('span_template')); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /View/Template/SemanticUiTemplate.php: -------------------------------------------------------------------------------- 1 | '← Previous', 12 | 'next_message' => 'Next →', 13 | 'dots_message' => '…', 14 | 'active_suffix' => '', 15 | 'css_container_class' => 'ui stackable fluid pagination menu', 16 | 'css_item_class' => 'item', 17 | 'css_prev_class' => 'prev', 18 | 'css_next_class' => 'next', 19 | 'css_disabled_class' => 'disabled', 20 | 'css_dots_class' => 'disabled', 21 | 'css_active_class' => 'active', 22 | ]; 23 | 24 | public function container(): string 25 | { 26 | return sprintf('
%%pages%%
', 27 | $this->option('css_container_class') 28 | ); 29 | } 30 | 31 | /** 32 | * @param int $page 33 | */ 34 | public function page($page): string 35 | { 36 | return $this->pageWithText($page, (string) $page); 37 | } 38 | 39 | /** 40 | * @param int $page 41 | * @param string $text 42 | */ 43 | public function pageWithText($page, $text, ?string $rel = null): string 44 | { 45 | return $this->pageWithTextAndClass($page, $text, ''); 46 | } 47 | 48 | /** 49 | * @param int $page 50 | * @param string $text 51 | * @param string $class 52 | */ 53 | private function pageWithTextAndClass($page, $text, $class): string 54 | { 55 | return $this->link($class, $this->generateRoute($page), $text); 56 | } 57 | 58 | public function previousDisabled(): string 59 | { 60 | return $this->div($this->previousDisabledClass(), $this->option('prev_message')); 61 | } 62 | 63 | private function previousDisabledClass(): string 64 | { 65 | return $this->option('css_prev_class').' '.$this->option('css_disabled_class'); 66 | } 67 | 68 | /** 69 | * @param int $page 70 | */ 71 | public function previousEnabled($page): string 72 | { 73 | return $this->pageWithTextAndClass($page, $this->option('prev_message'), $this->option('css_prev_class')); 74 | } 75 | 76 | public function nextDisabled(): string 77 | { 78 | return $this->div($this->nextDisabledClass(), $this->option('next_message')); 79 | } 80 | 81 | private function nextDisabledClass(): string 82 | { 83 | return $this->option('css_next_class').' '.$this->option('css_disabled_class'); 84 | } 85 | 86 | /** 87 | * @param int $page 88 | */ 89 | public function nextEnabled($page): string 90 | { 91 | return $this->pageWithTextAndClass($page, $this->option('next_message'), $this->option('css_next_class')); 92 | } 93 | 94 | public function first(): string 95 | { 96 | return $this->page(1); 97 | } 98 | 99 | /** 100 | * @param int $page 101 | */ 102 | public function last($page): string 103 | { 104 | return $this->page($page); 105 | } 106 | 107 | /** 108 | * @param int $page 109 | */ 110 | public function current($page): string 111 | { 112 | $text = trim($page.' '.$this->option('active_suffix')); 113 | 114 | return $this->div($this->option('css_active_class'), $text); 115 | } 116 | 117 | public function separator(): string 118 | { 119 | return $this->div($this->option('css_dots_class'), $this->option('dots_message')); 120 | } 121 | 122 | /** 123 | * @param int|string $text 124 | */ 125 | private function link(string $class, string $href, $text): string 126 | { 127 | return sprintf('%s', $this->option('css_item_class'), $class, $href, $text); 128 | } 129 | 130 | /** 131 | * @param int|string $text 132 | */ 133 | private function div(string $class, $text): string 134 | { 135 | return sprintf('
%s
', $this->option('css_item_class'), $class, $text); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /View/Template/Template.php: -------------------------------------------------------------------------------- 1 | options = $this->getDefaultOptions(); 30 | } 31 | 32 | /** 33 | * @param callable $routeGenerator 34 | * 35 | * @throws InvalidArgumentException if the route generator is not a callable 36 | */ 37 | public function setRouteGenerator($routeGenerator): void 38 | { 39 | if (!\is_callable($routeGenerator)) { 40 | throw new InvalidArgumentException(sprintf('The $routeGenerator argument of %s() must be a callable, %s given.', __METHOD__, get_debug_type($routeGenerator))); 41 | } 42 | 43 | $this->routeGenerator = $routeGenerator; 44 | } 45 | 46 | public function setOptions(array $options): void 47 | { 48 | $this->options = array_merge($this->options, $options); 49 | } 50 | 51 | /** 52 | * Generate the route (URL) for the given page. 53 | * 54 | * @param int $page 55 | * 56 | * @return string 57 | */ 58 | protected function generateRoute($page) 59 | { 60 | $generator = $this->getRouteGenerator(); 61 | 62 | return $generator($page); 63 | } 64 | 65 | protected function getDefaultOptions(): array 66 | { 67 | return static::$defaultOptions; 68 | } 69 | 70 | /** 71 | * @throws RuntimeException if the route generator has not been set 72 | */ 73 | private function getRouteGenerator(): callable 74 | { 75 | if (!$this->routeGenerator) { 76 | throw new RuntimeException(sprintf('The route generator was not set to the template, ensure you call %s::setRouteGenerator().', static::class)); 77 | } 78 | 79 | return $this->routeGenerator; 80 | } 81 | 82 | /** 83 | * @param string $name The name of the option to look up 84 | * 85 | * @return mixed The option value if it exists 86 | * 87 | * @throws InvalidArgumentException if the option does not exist 88 | */ 89 | protected function option($name) 90 | { 91 | if (!isset($this->options[$name])) { 92 | throw new InvalidArgumentException(sprintf('The option "%s" does not exist.', $name)); 93 | } 94 | 95 | return $this->options[$name]; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /View/Template/TemplateInterface.php: -------------------------------------------------------------------------------- 1 | '(current)', 13 | ] 14 | ); 15 | } 16 | 17 | public function container(): string 18 | { 19 | return sprintf('
    %%pages%%
', 20 | $this->option('css_container_class') 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /View/Template/TwitterBootstrap4Template.php: -------------------------------------------------------------------------------- 1 | %s', $liClass, $href, $rel, $text); 19 | } 20 | 21 | /** 22 | * @param string $class 23 | * @param string $text 24 | */ 25 | protected function spanLi($class, $text): string 26 | { 27 | $liClass = implode(' ', array_filter(['page-item', $class])); 28 | 29 | return sprintf('
  • %s
  • ', $liClass, $text); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /View/Template/TwitterBootstrap5Template.php: -------------------------------------------------------------------------------- 1 | '(current)', 13 | ] 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /View/Template/TwitterBootstrapTemplate.php: -------------------------------------------------------------------------------- 1 | '← Previous', 12 | 'next_message' => 'Next →', 13 | 'dots_message' => '…', 14 | 'active_suffix' => '', 15 | 'css_container_class' => 'pagination', 16 | 'css_prev_class' => 'prev', 17 | 'css_next_class' => 'next', 18 | 'css_disabled_class' => 'disabled', 19 | 'css_dots_class' => 'disabled', 20 | 'css_active_class' => 'active', 21 | 'rel_previous' => 'prev', 22 | 'rel_next' => 'next', 23 | ]; 24 | 25 | public function container(): string 26 | { 27 | return sprintf('
      %%pages%%
    ', 28 | $this->option('css_container_class') 29 | ); 30 | } 31 | 32 | /** 33 | * @param int $page 34 | */ 35 | public function page($page): string 36 | { 37 | return $this->pageWithText($page, (string) $page); 38 | } 39 | 40 | /** 41 | * @param int $page 42 | * @param string $text 43 | */ 44 | public function pageWithText($page, $text, ?string $rel = null): string 45 | { 46 | return $this->pageWithTextAndClass($page, $text, '', $rel); 47 | } 48 | 49 | /** 50 | * @param int $page 51 | * @param string $text 52 | * @param string $class 53 | */ 54 | private function pageWithTextAndClass($page, $text, $class, ?string $rel = null): string 55 | { 56 | return $this->linkLi($class, $this->generateRoute($page), $text, $rel); 57 | } 58 | 59 | public function previousDisabled(): string 60 | { 61 | return $this->spanLi($this->previousDisabledClass(), $this->option('prev_message')); 62 | } 63 | 64 | private function previousDisabledClass(): string 65 | { 66 | return $this->option('css_prev_class').' '.$this->option('css_disabled_class'); 67 | } 68 | 69 | /** 70 | * @param int $page 71 | */ 72 | public function previousEnabled($page): string 73 | { 74 | return $this->pageWithTextAndClass($page, $this->option('prev_message'), $this->option('css_prev_class'), $this->option('rel_previous')); 75 | } 76 | 77 | public function nextDisabled() 78 | { 79 | return $this->spanLi($this->nextDisabledClass(), $this->option('next_message')); 80 | } 81 | 82 | private function nextDisabledClass(): string 83 | { 84 | return $this->option('css_next_class').' '.$this->option('css_disabled_class'); 85 | } 86 | 87 | /** 88 | * @param int $page 89 | */ 90 | public function nextEnabled($page): string 91 | { 92 | return $this->pageWithTextAndClass($page, $this->option('next_message'), $this->option('css_next_class'), $this->option('rel_next')); 93 | } 94 | 95 | public function first(): string 96 | { 97 | return $this->page(1); 98 | } 99 | 100 | /** 101 | * @param int $page 102 | */ 103 | public function last($page): string 104 | { 105 | return $this->page($page); 106 | } 107 | 108 | /** 109 | * @param int $page 110 | */ 111 | public function current($page): string 112 | { 113 | $text = trim($page.' '.$this->option('active_suffix')); 114 | 115 | return $this->spanLi($this->option('css_active_class'), $text); 116 | } 117 | 118 | public function separator(): string 119 | { 120 | return $this->spanLi($this->option('css_dots_class'), $this->option('dots_message')); 121 | } 122 | 123 | /** 124 | * @param string $class 125 | * @param string $href 126 | * @param int|string $text 127 | * @param string|null $rel 128 | */ 129 | protected function linkLi($class, $href, $text, $rel = null): string 130 | { 131 | $liClass = $class ? sprintf(' class="%s"', $class) : ''; 132 | $rel = $rel ? sprintf(' rel="%s"', $rel) : ''; 133 | 134 | return sprintf('%s', $liClass, $href, $rel, $text); 135 | } 136 | 137 | /** 138 | * @param string $class 139 | * @param int|string $text 140 | */ 141 | protected function spanLi($class, $text): string 142 | { 143 | $liClass = $class ? sprintf(' class="%s"', $class) : ''; 144 | 145 | return sprintf('%s', $liClass, $text); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /View/TemplateView.php: -------------------------------------------------------------------------------- 1 | template = $template ?: $this->createDefaultTemplate(); 20 | } 21 | 22 | /** 23 | * @return TemplateInterface 24 | */ 25 | abstract protected function createDefaultTemplate(); 26 | 27 | /** 28 | * @throws InvalidArgumentException if the $routeGenerator is not a callable 29 | */ 30 | public function render(PagerfantaInterface $pagerfanta, $routeGenerator, array $options = []) 31 | { 32 | if (!\is_callable($routeGenerator)) { 33 | throw new InvalidArgumentException(sprintf('The $routeGenerator argument of %s() must be a callable, %s given.', __METHOD__, get_debug_type($routeGenerator))); 34 | } 35 | 36 | $this->initializePagerfanta($pagerfanta); 37 | $this->initializeOptions($options); 38 | 39 | $this->configureTemplate($routeGenerator, $options); 40 | 41 | return $this->generate(); 42 | } 43 | 44 | private function configureTemplate(callable $routeGenerator, array $options): void 45 | { 46 | if (method_exists($this->template, 'setRouteGenerator')) { 47 | $this->template->setRouteGenerator($routeGenerator); 48 | } 49 | 50 | if (method_exists($this->template, 'setOptions')) { 51 | $this->template->setOptions($options); 52 | } 53 | } 54 | 55 | private function generate(): string 56 | { 57 | return $this->generateContainer($this->generatePages()); 58 | } 59 | 60 | private function generateContainer(string $pages): string 61 | { 62 | return str_replace('%pages%', $pages, $this->template->container()); 63 | } 64 | 65 | private function generatePages(): string 66 | { 67 | $this->calculateStartAndEndPage(); 68 | 69 | return $this->previous(). 70 | $this->first(). 71 | $this->secondIfStartIs3(). 72 | $this->dotsIfStartIsOver3(). 73 | $this->pages(). 74 | $this->dotsIfEndIsUnder3ToLast(). 75 | $this->secondToLastIfEndIs3ToLast(). 76 | $this->last(). 77 | $this->next(); 78 | } 79 | 80 | private function previous(): string 81 | { 82 | if ($this->pagerfanta->hasPreviousPage()) { 83 | return $this->template->previousEnabled($this->pagerfanta->getPreviousPage()); 84 | } 85 | 86 | return $this->template->previousDisabled(); 87 | } 88 | 89 | private function first(): string 90 | { 91 | if ($this->startPage > 1) { 92 | return $this->template->first(); 93 | } 94 | 95 | return ''; 96 | } 97 | 98 | private function secondIfStartIs3(): string 99 | { 100 | if (3 === $this->startPage) { 101 | return $this->template->page(2); 102 | } 103 | 104 | return ''; 105 | } 106 | 107 | private function dotsIfStartIsOver3(): string 108 | { 109 | if ($this->startPage > 3) { 110 | return $this->template->separator(); 111 | } 112 | 113 | return ''; 114 | } 115 | 116 | private function pages(): string 117 | { 118 | $pages = ''; 119 | 120 | foreach (range($this->startPage, $this->endPage) as $page) { 121 | $pages .= $this->page($page); 122 | } 123 | 124 | return $pages; 125 | } 126 | 127 | private function page(int $page): string 128 | { 129 | if ($page === $this->currentPage) { 130 | return $this->template->current($page); 131 | } 132 | 133 | return $this->template->page($page); 134 | } 135 | 136 | private function dotsIfEndIsUnder3ToLast(): string 137 | { 138 | if ($this->endPage < $this->toLast(3)) { 139 | return $this->template->separator(); 140 | } 141 | 142 | return ''; 143 | } 144 | 145 | private function secondToLastIfEndIs3ToLast(): string 146 | { 147 | if ($this->endPage == $this->toLast(3)) { 148 | return $this->template->page($this->toLast(2)); 149 | } 150 | 151 | return ''; 152 | } 153 | 154 | private function last(): string 155 | { 156 | if ($this->pagerfanta->getNbPages() > $this->endPage) { 157 | return $this->template->last($this->pagerfanta->getNbPages()); 158 | } 159 | 160 | return ''; 161 | } 162 | 163 | private function next(): string 164 | { 165 | if ($this->pagerfanta->hasNextPage()) { 166 | return $this->template->nextEnabled($this->pagerfanta->getNextPage()); 167 | } 168 | 169 | return $this->template->nextDisabled(); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /View/TwitterBootstrap3View.php: -------------------------------------------------------------------------------- 1 | pagerfanta = $pagerfanta; 42 | 43 | $this->currentPage = $pagerfanta->getCurrentPage(); 44 | $this->nbPages = $pagerfanta->getNbPages(); 45 | } 46 | 47 | protected function initializeOptions(array $options): void 48 | { 49 | $this->proximity = isset($options['proximity']) ? (int) $options['proximity'] : $this->getDefaultProximity(); 50 | } 51 | 52 | /** 53 | * @return int 54 | */ 55 | protected function getDefaultProximity() 56 | { 57 | return 2; 58 | } 59 | 60 | protected function calculateStartAndEndPage(): void 61 | { 62 | $startPage = $this->currentPage - $this->proximity; 63 | $endPage = $this->currentPage + $this->proximity; 64 | 65 | if ($this->startPageUnderflow($startPage)) { 66 | $endPage = $this->calculateEndPageForStartPageUnderflow($startPage, $endPage); 67 | $startPage = 1; 68 | } 69 | 70 | if ($this->endPageOverflow($endPage)) { 71 | $startPage = $this->calculateStartPageForEndPageOverflow($startPage, $endPage); 72 | $endPage = $this->nbPages; 73 | } 74 | 75 | $this->startPage = $startPage; 76 | $this->endPage = $endPage; 77 | } 78 | 79 | /** 80 | * @param int $startPage 81 | * 82 | * @return bool 83 | */ 84 | protected function startPageUnderflow($startPage) 85 | { 86 | return $startPage < 1; 87 | } 88 | 89 | /** 90 | * @param int $endPage 91 | * 92 | * @return bool 93 | */ 94 | protected function endPageOverflow($endPage) 95 | { 96 | return $endPage > $this->nbPages; 97 | } 98 | 99 | /** 100 | * @param int $startPage 101 | * @param int $endPage 102 | * 103 | * @return int 104 | */ 105 | protected function calculateEndPageForStartPageUnderflow($startPage, $endPage) 106 | { 107 | return min($endPage + (1 - $startPage), $this->nbPages); 108 | } 109 | 110 | /** 111 | * @param int $startPage 112 | * @param int $endPage 113 | * 114 | * @return int 115 | */ 116 | protected function calculateStartPageForEndPageOverflow($startPage, $endPage) 117 | { 118 | return max($startPage - ($endPage - $this->nbPages), 1); 119 | } 120 | 121 | /** 122 | * @param int $n 123 | * 124 | * @return int 125 | */ 126 | protected function toLast($n) 127 | { 128 | return $this->pagerfanta->getNbPages() - ($n - 1); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /View/ViewFactory.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | private $views = []; 16 | 17 | /** 18 | * @param array $views 19 | */ 20 | public function add(array $views): void 21 | { 22 | foreach ($views as $name => $view) { 23 | $this->set($name, $view); 24 | } 25 | } 26 | 27 | /** 28 | * @return array 29 | */ 30 | public function all() 31 | { 32 | return $this->views; 33 | } 34 | 35 | public function clear(): void 36 | { 37 | $this->views = []; 38 | } 39 | 40 | /** 41 | * @param string $name 42 | * 43 | * @throws InvalidArgumentException if the view does not exist 44 | */ 45 | public function get($name) 46 | { 47 | if (!$this->has($name)) { 48 | throw new InvalidArgumentException(sprintf('The view "%s" does not exist.', $name)); 49 | } 50 | 51 | return $this->views[$name]; 52 | } 53 | 54 | /** 55 | * @param string $name 56 | */ 57 | public function has($name) 58 | { 59 | return isset($this->views[$name]); 60 | } 61 | 62 | /** 63 | * @param string $name 64 | * 65 | * @throws InvalidArgumentException if the view does not exist 66 | */ 67 | public function remove($name): void 68 | { 69 | if (!$this->has($name)) { 70 | throw new InvalidArgumentException(sprintf('The view "%s" does not exist.', $name)); 71 | } 72 | 73 | unset($this->views[$name]); 74 | } 75 | 76 | /** 77 | * @param string $name 78 | */ 79 | public function set($name, ViewInterface $view): void 80 | { 81 | $this->views[$name] = $view; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /View/ViewFactoryInterface.php: -------------------------------------------------------------------------------- 1 | $views 13 | * 14 | * @return void 15 | */ 16 | public function add(array $views); 17 | 18 | /** 19 | * Returns all the views. 20 | * 21 | * @return array 22 | */ 23 | public function all(); 24 | 25 | /** 26 | * Clears the views. 27 | * 28 | * @return void 29 | */ 30 | public function clear(); 31 | 32 | /** 33 | * Fetches a named view from the factory. 34 | * 35 | * @param string $name 36 | * 37 | * @return ViewInterface the view 38 | * 39 | * @throws InvalidArgumentException if the view does not exist 40 | */ 41 | public function get($name); 42 | 43 | /** 44 | * Checks whether a named view is registered to the factory. 45 | * 46 | * @param string $name 47 | * 48 | * @return bool 49 | */ 50 | public function has($name); 51 | 52 | /** 53 | * Removes a view. 54 | * 55 | * @param string $name the name 56 | * 57 | * @return void 58 | * 59 | * @throws InvalidArgumentException if the view does not exist 60 | */ 61 | public function remove($name); 62 | 63 | /** 64 | * Sets a view to the factory. 65 | * 66 | * @param string $name 67 | * 68 | * @return void 69 | */ 70 | public function set($name, ViewInterface $view); 71 | } 72 | -------------------------------------------------------------------------------- /View/ViewInterface.php: -------------------------------------------------------------------------------- 1 | $options 16 | * 17 | * @return string 18 | */ 19 | public function render(PagerfantaInterface $pagerfanta, $routeGenerator, array $options = []); 20 | 21 | /** 22 | * Returns the canonical name. 23 | * 24 | * @return string 25 | */ 26 | public function getName(); 27 | } 28 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pagerfanta/core", 3 | "type": "library", 4 | "description": "Core Pagerfanta API", 5 | "keywords": ["pagerfanta"], 6 | "license": "MIT", 7 | "require": { 8 | "php": "^7.2 || ^8.0", 9 | "ext-json": "*", 10 | "symfony/deprecation-contracts": "^2.1", 11 | "symfony/polyfill-php80": "^1.15" 12 | }, 13 | "autoload": { 14 | "psr-4": { 15 | "Pagerfanta\\": "./" 16 | } 17 | }, 18 | "minimum-stability": "dev" 19 | } 20 | --------------------------------------------------------------------------------