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:
195: public function setParent(IComponentContainer $parent = NULL, $name = NULL)
196: {
197: if ($parent === NULL && $this->parent === NULL && $name !== NULL) {
198: $this->name = $name; 199: return $this;
200:
201: } elseif ($parent === $this->parent && $name === NULL) {
202: return $this; 203: }
204:
205: 206: if ($this->parent !== NULL && $parent !== NULL) {
207: throw new InvalidStateException("Component '$this->name' already has a parent.");
208: }
209:
210: 211: if ($parent === NULL) {
212: $this->refreshMonitors(0);
213: $this->parent = NULL;
214:
215: } else { 216: $this->validateParent($parent);
217: $this->parent = $parent;
218: if ($name !== NULL) $this->name = $name;
219:
220: $tmp = array();
221: $this->refreshMonitors(0, $tmp);
222: }
223: return $this;
224: }
225:
226:
227:
228: 229: 230: 231: 232: 233: 234:
235: protected function validateParent(IComponentContainer $parent)
236: {
237: }
238:
239:
240:
241: 242: 243: 244: 245: 246: 247:
248: private function refreshMonitors($depth, & $missing = NULL, & $listeners = array())
249: {
250: if ($this instanceof IComponentContainer) {
251: foreach ($this->getComponents() as $component) {
252: if ($component instanceof Component) {
253: $component->refreshMonitors($depth + 1, $missing, $listeners);
254: }
255: }
256: }
257:
258: if ($missing === NULL) { 259: foreach ($this->monitors as $type => $rec) {
260: if (isset($rec[1]) && $rec[1] > $depth) {
261: if ($rec[3]) { 262: $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
263: $listeners[] = array($this, $rec[0]);
264: } else { 265: unset($this->monitors[$type]);
266: }
267: }
268: }
269:
270: } else { 271: foreach ($this->monitors as $type => $rec) {
272: if (isset($rec[0])) { 273: continue;
274:
275: } elseif (!$rec[3]) { 276: unset($this->monitors[$type]);
277:
278: } elseif (isset($missing[$type])) { 279: $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
280:
281: } else {
282: $this->monitors[$type] = NULL; 283: if ($obj = $this->lookup($type, FALSE)) {
284: $listeners[] = array($this, $obj);
285: } else {
286: $missing[$type] = TRUE;
287: }
288: $this->monitors[$type][3] = TRUE; 289: }
290: }
291: }
292:
293: if ($depth === 0) { 294: $method = $missing === NULL ? 'detached' : 'attached';
295: foreach ($listeners as $item) {
296: $item[0]->$method($item[1]);
297: }
298: }
299: }
300:
301:
302:
303:
304:
305:
306:
307: 308: 309:
310: public function __clone()
311: {
312: if ($this->parent === NULL) {
313: return;
314:
315: } elseif ($this->parent instanceof ComponentContainer) {
316: $this->parent = $this->parent->_isCloning();
317: if ($this->parent === NULL) { 318: $this->refreshMonitors(0);
319: }
320:
321: } else {
322: $this->parent = NULL;
323: $this->refreshMonitors(0);
324: }
325: }
326:
327:
328:
329: 330: 331:
332: final public function __wakeup()
333: {
334: throw new NotImplementedException;
335: }
336:
337: }
338: