Source for file ComponentContainer.php

Documentation is available at ComponentContainer.php

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