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