1: <?php
  2: 
  3:   4:   5:   6: 
  7: 
  8: namespace Nette\ComponentModel;
  9: 
 10: use Nette;
 11: 
 12: 
 13:  14:  15:  16:  17: 
 18: class Container extends Component implements IContainer
 19: {
 20:     
 21:     private $components = array();
 22: 
 23:     
 24:     private $cloning;
 25: 
 26: 
 27:     
 28: 
 29: 
 30:      31:  32:  33:  34:  35:  36:  37: 
 38:     public function addComponent(IComponent $component, $name, $insertBefore = NULL)
 39:     {
 40:         if ($name === NULL) {
 41:             $name = $component->getName();
 42:         }
 43: 
 44:         if (is_int($name)) {
 45:             $name = (string) $name;
 46: 
 47:         } elseif (!is_string($name)) {
 48:             throw new Nette\InvalidArgumentException(sprintf('Component name must be integer or string, %s given.', gettype($name)));
 49: 
 50:         } elseif (!preg_match('#^[a-zA-Z0-9_]+\z#', $name)) {
 51:             throw new Nette\InvalidArgumentException("Component name must be non-empty alphanumeric string, '$name' given.");
 52:         }
 53: 
 54:         if (isset($this->components[$name])) {
 55:             throw new Nette\InvalidStateException("Component with name '$name' already exists.");
 56:         }
 57: 
 58:         
 59:         $obj = $this;
 60:         do {
 61:             if ($obj === $component) {
 62:                 throw new Nette\InvalidStateException("Circular reference detected while adding component '$name'.");
 63:             }
 64:             $obj = $obj->getParent();
 65:         } while ($obj !== NULL);
 66: 
 67:         
 68:         $this->validateChildComponent($component);
 69: 
 70:         try {
 71:             if (isset($this->components[$insertBefore])) {
 72:                 $tmp = array();
 73:                 foreach ($this->components as $k => $v) {
 74:                     if ($k === $insertBefore) {
 75:                         $tmp[$name] = $component;
 76:                     }
 77:                     $tmp[$k] = $v;
 78:                 }
 79:                 $this->components = $tmp;
 80:             } else {
 81:                 $this->components[$name] = $component;
 82:             }
 83:             $component->setParent($this, $name);
 84: 
 85:         } catch (\Exception $e) {
 86:             unset($this->components[$name]); 
 87:             throw $e;
 88:         }
 89:         return $this;
 90:     }
 91: 
 92: 
 93:      94:  95:  96: 
 97:     public function removeComponent(IComponent $component)
 98:     {
 99:         $name = $component->getName();
100:         if (!isset($this->components[$name]) || $this->components[$name] !== $component) {
101:             throw new Nette\InvalidArgumentException("Component named '$name' is not located in this container.");
102:         }
103: 
104:         unset($this->components[$name]);
105:         $component->setParent(NULL);
106:     }
107: 
108: 
109:     110: 111: 112: 113: 114: 
115:     public function getComponent($name, $need = TRUE)
116:     {
117:         if (isset($this->components[$name])) {
118:             return $this->components[$name];
119:         }
120: 
121:         if (is_int($name)) {
122:             $name = (string) $name;
123: 
124:         } elseif (!is_string($name)) {
125:             throw new Nette\InvalidArgumentException(sprintf('Component name must be integer or string, %s given.', gettype($name)));
126: 
127:         } else {
128:             $a = strpos($name, self::NAME_SEPARATOR);
129:             if ($a !== FALSE) {
130:                 $ext = (string) substr($name, $a + 1);
131:                 $name = substr($name, 0, $a);
132:             }
133: 
134:             if ($name === '') {
135:                 if ($need) {
136:                     throw new Nette\InvalidArgumentException('Component or subcomponent name must not be empty string.');
137:                 }
138:                 return;
139:             }
140:         }
141: 
142:         if (!isset($this->components[$name])) {
143:             $component = $this->createComponent($name);
144:             if ($component) {
145:                 if (!$component instanceof IComponent) {
146:                     throw new Nette\UnexpectedValueException('Method createComponent() did not return Nette\ComponentModel\IComponent.');
147: 
148:                 } elseif (!isset($this->components[$name])) {
149:                     $this->addComponent($component, $name);
150:                 }
151:             }
152:         }
153: 
154:         if (isset($this->components[$name])) {
155:             if (!isset($ext)) {
156:                 return $this->components[$name];
157: 
158:             } elseif ($this->components[$name] instanceof IContainer) {
159:                 return $this->components[$name]->getComponent($ext, $need);
160: 
161:             } elseif ($need) {
162:                 throw new Nette\InvalidArgumentException("Component with name '$name' is not container and cannot have '$ext' component.");
163:             }
164: 
165:         } elseif ($need) {
166:             $hint = Nette\Utils\ObjectMixin::getSuggestion(array_merge(
167:                 array_keys($this->components),
168:                 array_map('lcfirst', preg_filter('#^createComponent([A-Z0-9].*)#', '$1', get_class_methods($this)))
169:             ), $name);
170:             throw new Nette\InvalidArgumentException("Component with name '$name' does not exist" . ($hint ? ", did you mean '$hint'?" : '.'));
171:         }
172:     }
173: 
174: 
175:     176: 177: 178: 179: 
180:     protected function createComponent($name)
181:     {
182:         $ucname = ucfirst($name);
183:         $method = 'createComponent' . $ucname;
184:         if ($ucname !== $name && method_exists($this, $method) && $this->getReflection()->getMethod($method)->getName() === $method) {
185:             $component = $this->$method($name);
186:             if (!$component instanceof IComponent && !isset($this->components[$name])) {
187:                 $class = get_class($this);
188:                 throw new Nette\UnexpectedValueException("Method $class::$method() did not return or create the desired component.");
189:             }
190:             return $component;
191:         }
192:     }
193: 
194: 
195:     196: 197: 198: 199: 200: 
201:     public function getComponents($deep = FALSE, $filterType = NULL)
202:     {
203:         $iterator = new RecursiveComponentIterator($this->components);
204:         if ($deep) {
205:             $deep = $deep > 0 ? \RecursiveIteratorIterator::SELF_FIRST : \RecursiveIteratorIterator::CHILD_FIRST;
206:             $iterator = new \RecursiveIteratorIterator($iterator, $deep);
207:         }
208:         if ($filterType) {
209:             $class = PHP_VERSION_ID < 50400 ? 'Nette\Iterators\Filter' : 'CallbackFilterIterator';
210:             $iterator = new $class($iterator, function ($item) use ($filterType) {
211:                 return $item instanceof $filterType;
212:             });
213:         }
214:         return $iterator;
215:     }
216: 
217: 
218:     219: 220: 221: 222: 
223:     protected function validateChildComponent(IComponent $child)
224:     {
225:     }
226: 
227: 
228:     
229: 
230: 
231:     232: 233: 
234:     public function __clone()
235:     {
236:         if ($this->components) {
237:             $oldMyself = reset($this->components)->getParent();
238:             $oldMyself->cloning = $this;
239:             foreach ($this->components as $name => $component) {
240:                 $this->components[$name] = clone $component;
241:             }
242:             $oldMyself->cloning = NULL;
243:         }
244:         parent::__clone();
245:     }
246: 
247: 
248:     249: 250: 251: 252: 
253:     public function _isCloning()
254:     {
255:         return $this->cloning;
256:     }
257: 
258: }
259: