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