PageRenderTime 38ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Navigation/Container.php

https://bitbucket.org/Ebozavrik/test-application
PHP | 526 lines | 231 code | 61 blank | 234 comment | 26 complexity | d4cec5ad87b71e53285faa2f0d65e7bb MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Navigation
  17. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id: Container.php 24593 2012-01-05 20:35:02Z matthew $
  20. */
  21. /**
  22. * Zend_Navigation_Container
  23. *
  24. * Container class for Zend_Navigation_Page classes.
  25. *
  26. * @category Zend
  27. * @package Zend_Navigation
  28. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  29. * @license http://framework.zend.com/license/new-bsd New BSD License
  30. */
  31. abstract class Zend_Navigation_Container implements RecursiveIterator, Countable
  32. {
  33. /**
  34. * Contains sub pages
  35. *
  36. * @var array
  37. */
  38. protected $_pages = array();
  39. /**
  40. * An index that contains the order in which to iterate pages
  41. *
  42. * @var array
  43. */
  44. protected $_index = array();
  45. /**
  46. * Whether index is dirty and needs to be re-arranged
  47. *
  48. * @var bool
  49. */
  50. protected $_dirtyIndex = false;
  51. // Internal methods:
  52. /**
  53. * Sorts the page index according to page order
  54. *
  55. * @return void
  56. */
  57. protected function _sort ()
  58. {
  59. if ($this->_dirtyIndex) {
  60. $newIndex = array();
  61. $index = 0;
  62. foreach ($this->_pages as $hash => $page) {
  63. $order = $page->getOrder();
  64. if ($order === null) {
  65. $newIndex[$hash] = $index;
  66. $index++;
  67. } else {
  68. $newIndex[$hash] = $order;
  69. }
  70. }
  71. asort($newIndex);
  72. $this->_index = $newIndex;
  73. $this->_dirtyIndex = false;
  74. }
  75. }
  76. // Public methods:
  77. /**
  78. * Notifies container that the order of pages are updated
  79. *
  80. * @return void
  81. */
  82. public function notifyOrderUpdated ()
  83. {
  84. $this->_dirtyIndex = true;
  85. }
  86. /**
  87. * Adds a page to the container
  88. *
  89. * This method will inject the container as the given page's parent by
  90. * calling {@link Zend_Navigation_Page::setParent()}.
  91. *
  92. * @param Zend_Navigation_Page|array|Zend_Config $page page to add
  93. *
  94. * @return Zend_Navigation_Container fluent interface,
  95. * returns self
  96. * @throws Zend_Navigation_Exception if page is invalid
  97. */
  98. public function addPage ($page)
  99. {
  100. if ($page === $this) {
  101. require_once 'Zend/Navigation/Exception.php';
  102. throw new Zend_Navigation_Exception(
  103. 'A page cannot have itself as a parent' );
  104. }
  105. if (is_array($page) || $page instanceof Zend_Config) {
  106. require_once 'Zend/Navigation/Page.php';
  107. $page = Zend_Navigation_Page::factory($page);
  108. } elseif (!$page instanceof Zend_Navigation_Page) {
  109. require_once 'Zend/Navigation/Exception.php';
  110. throw new Zend_Navigation_Exception(
  111. 'Invalid argument: $page must be an instance of ' .
  112. 'Zend_Navigation_Page or Zend_Config, or an array' );
  113. }
  114. $hash = $page->hashCode();
  115. if (array_key_exists($hash, $this->_index)) {
  116. // page is already in container
  117. return $this;
  118. }
  119. // adds page to container and sets dirty flag
  120. $this->_pages[$hash] = $page;
  121. $this->_index[$hash] = $page->getOrder();
  122. $this->_dirtyIndex = true;
  123. // inject self as page parent
  124. $page->setParent($this);
  125. return $this;
  126. }
  127. /**
  128. * Adds several pages at once
  129. *
  130. * @param array|Zend_Config|Zend_Navigation_Container $pages pages to add
  131. *
  132. * @return Zend_Navigation_Container fluent interface,
  133. * returns self
  134. * @throws Zend_Navigation_Exception if $pages is not
  135. * array, Zend_Config or
  136. * Zend_Navigation_Container
  137. */
  138. public function addPages ($pages)
  139. {
  140. if ($pages instanceof Zend_Config) {
  141. $pages = $pages->toArray();
  142. }
  143. if ($pages instanceof Zend_Navigation_Container) {
  144. $pages = iterator_to_array($pages);
  145. }
  146. if (!is_array($pages)) {
  147. require_once 'Zend/Navigation/Exception.php';
  148. throw new Zend_Navigation_Exception(
  149. 'Invalid argument: $pages must be an array, an ' .
  150. 'instance of Zend_Config or an instance of ' .
  151. 'Zend_Navigation_Container' );
  152. }
  153. foreach ($pages as $page) {
  154. $this->addPage($page);
  155. }
  156. return $this;
  157. }
  158. /**
  159. * Sets pages this container should have, removing existing pages
  160. *
  161. * @param array $pages pages to set
  162. *
  163. * @return Zend_Navigation_Container fluent interface, returns self
  164. */
  165. public function setPages (array $pages)
  166. {
  167. $this->removePages();
  168. return $this->addPages($pages);
  169. }
  170. /**
  171. * Returns pages in the container
  172. *
  173. * @return array array of Zend_Navigation_Page instances
  174. */
  175. public function getPages ()
  176. {
  177. return $this->_pages;
  178. }
  179. /**
  180. * Removes the given page from the container
  181. *
  182. * @param Zend_Navigation_Page|int $page page to remove, either a page
  183. * instance or a specific page order
  184. *
  185. * @return bool whether the removal was
  186. * successful
  187. */
  188. public function removePage ($page)
  189. {
  190. if ($page instanceof Zend_Navigation_Page) {
  191. $hash = $page->hashCode();
  192. } elseif (is_int($page)) {
  193. $this->_sort();
  194. if (!$hash = array_search($page, $this->_index)) {
  195. return false;
  196. }
  197. } else {
  198. return false;
  199. }
  200. if (isset( $this->_pages[$hash] )) {
  201. unset( $this->_pages[$hash] );
  202. unset( $this->_index[$hash] );
  203. $this->_dirtyIndex = true;
  204. return true;
  205. }
  206. return false;
  207. }
  208. /**
  209. * Removes all pages in container
  210. *
  211. * @return Zend_Navigation_Container fluent interface, returns self
  212. */
  213. public function removePages ()
  214. {
  215. $this->_pages = array();
  216. $this->_index = array();
  217. return $this;
  218. }
  219. /**
  220. * Checks if the container has the given page
  221. *
  222. * @param Zend_Navigation_Page $page page to look for
  223. * @param bool $recursive [optional] whether to search
  224. * recursively. Default is false.
  225. *
  226. * @return bool whether page is in container
  227. */
  228. public function hasPage (Zend_Navigation_Page $page, $recursive = false)
  229. {
  230. if (array_key_exists($page->hashCode(), $this->_index)) {
  231. return true;
  232. } elseif ($recursive) {
  233. foreach ($this->_pages as $childPage) {
  234. if ($childPage->hasPage($page, true)) {
  235. return true;
  236. }
  237. }
  238. }
  239. return false;
  240. }
  241. /**
  242. * Returns true if container contains any pages
  243. *
  244. * @return bool whether container has any pages
  245. */
  246. public function hasPages ()
  247. {
  248. return count($this->_index) > 0;
  249. }
  250. /**
  251. * Returns a child page matching $property == $value, or null if not found
  252. *
  253. * @param string $property name of property to match against
  254. * @param mixed $value value to match property against
  255. *
  256. * @return Zend_Navigation_Page|null matching page or null
  257. */
  258. public function findOneBy ($property, $value)
  259. {
  260. $iterator = new RecursiveIteratorIterator( $this,
  261. RecursiveIteratorIterator::SELF_FIRST );
  262. foreach ($iterator as $page) {
  263. if ($page->get($property) == $value) {
  264. return $page;
  265. }
  266. }
  267. return null;
  268. }
  269. /**
  270. * Returns all child pages matching $property == $value, or an empty array
  271. * if no pages are found
  272. *
  273. * @param string $property name of property to match against
  274. * @param mixed $value value to match property against
  275. *
  276. * @return array array containing only Zend_Navigation_Page
  277. * instances
  278. */
  279. public function findAllBy ($property, $value)
  280. {
  281. $found = array();
  282. $iterator = new RecursiveIteratorIterator( $this,
  283. RecursiveIteratorIterator::SELF_FIRST );
  284. foreach ($iterator as $page) {
  285. if ($page->get($property) == $value) {
  286. $found[] = $page;
  287. }
  288. }
  289. return $found;
  290. }
  291. /**
  292. * Returns page(s) matching $property == $value
  293. *
  294. * @param string $property name of property to match against
  295. * @param mixed $value value to match property against
  296. * @param bool $all [optional] whether an array of all matching
  297. * pages should be returned, or only the first.
  298. * If true, an array will be returned, even if not
  299. * matching pages are found. If false, null will
  300. * be returned if no matching page is found.
  301. * Default is false.
  302. *
  303. * @return Zend_Navigation_Page|null matching page or null
  304. */
  305. public function findBy ($property, $value, $all = false)
  306. {
  307. if ($all) {
  308. return $this->findAllBy($property, $value);
  309. } else {
  310. return $this->findOneBy($property, $value);
  311. }
  312. }
  313. /**
  314. * Magic overload: Proxy calls to finder methods
  315. *
  316. * Examples of finder calls:
  317. * <code>
  318. * // METHOD // SAME AS
  319. * $nav->findByLabel('foo'); // $nav->findOneBy('label', 'foo');
  320. * $nav->findOneByLabel('foo'); // $nav->findOneBy('label', 'foo');
  321. * $nav->findAllByClass('foo'); // $nav->findAllBy('class', 'foo');
  322. * </code>
  323. *
  324. * @param string $method method name
  325. * @param array $arguments method arguments
  326. *
  327. * @throws Zend_Navigation_Exception if method does not exist
  328. */
  329. public function __call ($method, $arguments)
  330. {
  331. if (@preg_match('/(find(?:One|All)?By)(.+)/', $method, $match)) {
  332. return $this->{$match[1]}($match[2], $arguments[0]);
  333. }
  334. require_once 'Zend/Navigation/Exception.php';
  335. throw new Zend_Navigation_Exception( sprintf(
  336. 'Bad method call: Unknown method %s::%s',
  337. get_class($this),
  338. $method) );
  339. }
  340. /**
  341. * Returns an array representation of all pages in container
  342. *
  343. * @return array
  344. */
  345. public function toArray ()
  346. {
  347. $pages = array();
  348. $this->_dirtyIndex = true;
  349. $this->_sort();
  350. $indexes = array_keys($this->_index);
  351. foreach ($indexes as $hash) {
  352. $pages[] = $this->_pages[$hash]->toArray();
  353. }
  354. return $pages;
  355. }
  356. // RecursiveIterator interface:
  357. /**
  358. * Returns current page
  359. *
  360. * Implements RecursiveIterator interface.
  361. *
  362. * @return Zend_Navigation_Page current page or null
  363. * @throws Zend_Navigation_Exception if the index is invalid
  364. */
  365. public function current ()
  366. {
  367. $this->_sort();
  368. current($this->_index);
  369. $hash = key($this->_index);
  370. if (isset( $this->_pages[$hash] )) {
  371. return $this->_pages[$hash];
  372. } else {
  373. require_once 'Zend/Navigation/Exception.php';
  374. throw new Zend_Navigation_Exception(
  375. 'Corruption detected in container; ' .
  376. 'invalid key found in internal iterator' );
  377. }
  378. }
  379. /**
  380. * Returns hash code of current page
  381. *
  382. * Implements RecursiveIterator interface.
  383. *
  384. * @return string hash code of current page
  385. */
  386. public function key ()
  387. {
  388. $this->_sort();
  389. return key($this->_index);
  390. }
  391. /**
  392. * Moves index pointer to next page in the container
  393. *
  394. * Implements RecursiveIterator interface.
  395. *
  396. * @return void
  397. */
  398. public function next ()
  399. {
  400. $this->_sort();
  401. next($this->_index);
  402. }
  403. /**
  404. * Sets index pointer to first page in the container
  405. *
  406. * Implements RecursiveIterator interface.
  407. *
  408. * @return void
  409. */
  410. public function rewind ()
  411. {
  412. $this->_sort();
  413. reset($this->_index);
  414. }
  415. /**
  416. * Checks if container index is valid
  417. *
  418. * Implements RecursiveIterator interface.
  419. *
  420. * @return bool
  421. */
  422. public function valid ()
  423. {
  424. $this->_sort();
  425. return current($this->_index) !== false;
  426. }
  427. /**
  428. * Proxy to hasPages()
  429. *
  430. * Implements RecursiveIterator interface.
  431. *
  432. * @return bool whether container has any pages
  433. */
  434. public function hasChildren ()
  435. {
  436. return $this->hasPages();
  437. }
  438. /**
  439. * Returns the child container.
  440. *
  441. * Implements RecursiveIterator interface.
  442. *
  443. * @return Zend_Navigation_Page|null
  444. */
  445. public function getChildren ()
  446. {
  447. $hash = key($this->_index);
  448. if (isset( $this->_pages[$hash] )) {
  449. return $this->_pages[$hash];
  450. }
  451. return null;
  452. }
  453. // Countable interface:
  454. /**
  455. * Returns number of pages in container
  456. *
  457. * Implements Countable interface.
  458. *
  459. * @return int number of pages in the container
  460. */
  461. public function count ()
  462. {
  463. return count($this->_index);
  464. }
  465. }