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:
26: abstract class NComponent extends NObject implements IComponent
27: {
28:
29: private $parent;
30:
31:
32: private $name;
33:
34:
35: private $monitors = array();
36:
37:
38:
39: 40:
41: public function __construct(IComponentContainer $parent = NULL, $name = NULL)
42: {
43: if ($parent !== NULL) {
44: $parent->addComponent($this, $name);
45:
46: } elseif (is_string($name)) {
47: $this->name = $name;
48: }
49: }
50:
51:
52:
53: 54: 55: 56: 57: 58:
59: public function lookup($type, $need = TRUE)
60: {
61: if ($a = strrpos($type, '\\')) $type = substr($type, $a + 1);
62: if (!isset($this->monitors[$type])) {
63: $obj = $this->parent;
64: $path = self::NAME_SEPARATOR . $this->name;
65: $depth = 1;
66: while ($obj !== NULL) {
67: if ($obj instanceof $type) break;
68: $path = self::NAME_SEPARATOR . $obj->getName() . $path;
69: $depth++;
70: $obj = $obj->getParent();
71: if ($obj === $this) $obj = NULL;
72: }
73:
74: if ($obj) {
75: $this->monitors[$type] = array($obj, $depth, substr($path, 1), FALSE);
76:
77: } else {
78: $this->monitors[$type] = array(NULL, NULL, NULL, FALSE);
79: }
80: }
81:
82: if ($need && $this->monitors[$type][0] === NULL) {
83: throw new InvalidStateException("Component '$this->name' is not attached to '$type'.");
84: }
85:
86: return $this->monitors[$type][0];
87: }
88:
89:
90:
91: 92: 93: 94: 95: 96: 97:
98: public function lookupPath($type, $need = TRUE)
99: {
100: if ($a = strrpos($type, '\\')) $type = substr($type, $a + 1);
101: $this->lookup($type, $need);
102: return $this->monitors[$type][2];
103: }
104:
105:
106:
107: 108: 109: 110: 111:
112: public function monitor($type)
113: {
114: if ($a = strrpos($type, '\\')) $type = substr($type, $a + 1);
115: if (empty($this->monitors[$type][3])) {
116: if ($obj = $this->lookup($type, FALSE)) {
117: $this->attached($obj);
118: }
119: $this->monitors[$type][3] = TRUE;
120: }
121: }
122:
123:
124:
125: 126: 127: 128: 129:
130: public function unmonitor($type)
131: {
132: if ($a = strrpos($type, '\\')) $type = substr($type, $a + 1);
133: unset($this->monitors[$type]);
134: }
135:
136:
137:
138: 139: 140: 141: 142: 143:
144: protected function attached($obj)
145: {
146: }
147:
148:
149:
150: 151: 152: 153: 154: 155:
156: protected function detached($obj)
157: {
158: }
159:
160:
161:
162:
163:
164:
165:
166: 167: 168:
169: final public function getName()
170: {
171: return $this->name;
172: }
173:
174:
175:
176: 177: 178: 179:
180: final public function getParent()
181: {
182: return $this->parent;
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 NComponent) {
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 NComponentContainer) {
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: