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