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