PageRenderTime 43ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/www/libs/nette-dev/ComponentContainer.php

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