PageRenderTime 42ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/Nette/ComponentContainer.php

https://github.com/DocX/nette
PHP | 313 lines | 146 code | 72 blank | 95 comment | 26 complexity | 43d2e50207c3f67369542a576720015c MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * Nette Framework
  4. *
  5. * Copyright (c) 2004, 2009 David Grudl (http://davidgrudl.com)
  6. *
  7. * This source file is subject to the "Nette license" that is bundled
  8. * with this package in the file license.txt.
  9. *
  10. * For more information please see http://nettephp.com
  11. *
  12. * @copyright Copyright (c) 2004, 2009 David Grudl
  13. * @license http://nettephp.com/license Nette license
  14. * @link http://nettephp.com
  15. * @category Nette
  16. * @package Nette
  17. */
  18. /*namespace Nette;*/
  19. require_once dirname(__FILE__) . '/Component.php';
  20. require_once dirname(__FILE__) . '/IComponentContainer.php';
  21. /**
  22. * ComponentContainer is default implementation of IComponentContainer.
  23. *
  24. * @author David Grudl
  25. * @copyright Copyright (c) 2004, 2009 David Grudl
  26. * @package Nette
  27. *
  28. * @property-read \ArrayIterator $components
  29. */
  30. class ComponentContainer extends Component implements IComponentContainer
  31. {
  32. /** @var array of IComponent */
  33. private $components = array();
  34. /** @var IComponent|NULL */
  35. private $cloning;
  36. /********************* interface IComponentContainer ****************d*g**/
  37. /**
  38. * Adds the specified component to the IComponentContainer.
  39. * @param IComponent
  40. * @param string
  41. * @param string
  42. * @return void
  43. * @throws \InvalidStateException
  44. */
  45. public function addComponent(IComponent $component, $name, $insertBefore = NULL)
  46. {
  47. if ($name === NULL) {
  48. $name = $component->getName();
  49. }
  50. if (is_int($name)) {
  51. $name = (string) $name;
  52. } elseif (!is_string($name)) {
  53. throw new /*\*/InvalidArgumentException("Component name must be integer or string, " . gettype($name) . " given.");
  54. } elseif (!preg_match('#^[a-zA-Z0-9_]+$#', $name)) {
  55. throw new /*\*/InvalidArgumentException("Component name must be non-empty alphanumeric string, '$name' given.");
  56. }
  57. if (isset($this->components[$name])) {
  58. throw new /*\*/InvalidStateException("Component with name '$name' already exists.");
  59. }
  60. // check circular reference
  61. $obj = $this;
  62. do {
  63. if ($obj === $component) {
  64. throw new /*\*/InvalidStateException("Circular reference detected while adding component '$name'.");
  65. }
  66. $obj = $obj->getParent();
  67. } while ($obj !== NULL);
  68. // user checking
  69. $this->validateChildComponent($component);
  70. try {
  71. if (isset($this->components[$insertBefore])) {
  72. $tmp = array();
  73. foreach ($this->components as $k => $v) {
  74. if ($k === $insertBefore) $tmp[$name] = $component;
  75. $tmp[$k] = $v;
  76. }
  77. $this->components = $tmp;
  78. } else {
  79. $this->components[$name] = $component;
  80. }
  81. $component->setParent($this, $name);
  82. } catch (/*\*/Exception $e) {
  83. unset($this->components[$name]); // undo
  84. throw $e;
  85. }
  86. }
  87. /**
  88. * Removes a component from the IComponentContainer.
  89. * @param IComponent
  90. * @return void
  91. */
  92. public function removeComponent(IComponent $component)
  93. {
  94. $name = $component->getName();
  95. if (!isset($this->components[$name]) || $this->components[$name] !== $component) {
  96. throw new /*\*/InvalidArgumentException("Component named '$name' is not located in this container.");
  97. }
  98. unset($this->components[$name]);
  99. $component->setParent(NULL);
  100. }
  101. /**
  102. * Returns component specified by name or path.
  103. * @param string
  104. * @param bool throw exception if component doesn't exist?
  105. * @return IComponent|NULL
  106. */
  107. final public function getComponent($name, $need = TRUE)
  108. {
  109. if (is_int($name)) {
  110. $name = (string) $name;
  111. } elseif (!is_string($name)) {
  112. throw new /*\*/InvalidArgumentException("Component name must be integer or string, " . gettype($name) . " given.");
  113. } else {
  114. $a = strpos($name, self::NAME_SEPARATOR);
  115. if ($a !== FALSE) {
  116. $ext = (string) substr($name, $a + 1);
  117. $name = substr($name, 0, $a);
  118. }
  119. if ($name === '') {
  120. throw new /*\*/InvalidArgumentException("Component or subcomponent name must not be empty string.");
  121. }
  122. }
  123. if (!isset($this->components[$name])) {
  124. $this->createComponent($name);
  125. }
  126. if (isset($this->components[$name])) {
  127. if (!isset($ext)) {
  128. return $this->components[$name];
  129. } elseif ($this->components[$name] instanceof IComponentContainer) {
  130. return $this->components[$name]->getComponent($ext, $need);
  131. } elseif ($need) {
  132. throw new /*\*/InvalidArgumentException("Component with name '$name' is not container and cannot have '$ext' component.");
  133. }
  134. } elseif ($need) {
  135. throw new /*\*/InvalidArgumentException("Component with name '$name' does not exist.");
  136. }
  137. }
  138. /**
  139. * Component factory. Delegates the creation of components to a createComponent<Name> method.
  140. * @param string component name
  141. * @return void
  142. */
  143. protected function createComponent($name)
  144. {
  145. $ucname = ucfirst($name);
  146. $method = 'createComponent' . $ucname;
  147. if ($ucname !== $name && method_exists($this, $method) && $this->getReflection()->getMethod($method)->getName() === $method) {
  148. $component = $this->$method($name);
  149. if ($component instanceof IComponent && $component->getParent() === NULL) {
  150. $this->addComponent($component, $name);
  151. }
  152. }
  153. }
  154. /**
  155. * Iterates over a components.
  156. * @param bool recursive?
  157. * @param string class types filter
  158. * @return \ArrayIterator
  159. */
  160. final public function getComponents($deep = FALSE, $filterType = NULL)
  161. {
  162. $iterator = new RecursiveComponentIterator($this->components);
  163. if ($deep) {
  164. $deep = $deep > 0 ? /*\*/RecursiveIteratorIterator::SELF_FIRST : /*\*/RecursiveIteratorIterator::CHILD_FIRST;
  165. $iterator = new /*\*/RecursiveIteratorIterator($iterator, $deep);
  166. }
  167. if ($filterType) {
  168. /**/fixNamespace($filterType);/**/
  169. $iterator = new InstanceFilterIterator($iterator, $filterType);
  170. }
  171. return $iterator;
  172. }
  173. /**
  174. * Descendant can override this method to disallow insert a child by throwing an \InvalidStateException.
  175. * @param IComponent
  176. * @return void
  177. * @throws \InvalidStateException
  178. */
  179. protected function validateChildComponent(IComponent $child)
  180. {
  181. }
  182. /********************* cloneable, serializable ****************d*g**/
  183. /**
  184. * Object cloning.
  185. */
  186. public function __clone()
  187. {
  188. if ($this->components) {
  189. $oldMyself = reset($this->components)->getParent();
  190. $oldMyself->cloning = $this;
  191. foreach ($this->components as $name => $component) {
  192. $this->components[$name] = clone $component;
  193. }
  194. $oldMyself->cloning = NULL;
  195. }
  196. parent::__clone();
  197. }
  198. /**
  199. * Is container cloning now?
  200. * @return NULL|IComponent
  201. * @internal
  202. */
  203. public function isCloning()
  204. {
  205. return $this->cloning;
  206. }
  207. }
  208. /**
  209. * Recursive component iterator. See ComponentContainer::getComponents().
  210. *
  211. * @author David Grudl
  212. * @copyright Copyright (c) 2004, 2009 David Grudl
  213. * @package Nette
  214. */
  215. class RecursiveComponentIterator extends /*\*/RecursiveArrayIterator implements /*\*/Countable
  216. {
  217. /**
  218. * Has the current element has children?
  219. * @return bool
  220. */
  221. public function hasChildren()
  222. {
  223. return $this->current() instanceof IComponentContainer;
  224. }
  225. /**
  226. * The sub-iterator for the current element.
  227. * @return \RecursiveIterator
  228. */
  229. public function getChildren()
  230. {
  231. return $this->current()->getComponents();
  232. }
  233. /**
  234. * Returns the count of elements.
  235. * @return int
  236. */
  237. public function count()
  238. {
  239. return iterator_count($this);
  240. }
  241. }