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: