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