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