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 (is_int($name)) {
118: $name = (string) $name;
119:
120: } elseif (!is_string($name)) {
121: throw new Nette\InvalidArgumentException(sprintf('Component name must be integer or string, %s given.', gettype($name)));
122:
123: } else {
124: $a = strpos($name, self::NAME_SEPARATOR);
125: if ($a !== FALSE) {
126: $ext = (string) substr($name, $a + 1);
127: $name = substr($name, 0, $a);
128: }
129:
130: if ($name === '') {
131: if ($need) {
132: throw new Nette\InvalidArgumentException('Component or subcomponent name must not be empty string.');
133: }
134: return;
135: }
136: }
137:
138: if (!isset($this->components[$name])) {
139: $component = $this->createComponent($name);
140: if ($component) {
141: if (!$component instanceof IComponent) {
142: throw new Nette\UnexpectedValueException('Method createComponent() did not return Nette\ComponentModel\IComponent.');
143:
144: } elseif (!isset($this->components[$name])) {
145: $this->addComponent($component, $name);
146: }
147: }
148: }
149:
150: if (isset($this->components[$name])) {
151: if (!isset($ext)) {
152: return $this->components[$name];
153:
154: } elseif ($this->components[$name] instanceof IContainer) {
155: return $this->components[$name]->getComponent($ext, $need);
156:
157: } elseif ($need) {
158: throw new Nette\InvalidArgumentException("Component with name '$name' is not container and cannot have '$ext' component.");
159: }
160:
161: } elseif ($need) {
162: $hint = Nette\Utils\ObjectMixin::getSuggestion(array_merge(
163: array_keys($this->components),
164: array_map('lcfirst', preg_filter('#^createComponent([A-Z0-9].*)#', '$1', get_class_methods($this)))
165: ), $name);
166: throw new Nette\InvalidArgumentException("Component with name '$name' does not exist" . ($hint ? ", did you mean '$hint'?" : '.'));
167: }
168: }
169:
170:
171: 172: 173: 174: 175:
176: protected function createComponent($name)
177: {
178: $ucname = ucfirst($name);
179: $method = 'createComponent' . $ucname;
180: if ($ucname !== $name && method_exists($this, $method) && $this->getReflection()->getMethod($method)->getName() === $method) {
181: $component = $this->$method($name);
182: if (!$component instanceof IComponent && !isset($this->components[$name])) {
183: $class = get_class($this);
184: throw new Nette\UnexpectedValueException("Method $class::$method() did not return or create the desired component.");
185: }
186: return $component;
187: }
188: }
189:
190:
191: 192: 193: 194: 195: 196:
197: public function getComponents($deep = FALSE, $filterType = NULL)
198: {
199: $iterator = new RecursiveComponentIterator($this->components);
200: if ($deep) {
201: $deep = $deep > 0 ? \RecursiveIteratorIterator::SELF_FIRST : \RecursiveIteratorIterator::CHILD_FIRST;
202: $iterator = new \RecursiveIteratorIterator($iterator, $deep);
203: }
204: if ($filterType) {
205: $class = PHP_VERSION_ID < 50400 ? 'Nette\Iterators\Filter' : 'CallbackFilterIterator';
206: $iterator = new $class($iterator, function ($item) use ($filterType) {
207: return $item instanceof $filterType;
208: });
209: }
210: return $iterator;
211: }
212:
213:
214: 215: 216: 217: 218:
219: protected function validateChildComponent(IComponent $child)
220: {
221: }
222:
223:
224:
225:
226:
227: 228: 229:
230: public function __clone()
231: {
232: if ($this->components) {
233: $oldMyself = reset($this->components)->getParent();
234: $oldMyself->cloning = $this;
235: foreach ($this->components as $name => $component) {
236: $this->components[$name] = clone $component;
237: }
238: $oldMyself->cloning = NULL;
239: }
240: parent::__clone();
241: }
242:
243:
244: 245: 246: 247: 248:
249: public function _isCloning()
250: {
251: return $this->cloning;
252: }
253:
254: }
255: