adapter = $adapter; } elseif ($adapter instanceof AdapterAggregateInterface) { $this->adapter = $adapter->getPaginatorAdapter(); } else { throw new Exception\InvalidArgumentException( 'Zend\Paginator only accepts instances of the type ' . 'Zend\Paginator\Adapter\AdapterInterface or Zend\Paginator\AdapterAggregateInterface.' ); } $config = static::$config; if (!empty($config)) { $setupMethods = array('ItemCountPerPage', 'PageRange'); foreach ($setupMethods as $setupMethod) { $key = strtolower($setupMethod); $value = isset($config[$key]) ? $config[$key] : null; if ($value != null) { $setupMethod = 'set' . $setupMethod; $this->$setupMethod($value); } } } } /** * Serializes the object as a string. Proxies to {@link render()}. * * @return string */ public function __toString() { try { $return = $this->render(); return $return; } catch (\Exception $e) { trigger_error($e->getMessage(), E_USER_WARNING); } return ''; } /** * Enables/Disables the cache for this instance * * @param bool $enable * @return Paginator */ public function setCacheEnabled($enable) { $this->cacheEnabled = (bool) $enable; return $this; } /** * Returns the number of pages. * * @return int */ public function count() { if (!$this->pageCount) { $this->pageCount = $this->_calculatePageCount(); } return $this->pageCount; } /** * Returns the total number of items available. * * @return int */ public function getTotalItemCount() { return count($this->getAdapter()); } /** * Clear the page item cache. * * @param int $pageNumber * @return Paginator */ public function clearPageItemCache($pageNumber = null) { if (!$this->cacheEnabled()) { return $this; } if (null === $pageNumber) { $prefixLength = strlen(self::CACHE_TAG_PREFIX); $cacheIterator = static::$cache->getIterator(); $cacheIterator->setMode(CacheIterator::CURRENT_AS_KEY); foreach ($cacheIterator as $key) { $tags = static::$cache->getTags($key); if ($tags && in_array($this->_getCacheInternalId(), $tags)) { if (substr($key, 0, $prefixLength) == self::CACHE_TAG_PREFIX) { static::$cache->removeItem($this->_getCacheId((int)substr($key, $prefixLength))); } } } } else { $cleanId = $this->_getCacheId($pageNumber); static::$cache->removeItem($cleanId); } return $this; } /** * Returns the absolute item number for the specified item. * * @param int $relativeItemNumber Relative item number * @param int $pageNumber Page number * @return int */ public function getAbsoluteItemNumber($relativeItemNumber, $pageNumber = null) { $relativeItemNumber = $this->normalizeItemNumber($relativeItemNumber); if ($pageNumber == null) { $pageNumber = $this->getCurrentPageNumber(); } $pageNumber = $this->normalizePageNumber($pageNumber); return (($pageNumber - 1) * $this->getItemCountPerPage()) + $relativeItemNumber; } /** * Returns the adapter. * * @return AdapterInterface */ public function getAdapter() { return $this->adapter; } /** * Returns the number of items for the current page. * * @return int */ public function getCurrentItemCount() { if ($this->currentItemCount === null) { $this->currentItemCount = $this->getItemCount($this->getCurrentItems()); } return $this->currentItemCount; } /** * Returns the items for the current page. * * @return Traversable */ public function getCurrentItems() { if ($this->currentItems === null) { $this->currentItems = $this->getItemsByPage($this->getCurrentPageNumber()); } return $this->currentItems; } /** * Returns the current page number. * * @return int */ public function getCurrentPageNumber() { return $this->normalizePageNumber($this->currentPageNumber); } /** * Sets the current page number. * * @param int $pageNumber Page number * @return Paginator $this */ public function setCurrentPageNumber($pageNumber) { $this->currentPageNumber = (int) $pageNumber; $this->currentItems = null; $this->currentItemCount = null; return $this; } /** * Get the filter * * @return FilterInterface */ public function getFilter() { return $this->filter; } /** * Set a filter chain * * @param FilterInterface $filter * @return Paginator */ public function setFilter(FilterInterface $filter) { $this->filter = $filter; return $this; } /** * Returns an item from a page. The current page is used if there's no * page specified. * * @param int $itemNumber Item number (1 to itemCountPerPage) * @param int $pageNumber * @throws Exception\InvalidArgumentException * @return mixed */ public function getItem($itemNumber, $pageNumber = null) { if ($pageNumber == null) { $pageNumber = $this->getCurrentPageNumber(); } elseif ($pageNumber < 0) { $pageNumber = ($this->count() + 1) + $pageNumber; } $page = $this->getItemsByPage($pageNumber); $itemCount = $this->getItemCount($page); if ($itemCount == 0) { throw new Exception\InvalidArgumentException('Page ' . $pageNumber . ' does not exist'); } if ($itemNumber < 0) { $itemNumber = ($itemCount + 1) + $itemNumber; } $itemNumber = $this->normalizeItemNumber($itemNumber); if ($itemNumber > $itemCount) { throw new Exception\InvalidArgumentException('Page ' . $pageNumber . ' does not' . ' contain item number ' . $itemNumber); } return $page[$itemNumber - 1]; } /** * Returns the number of items per page. * * @return int */ public function getItemCountPerPage() { if (empty($this->itemCountPerPage)) { $this->itemCountPerPage = static::getDefaultItemCountPerPage(); } return $this->itemCountPerPage; } /** * Sets the number of items per page. * * @param int $itemCountPerPage * @return Paginator $this */ public function setItemCountPerPage($itemCountPerPage = -1) { $this->itemCountPerPage = (int) $itemCountPerPage; if ($this->itemCountPerPage < 1) { $this->itemCountPerPage = $this->getTotalItemCount(); } $this->pageCount = $this->_calculatePageCount(); $this->currentItems = null; $this->currentItemCount = null; return $this; } /** * Returns the number of items in a collection. * * @param mixed $items Items * @return int */ public function getItemCount($items) { $itemCount = 0; if (is_array($items) || $items instanceof Countable) { $itemCount = count($items); } elseif ($items instanceof Traversable) { // $items is something like LimitIterator $itemCount = iterator_count($items); } return $itemCount; } /** * Returns the items for a given page. * * @param int $pageNumber * @return mixed */ public function getItemsByPage($pageNumber) { $pageNumber = $this->normalizePageNumber($pageNumber); if ($this->cacheEnabled()) { $data = static::$cache->getItem($this->_getCacheId($pageNumber)); if ($data) { return $data; } } $offset = ($pageNumber - 1) * $this->getItemCountPerPage(); $items = $this->adapter->getItems($offset, $this->getItemCountPerPage()); $filter = $this->getFilter(); if ($filter !== null) { $items = $filter->filter($items); } if (!$items instanceof Traversable) { $items = new ArrayIterator($items); } if ($this->cacheEnabled()) { $cacheId = $this->_getCacheId($pageNumber); static::$cache->setItem($cacheId, $items); static::$cache->setTags($cacheId, array($this->_getCacheInternalId())); } return $items; } /** * Returns a foreach-compatible iterator. * * @throws Exception\RuntimeException * @return Traversable */ public function getIterator() { try { return $this->getCurrentItems(); } catch (\Exception $e) { throw new Exception\RuntimeException('Error producing an iterator', null, $e); } } /** * Returns the page range (see property declaration above). * * @return int */ public function getPageRange() { return $this->pageRange; } /** * Sets the page range (see property declaration above). * * @param int $pageRange * @return Paginator $this */ public function setPageRange($pageRange) { $this->pageRange = (int) $pageRange; return $this; } /** * Returns the page collection. * * @param string $scrollingStyle Scrolling style * @return array */ public function getPages($scrollingStyle = null) { if ($this->pages === null) { $this->pages = $this->_createPages($scrollingStyle); } return $this->pages; } /** * Returns a subset of pages within a given range. * * @param int $lowerBound Lower bound of the range * @param int $upperBound Upper bound of the range * @return array */ public function getPagesInRange($lowerBound, $upperBound) { $lowerBound = $this->normalizePageNumber($lowerBound); $upperBound = $this->normalizePageNumber($upperBound); $pages = array(); for ($pageNumber = $lowerBound; $pageNumber <= $upperBound; $pageNumber++) { $pages[$pageNumber] = $pageNumber; } return $pages; } /** * Returns the page item cache. * * @return array */ public function getPageItemCache() { $data = array(); if ($this->cacheEnabled()) { $prefixLength = strlen(self::CACHE_TAG_PREFIX); $cacheIterator = static::$cache->getIterator(); $cacheIterator->setMode(CacheIterator::CURRENT_AS_VALUE); foreach ($cacheIterator as $key => $value) { $tags = static::$cache->getTags($key); if ($tags && in_array($this->_getCacheInternalId(), $tags)) { if (substr($key, 0, $prefixLength) == self::CACHE_TAG_PREFIX) { $data[(int) substr($key, $prefixLength)] = $value; } } } } return $data; } /** * Retrieves the view instance. * * If none registered, instantiates a PhpRenderer instance. * * @return \Zend\View\Renderer\RendererInterface|null */ public function getView() { if ($this->view === null) { $this->setView(new View\Renderer\PhpRenderer()); } return $this->view; } /** * Sets the view object. * * @param \Zend\View\Renderer\RendererInterface $view * @return Paginator */ public function setView(View\Renderer\RendererInterface $view = null) { $this->view = $view; return $this; } /** * Brings the item number in range of the page. * * @param int $itemNumber * @return int */ public function normalizeItemNumber($itemNumber) { $itemNumber = (int) $itemNumber; if ($itemNumber < 1) { $itemNumber = 1; } if ($itemNumber > $this->getItemCountPerPage()) { $itemNumber = $this->getItemCountPerPage(); } return $itemNumber; } /** * Brings the page number in range of the paginator. * * @param int $pageNumber * @return int */ public function normalizePageNumber($pageNumber) { $pageNumber = (int) $pageNumber; if ($pageNumber < 1) { $pageNumber = 1; } $pageCount = $this->count(); if ($pageCount > 0 && $pageNumber > $pageCount) { $pageNumber = $pageCount; } return $pageNumber; } /** * Renders the paginator. * * @param \Zend\View\Renderer\RendererInterface $view * @return string */ public function render(View\Renderer\RendererInterface $view = null) { if (null !== $view) { $this->setView($view); } $view = $this->getView(); return $view->paginationControl($this); } /** * Returns the items of the current page as JSON. * * @return string */ public function toJson() { $currentItems = $this->getCurrentItems(); if ($currentItems instanceof AbstractResultSet) { return Json::encode($currentItems->toArray()); } return Json::encode($currentItems); } /** * Tells if there is an active cache object * and if the cache has not been disabled * * @return bool */ protected function cacheEnabled() { return ((static::$cache !== null) && $this->cacheEnabled); } /** * Makes an Id for the cache * Depends on the adapter object and the page number * * Used to store item in cache from that Paginator instance * and that current page * * @param int $page * @return string */ protected function _getCacheId($page = null) { if ($page === null) { $page = $this->getCurrentPageNumber(); } return self::CACHE_TAG_PREFIX . $page . '_' . $this->_getCacheInternalId(); } /** * Get the internal cache id * Depends on the adapter and the item count per page * * Used to tag that unique Paginator instance in cache * * @return string */ protected function _getCacheInternalId() { return md5(serialize(array( spl_object_hash($this->getAdapter()), $this->getItemCountPerPage() ))); } /** * Calculates the page count. * * @return int */ protected function _calculatePageCount() { return (int) ceil($this->getAdapter()->count() / $this->getItemCountPerPage()); } /** * Creates the page collection. * * @param string $scrollingStyle Scrolling style * @return \stdClass */ protected function _createPages($scrollingStyle = null) { $pageCount = $this->count(); $currentPageNumber = $this->getCurrentPageNumber(); $pages = new \stdClass(); $pages->pageCount = $pageCount; $pages->itemCountPerPage = $this->getItemCountPerPage(); $pages->first = 1; $pages->current = $currentPageNumber; $pages->last = $pageCount; // Previous and next if ($currentPageNumber - 1 > 0) { $pages->previous = $currentPageNumber - 1; } if ($currentPageNumber + 1 <= $pageCount) { $pages->next = $currentPageNumber + 1; } // Pages in range $scrollingStyle = $this->_loadScrollingStyle($scrollingStyle); $pages->pagesInRange = $scrollingStyle->getPages($this); $pages->firstPageInRange = min($pages->pagesInRange); $pages->lastPageInRange = max($pages->pagesInRange); // Item numbers if ($this->getCurrentItems() !== null) { $pages->currentItemCount = $this->getCurrentItemCount(); $pages->itemCountPerPage = $this->getItemCountPerPage(); $pages->totalItemCount = $this->getTotalItemCount(); $pages->firstItemNumber = (($currentPageNumber - 1) * $this->getItemCountPerPage()) + 1; $pages->lastItemNumber = $pages->firstItemNumber + $pages->currentItemCount - 1; } return $pages; } /** * Loads a scrolling style. * * @param string $scrollingStyle * @return ScrollingStyleInterface * @throws Exception\InvalidArgumentException */ protected function _loadScrollingStyle($scrollingStyle = null) { if ($scrollingStyle === null) { $scrollingStyle = static::$defaultScrollingStyle; } switch (strtolower(gettype($scrollingStyle))) { case 'object': if (!$scrollingStyle instanceof ScrollingStyleInterface) { throw new Exception\InvalidArgumentException( 'Scrolling style must implement Zend\Paginator\ScrollingStyle\ScrollingStyleInterface' ); } return $scrollingStyle; case 'string': return static::getScrollingStylePluginManager()->get($scrollingStyle); case 'null': // Fall through to default case default: throw new Exception\InvalidArgumentException( 'Scrolling style must be a class ' . 'name or object implementing Zend\Paginator\ScrollingStyle\ScrollingStyleInterface' ); } } }