Source of file AbstractContainer.php
Size: 13,793 Bytes - Last Modified: 2014-03-12T23:21:18+01:00
/home/theseer/Downloads/ZendFramework-2.3.0/library/Zend/Navigation/AbstractContainer.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512 | <?php /** * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ namespace Zend\Navigation; use Countable; use RecursiveIterator; use RecursiveIteratorIterator; use Traversable; use Zend\Stdlib\ErrorHandler; /** * Zend\Navigation\Container * * AbstractContainer class for Zend\Navigation\Page classes. */ abstract class AbstractContainer implements Countable, RecursiveIterator { /** * Contains sub pages * * @var array */ protected $pages = array(); /** * An index that contains the order in which to iterate pages * * @var array */ protected $index = array(); /** * Whether index is dirty and needs to be re-arranged * * @var bool */ protected $dirtyIndex = false; // Internal methods: /** * Sorts the page index according to page order * * @return void */ protected function sort() { if (!$this->dirtyIndex) { return; } $newIndex = array(); $index = 0; foreach ($this->pages as $hash => $page) { $order = $page->getOrder(); if ($order === null) { $newIndex[$hash] = $index; $index++; } else { $newIndex[$hash] = $order; } } asort($newIndex); $this->index = $newIndex; $this->dirtyIndex = false; } // Public methods: /** * Notifies container that the order of pages are updated * * @return void */ public function notifyOrderUpdated() { $this->dirtyIndex = true; } /** * Adds a page to the container * * This method will inject the container as the given page's parent by * calling {@link Page\AbstractPage::setParent()}. * * @param Page\AbstractPage|array|Traversable $page page to add * @return self fluent interface, returns self * @throws Exception\InvalidArgumentException if page is invalid */ public function addPage($page) { if ($page === $this) { throw new Exception\InvalidArgumentException( 'A page cannot have itself as a parent' ); } if (!$page instanceof Page\AbstractPage) { if (!is_array($page) && !$page instanceof Traversable) { throw new Exception\InvalidArgumentException( 'Invalid argument: $page must be an instance of ' . 'Zend\Navigation\Page\AbstractPage or Traversable, or an array' ); } $page = Page\AbstractPage::factory($page); } $hash = $page->hashCode(); if (array_key_exists($hash, $this->index)) { // page is already in container return $this; } // adds page to container and sets dirty flag $this->pages[$hash] = $page; $this->index[$hash] = $page->getOrder(); $this->dirtyIndex = true; // inject self as page parent $page->setParent($this); return $this; } /** * Adds several pages at once * * @param array|Traversable|AbstractContainer $pages pages to add * @return self fluent interface, returns self * @throws Exception\InvalidArgumentException if $pages is not array, * Traversable or AbstractContainer */ public function addPages($pages) { if (!is_array($pages) && !$pages instanceof Traversable) { throw new Exception\InvalidArgumentException( 'Invalid argument: $pages must be an array, an ' . 'instance of Traversable or an instance of ' . 'Zend\Navigation\AbstractContainer' ); } // Because adding a page to a container removes it from the original // (see {@link Page\AbstractPage::setParent()}), iteration of the // original container will break. As such, we need to iterate the // container into an array first. if ($pages instanceof AbstractContainer) { $pages = iterator_to_array($pages); } foreach ($pages as $page) { if (null === $page) { continue; } $this->addPage($page); } return $this; } /** * Sets pages this container should have, removing existing pages * * @param array $pages pages to set * @return self fluent interface, returns self */ public function setPages(array $pages) { $this->removePages(); return $this->addPages($pages); } /** * Returns pages in the container * * @return array array of Page\AbstractPage instances */ public function getPages() { return $this->pages; } /** * Removes the given page from the container * * @param Page\AbstractPage|int $page page to remove, either a page * instance or a specific page order * @return bool whether the removal was successful */ public function removePage($page) { if ($page instanceof Page\AbstractPage) { $hash = $page->hashCode(); } elseif (is_int($page)) { $this->sort(); if (!$hash = array_search($page, $this->index)) { return false; } } else { return false; } if (isset($this->pages[$hash])) { unset($this->pages[$hash]); unset($this->index[$hash]); $this->dirtyIndex = true; return true; } return false; } /** * Removes all pages in container * * @return self fluent interface, returns self */ public function removePages() { $this->pages = array(); $this->index = array(); return $this; } /** * Checks if the container has the given page * * @param Page\AbstractPage $page page to look for * @param bool $recursive [optional] whether to search recursively. * Default is false. * @return bool whether page is in container */ public function hasPage(Page\AbstractPage $page, $recursive = false) { if (array_key_exists($page->hashCode(), $this->index)) { return true; } elseif ($recursive) { foreach ($this->pages as $childPage) { if ($childPage->hasPage($page, true)) { return true; } } } return false; } /** * Returns true if container contains any pages * * @param bool $onlyVisible whether to check only visible pages * @return bool whether container has any pages */ public function hasPages($onlyVisible = false) { if ($onlyVisible) { foreach ($this->pages as $page) { if ($page->isVisible()) { return true; } } // no visible pages found return false; } return count($this->index) > 0; } /** * Returns a child page matching $property == $value, or null if not found * * @param string $property name of property to match against * @param mixed $value value to match property against * @return Page\AbstractPage|null matching page or null */ public function findOneBy($property, $value) { $iterator = new RecursiveIteratorIterator($this, RecursiveIteratorIterator::SELF_FIRST); foreach ($iterator as $page) { if ($page->get($property) == $value) { return $page; } } return null; } /** * Returns all child pages matching $property == $value, or an empty array * if no pages are found * * @param string $property name of property to match against * @param mixed $value value to match property against * @return array array containing only Page\AbstractPage instances */ public function findAllBy($property, $value) { $found = array(); $iterator = new RecursiveIteratorIterator($this, RecursiveIteratorIterator::SELF_FIRST); foreach ($iterator as $page) { if ($page->get($property) == $value) { $found[] = $page; } } return $found; } /** * Returns page(s) matching $property == $value * * @param string $property name of property to match against * @param mixed $value value to match property against * @param bool $all [optional] whether an array of all matching * pages should be returned, or only the first. * If true, an array will be returned, even if not * matching pages are found. If false, null will * be returned if no matching page is found. * Default is false. * @return Page\AbstractPage|null matching page or null */ public function findBy($property, $value, $all = false) { if ($all) { return $this->findAllBy($property, $value); } return $this->findOneBy($property, $value); } /** * Magic overload: Proxy calls to finder methods * * Examples of finder calls: * <code> * // METHOD // SAME AS * $nav->findByLabel('foo'); // $nav->findOneBy('label', 'foo'); * $nav->findOneByLabel('foo'); // $nav->findOneBy('label', 'foo'); * $nav->findAllByClass('foo'); // $nav->findAllBy('class', 'foo'); * </code> * * @param string $method method name * @param array $arguments method arguments * @throws Exception\BadMethodCallException if method does not exist */ public function __call($method, $arguments) { ErrorHandler::start(E_WARNING); $result = preg_match('/(find(?:One|All)?By)(.+)/', $method, $match); $error = ErrorHandler::stop(); if (!$result) { throw new Exception\BadMethodCallException(sprintf( 'Bad method call: Unknown method %s::%s', get_class($this), $method ), 0, $error); } return $this->{$match[1]}($match[2], $arguments[0]); } /** * Returns an array representation of all pages in container * * @return array */ public function toArray() { $this->sort(); $pages = array(); $indexes = array_keys($this->index); foreach ($indexes as $hash) { $pages[] = $this->pages[$hash]->toArray(); } return $pages; } // RecursiveIterator interface: /** * Returns current page * * Implements RecursiveIterator interface. * * @return Page\AbstractPage current page or null * @throws Exception\OutOfBoundsException if the index is invalid */ public function current() { $this->sort(); current($this->index); $hash = key($this->index); if (!isset($this->pages[$hash])) { throw new Exception\OutOfBoundsException( 'Corruption detected in container; ' . 'invalid key found in internal iterator' ); } return $this->pages[$hash]; } /** * Returns hash code of current page * * Implements RecursiveIterator interface. * * @return string hash code of current page */ public function key() { $this->sort(); return key($this->index); } /** * Moves index pointer to next page in the container * * Implements RecursiveIterator interface. * * @return void */ public function next() { $this->sort(); next($this->index); } /** * Sets index pointer to first page in the container * * Implements RecursiveIterator interface. * * @return void */ public function rewind() { $this->sort(); reset($this->index); } /** * Checks if container index is valid * * Implements RecursiveIterator interface. * * @return bool */ public function valid() { $this->sort(); return current($this->index) !== false; } /** * Proxy to hasPages() * * Implements RecursiveIterator interface. * * @return bool whether container has any pages */ public function hasChildren() { return $this->hasPages(); } /** * Returns the child container. * * Implements RecursiveIterator interface. * * @return Page\AbstractPage|null */ public function getChildren() { $hash = key($this->index); if (isset($this->pages[$hash])) { return $this->pages[$hash]; } return null; } // Countable interface: /** * Returns number of pages in container * * Implements Countable interface. * * @return int number of pages in the container */ public function count() { return count($this->index); } } |