PluginProbe
Packeta / 2.1
Packeta v2.1
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / deps / nette / component-model / src / ComponentModel / Component.php

Component.php in Packeta 2.1, at deps/nette/component-model/src/ComponentModel/Component.php

272 lines 9.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * This file is part of the Nette Framework (https://nette.org)
5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6 */
7 declare (strict_types=1);
8 namespace Packetery\Nette\ComponentModel;
9
10 use Packetery\Nette;
11 /**
12 * Component is the base class for all components.
13 *
14 * Components are objects implementing IComponent. They has parent component and own name.
15 *
16 * @property-read string $name
17 * @property-read IContainer|null $parent
18 */
19 abstract class Component implements IComponent
20 {
21 use \Packetery\Nette\SmartObject;
22 /** @var IContainer|null */
23 private $parent;
24 /** @var string|null */
25 private $name;
26 /** @var array<string, array{?IComponent, ?int, ?string, array<int, array{?callable, ?callable}>}> means [type => [obj, depth, path, [attached, detached]]] */
27 private $monitors = [];
28 /**
29 * Finds the closest ancestor specified by class or interface name.
30 * @param bool $throw throw exception if component doesn't exist?
31 */
32 public final function lookup(?string $type, bool $throw = \true) : ?IComponent
33 {
34 if (!isset($this->monitors[$type])) {
35 // not monitored or not processed yet
36 $obj = $this->parent;
37 $path = self::NameSeparator . $this->name;
38 $depth = 1;
39 while ($obj !== null) {
40 $parent = $obj->getParent();
41 if ($type ? $obj instanceof $type : $parent === null) {
42 break;
43 }
44 $path = self::NameSeparator . $obj->getName() . $path;
45 $depth++;
46 $obj = $parent;
47 // IComponent::getParent()
48 if ($obj === $this) {
49 $obj = null;
50 // prevent cycling
51 }
52 }
53 if ($obj) {
54 $this->monitors[$type] = [$obj, $depth, \substr($path, 1), []];
55 } else {
56 $this->monitors[$type] = [null, null, null, []];
57 // not found
58 }
59 }
60 if ($throw && $this->monitors[$type][0] === null) {
61 $message = $this->name !== null ? "Component '{$this->name}' is not attached to '{$type}'." : "Component of type '" . static::class . "' is not attached to '{$type}'.";
62 throw new \Packetery\Nette\InvalidStateException($message);
63 }
64 return $this->monitors[$type][0];
65 }
66 /**
67 * Finds the closest ancestor specified by class or interface name and returns backtrace path.
68 * A path is the concatenation of component names separated by self::NAME_SEPARATOR.
69 */
70 public final function lookupPath(?string $type = null, bool $throw = \true) : ?string
71 {
72 $this->lookup($type, $throw);
73 return $this->monitors[$type][2];
74 }
75 /**
76 * Starts monitoring of ancestors.
77 */
78 public final function monitor(string $type, ?callable $attached = null, ?callable $detached = null) : void
79 {
80 if (\func_num_args() === 1) {
81 $attached = [$this, 'attached'];
82 $detached = [$this, 'detached'];
83 }
84 if (($obj = $this->lookup($type, \false)) && $attached && !\in_array([$attached, $detached], $this->monitors[$type][3], \true)) {
85 $attached($obj);
86 }
87 $this->monitors[$type][3][] = [$attached, $detached];
88 // mark as monitored
89 }
90 /**
91 * Stops monitoring of ancestors.
92 */
93 public final function unmonitor(string $type) : void
94 {
95 unset($this->monitors[$type]);
96 }
97 /**
98 * This method will be called when the component (or component's parent)
99 * becomes attached to a monitored object. Do not call this method yourself.
100 * @deprecated use monitor($type, $attached)
101 */
102 protected function attached(IComponent $obj) : void
103 {
104 }
105 /**
106 * This method will be called before the component (or component's parent)
107 * becomes detached from a monitored object. Do not call this method yourself.
108 * @deprecated use monitor($type, null, $detached)
109 */
110 protected function detached(IComponent $obj) : void
111 {
112 }
113 /********************* interface IComponent ****************d*g**/
114 public final function getName() : ?string
115 {
116 return $this->name;
117 }
118 /**
119 * Returns the parent container if any.
120 */
121 public final function getParent() : ?IContainer
122 {
123 return $this->parent;
124 }
125 /**
126 * Sets or removes the parent of this component. This method is managed by containers and should
127 * not be called by applications
128 * @return static
129 * @throws \Packetery\Nette\InvalidStateException
130 * @internal
131 */
132 public function setParent(?IContainer $parent, ?string $name = null)
133 {
134 if ($parent === null && $this->parent === null && $name !== null) {
135 $this->name = $name;
136 // just rename
137 return $this;
138 } elseif ($parent === $this->parent && $name === null) {
139 return $this;
140 // nothing to do
141 }
142 // A component cannot be given a parent if it already has a parent.
143 if ($this->parent !== null && $parent !== null) {
144 throw new \Packetery\Nette\InvalidStateException("Component '{$this->name}' already has a parent.");
145 }
146 // remove from parent?
147 if ($parent === null) {
148 $this->refreshMonitors(0);
149 $this->parent = null;
150 } else {
151 // add to parent
152 $this->validateParent($parent);
153 $this->parent = $parent;
154 if ($name !== null) {
155 $this->name = $name;
156 }
157 $tmp = [];
158 $this->refreshMonitors(0, $tmp);
159 }
160 return $this;
161 }
162 /**
163 * Is called by a component when it is about to be set new parent. Descendant can
164 * override this method to disallow a parent change by throwing an \Packetery\Nette\InvalidStateException
165 * @throws \Packetery\Nette\InvalidStateException
166 */
167 protected function validateParent(IContainer $parent) : void
168 {
169 }
170 /**
171 * Refreshes monitors.
172 * @param array<string,true>|null $missing (array = attaching, null = detaching)
173 * @param array<int,array{callable,IComponent}> $listeners
174 */
175 private function refreshMonitors(int $depth, ?array &$missing = null, array &$listeners = []) : void
176 {
177 if ($this instanceof IContainer) {
178 foreach ($this->getComponents() as $component) {
179 if ($component instanceof self) {
180 $component->refreshMonitors($depth + 1, $missing, $listeners);
181 }
182 }
183 }
184 if ($missing === null) {
185 // detaching
186 foreach ($this->monitors as $type => $rec) {
187 if (isset($rec[1]) && $rec[1] > $depth) {
188 if ($rec[3]) {
189 // monitored
190 $this->monitors[$type] = [null, null, null, $rec[3]];
191 foreach ($rec[3] as $pair) {
192 $listeners[] = [$pair[1], $rec[0]];
193 }
194 } else {
195 // not monitored, just randomly cached
196 unset($this->monitors[$type]);
197 }
198 }
199 }
200 } else {
201 // attaching
202 foreach ($this->monitors as $type => $rec) {
203 if (isset($rec[0])) {
204 // is in cache yet
205 continue;
206 } elseif (!$rec[3]) {
207 // not monitored, just randomly cached
208 unset($this->monitors[$type]);
209 } elseif (isset($missing[$type])) {
210 // known from previous lookup
211 $this->monitors[$type] = [null, null, null, $rec[3]];
212 } else {
213 unset($this->monitors[$type]);
214 // forces re-lookup
215 if ($obj = $this->lookup($type, \false)) {
216 foreach ($rec[3] as $pair) {
217 $listeners[] = [$pair[0], $obj];
218 }
219 } else {
220 $missing[$type] = \true;
221 }
222 $this->monitors[$type][3] = $rec[3];
223 // mark as monitored
224 }
225 }
226 }
227 if ($depth === 0) {
228 // call listeners
229 $prev = [];
230 foreach ($listeners as $item) {
231 if ($item[0] && !\in_array($item, $prev, \true)) {
232 $item[0]($item[1]);
233 $prev[] = $item;
234 }
235 }
236 }
237 }
238 /********************* cloneable, serializable ****************d*g**/
239 /**
240 * Object cloning.
241 */
242 public function __clone()
243 {
244 if ($this->parent === null) {
245 return;
246 } elseif ($this->parent instanceof Container) {
247 $this->parent = $this->parent->_isCloning();
248 if ($this->parent === null) {
249 // not cloning
250 $this->refreshMonitors(0);
251 }
252 } else {
253 $this->parent = null;
254 $this->refreshMonitors(0);
255 }
256 }
257 /**
258 * Prevents serialization.
259 */
260 public final function __sleep()
261 {
262 throw new \Packetery\Nette\NotImplementedException('Object serialization is not supported by class ' . static::class);
263 }
264 /**
265 * Prevents unserialization.
266 */
267 public final function __wakeup()
268 {
269 throw new \Packetery\Nette\NotImplementedException('Object unserialization is not supported by class ' . static::class);
270 }
271 }
272