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