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 (!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) {
67: break;
68: }
69: $path = self::NAME_SEPARATOR . $obj->getName() . $path;
70: $depth++;
71: $obj = $obj->getParent();
72: if ($obj === $this) {
73: $obj = NULL;
74: }
75: }
76:
77: if ($obj) {
78: $this->monitors[$type] = array($obj, $depth, substr($path, 1), FALSE);
79:
80: } else {
81: $this->monitors[$type] = array(NULL, NULL, NULL, FALSE);
82: }
83: }
84:
85: if ($need && $this->monitors[$type][0] === NULL) {
86: throw new InvalidStateException("Component '$this->name' is not attached to '$type'.");
87: }
88:
89: return $this->monitors[$type][0];
90: }
91:
92:
93:
94: 95: 96: 97: 98: 99: 100:
101: public function lookupPath($type, $need = TRUE)
102: {
103: $this->lookup($type, $need);
104: return $this->monitors[$type][2];
105: }
106:
107:
108:
109: 110: 111: 112: 113:
114: public function monitor($type)
115: {
116: if (empty($this->monitors[$type][3])) {
117: if ($obj = $this->lookup($type, FALSE)) {
118: $this->attached($obj);
119: }
120: $this->monitors[$type][3] = TRUE;
121: }
122: }
123:
124:
125:
126: 127: 128: 129: 130:
131: public function unmonitor($type)
132: {
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:
196: public function setParent(IComponentContainer $parent = NULL, $name = NULL)
197: {
198: if ($parent === NULL && $this->parent === NULL && $name !== NULL) {
199: $this->name = $name;
200: return $this;
201:
202: } elseif ($parent === $this->parent && $name === NULL) {
203: return $this;
204: }
205:
206:
207: if ($this->parent !== NULL && $parent !== NULL) {
208: throw new InvalidStateException("Component '$this->name' already has a parent.");
209: }
210:
211:
212: if ($parent === NULL) {
213: $this->refreshMonitors(0);
214: $this->parent = NULL;
215:
216: } else {
217: $this->validateParent($parent);
218: $this->parent = $parent;
219: if ($name !== NULL) {
220: $this->name = $name;
221: }
222:
223: $tmp = array();
224: $this->refreshMonitors(0, $tmp);
225: }
226: return $this;
227: }
228:
229:
230:
231: 232: 233: 234: 235: 236:
237: protected function validateParent(IComponentContainer $parent)
238: {
239: }
240:
241:
242:
243: 244: 245: 246: 247: 248: 249:
250: private function refreshMonitors($depth, & $missing = NULL, & $listeners = array())
251: {
252: if ($this instanceof IComponentContainer) {
253: foreach ($this->getComponents() as $component) {
254: if ($component instanceof NComponent) {
255: $component->refreshMonitors($depth + 1, $missing, $listeners);
256: }
257: }
258: }
259:
260: if ($missing === NULL) {
261: foreach ($this->monitors as $type => $rec) {
262: if (isset($rec[1]) && $rec[1] > $depth) {
263: if ($rec[3]) {
264: $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
265: $listeners[] = array($this, $rec[0]);
266: } else {
267: unset($this->monitors[$type]);
268: }
269: }
270: }
271:
272: } else {
273: foreach ($this->monitors as $type => $rec) {
274: if (isset($rec[0])) {
275: continue;
276:
277: } elseif (!$rec[3]) {
278: unset($this->monitors[$type]);
279:
280: } elseif (isset($missing[$type])) {
281: $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
282:
283: } else {
284: $this->monitors[$type] = NULL;
285: if ($obj = $this->lookup($type, FALSE)) {
286: $listeners[] = array($this, $obj);
287: } else {
288: $missing[$type] = TRUE;
289: }
290: $this->monitors[$type][3] = TRUE;
291: }
292: }
293: }
294:
295: if ($depth === 0) {
296: $method = $missing === NULL ? 'detached' : 'attached';
297: foreach ($listeners as $item) {
298: $item[0]->$method($item[1]);
299: }
300: }
301: }
302:
303:
304:
305:
306:
307:
308:
309: 310: 311:
312: public function __clone()
313: {
314: if ($this->parent === NULL) {
315: return;
316:
317: } elseif ($this->parent instanceof NComponentContainer) {
318: $this->parent = $this->parent->_isCloning();
319: if ($this->parent === NULL) {
320: $this->refreshMonitors(0);
321: }
322:
323: } else {
324: $this->parent = NULL;
325: $this->refreshMonitors(0);
326: }
327: }
328:
329:
330:
331: 332: 333:
334: final public function __sleep()
335: {
336: throw new NotImplementedException('Object serialization is not supported by class ' . get_class($this));
337: }
338:
339:
340:
341: 342: 343:
344: final public function __wakeup()
345: {
346: throw new NotImplementedException('Object unserialization is not supported by class ' . get_class($this));
347: }
348:
349: }
350: