1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14:
15: 16: 17: 18: 19: 20: 21: 22: 23: 24:
25: abstract class Component extends Object implements IComponent
26: {
27:
28: private $parent;
29:
30:
31: private $name;
32:
33:
34: private $monitors = array();
35:
36:
37:
38: 39:
40: public function __construct(IComponentContainer $parent = NULL, $name = NULL)
41: {
42: if ($parent !== NULL) {
43: $parent->addComponent($this, $name);
44:
45: } elseif (is_string($name)) {
46: $this->name = $name;
47: }
48: }
49:
50:
51:
52: 53: 54: 55: 56: 57:
58: public function lookup($type, $need = TRUE)
59: {
60: if ($a = strrpos($type, '\\')) $type = substr($type, $a + 1); 61: if (!isset($this->monitors[$type])) { 62: $obj = $this->parent;
63: $path = self::NAME_SEPARATOR . $this->name;
64: $depth = 1;
65: while ($obj !== NULL) {
66: if ($obj instanceof $type) break;
67: $path = self::NAME_SEPARATOR . $obj->getName() . $path;
68: $depth++;
69: $obj = $obj->getParent(); 70: if ($obj === $this) $obj = NULL; 71: }
72:
73: if ($obj) {
74: $this->monitors[$type] = array($obj, $depth, substr($path, 1), FALSE);
75:
76: } else {
77: $this->monitors[$type] = array(NULL, NULL, NULL, FALSE); 78: }
79: }
80:
81: if ($need && $this->monitors[$type][0] === NULL) {
82: throw new InvalidStateException("Component '$this->name' is not attached to '$type'.");
83: }
84:
85: return $this->monitors[$type][0];
86: }
87:
88:
89:
90: 91: 92: 93: 94: 95: 96:
97: public function lookupPath($type, $need = TRUE)
98: {
99: if ($a = strrpos($type, '\\')) $type = substr($type, $a + 1); 100: $this->lookup($type, $need);
101: return $this->monitors[$type][2];
102: }
103:
104:
105:
106: 107: 108: 109: 110:
111: public function monitor($type)
112: {
113: if ($a = strrpos($type, '\\')) $type = substr($type, $a + 1); 114: if (empty($this->monitors[$type][3])) {
115: if ($obj = $this->lookup($type, FALSE)) {
116: $this->attached($obj);
117: }
118: $this->monitors[$type][3] = TRUE; 119: }
120: }
121:
122:
123:
124: 125: 126: 127: 128:
129: public function unmonitor($type)
130: {
131: if ($a = strrpos($type, '\\')) $type = substr($type, $a + 1); 132: unset($this->monitors[$type]);
133: }
134:
135:
136:
137: 138: 139: 140: 141: 142:
143: protected function attached($obj)
144: {
145: }
146:
147:
148:
149: 150: 151: 152: 153: 154:
155: protected function detached($obj)
156: {
157: }
158:
159:
160:
161:
162:
163:
164:
165: 166: 167:
168: final public function getName()
169: {
170: return $this->name;
171: }
172:
173:
174:
175: 176: 177: 178:
179: final public function getParent()
180: {
181: return $this->parent;
182: }
183:
184:
185:
186: 187: 188: 189: 190: 191: 192: 193:
194: public function setParent(IComponentContainer $parent = NULL, $name = NULL)
195: {
196: if ($parent === NULL && $this->parent === NULL && $name !== NULL) {
197: $this->name = $name; 198: return $this;
199:
200: } elseif ($parent === $this->parent && $name === NULL) {
201: return $this; 202: }
203:
204: 205: if ($this->parent !== NULL && $parent !== NULL) {
206: throw new InvalidStateException("Component '$this->name' already has a parent.");
207: }
208:
209: 210: if ($parent === NULL) {
211: $this->refreshMonitors(0);
212: $this->parent = NULL;
213:
214: } else { 215: $this->validateParent($parent);
216: $this->parent = $parent;
217: if ($name !== NULL) $this->name = $name;
218:
219: $tmp = array();
220: $this->refreshMonitors(0, $tmp);
221: }
222: return $this;
223: }
224:
225:
226:
227: 228: 229: 230: 231: 232: 233:
234: protected function validateParent(IComponentContainer $parent)
235: {
236: }
237:
238:
239:
240: 241: 242: 243: 244: 245: 246:
247: private function refreshMonitors($depth, & $missing = NULL, & $listeners = array())
248: {
249: if ($this instanceof IComponentContainer) {
250: foreach ($this->getComponents() as $component) {
251: if ($component instanceof Component) {
252: $component->refreshMonitors($depth + 1, $missing, $listeners);
253: }
254: }
255: }
256:
257: if ($missing === NULL) { 258: foreach ($this->monitors as $type => $rec) {
259: if (isset($rec[1]) && $rec[1] > $depth) {
260: if ($rec[3]) { 261: $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
262: $listeners[] = array($this, $rec[0]);
263: } else { 264: unset($this->monitors[$type]);
265: }
266: }
267: }
268:
269: } else { 270: foreach ($this->monitors as $type => $rec) {
271: if (isset($rec[0])) { 272: continue;
273:
274: } elseif (!$rec[3]) { 275: unset($this->monitors[$type]);
276:
277: } elseif (isset($missing[$type])) { 278: $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
279:
280: } else {
281: $this->monitors[$type] = NULL; 282: if ($obj = $this->lookup($type, FALSE)) {
283: $listeners[] = array($this, $obj);
284: } else {
285: $missing[$type] = TRUE;
286: }
287: $this->monitors[$type][3] = TRUE; 288: }
289: }
290: }
291:
292: if ($depth === 0) { 293: $method = $missing === NULL ? 'detached' : 'attached';
294: foreach ($listeners as $item) {
295: $item[0]->$method($item[1]);
296: }
297: }
298: }
299:
300:
301:
302:
303:
304:
305:
306: 307: 308:
309: public function __clone()
310: {
311: if ($this->parent === NULL) {
312: return;
313:
314: } elseif ($this->parent instanceof ComponentContainer) {
315: $this->parent = $this->parent->_isCloning();
316: if ($this->parent === NULL) { 317: $this->refreshMonitors(0);
318: }
319:
320: } else {
321: $this->parent = NULL;
322: $this->refreshMonitors(0);
323: }
324: }
325:
326:
327:
328: 329: 330:
331: final public function __wakeup()
332: {
333: throw new NotImplementedException;
334: }
335:
336: }
337: