Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
      • Traits
    • Reflection
    • Security
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

  • Component
  • Container
  • RecursiveComponentIterator

Interfaces

  • IComponent
  • IContainer

Traits

  • ArrayAccess
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  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: 
  8: namespace Nette\ComponentModel;
  9: 
 10: use Nette;
 11: 
 12: 
 13: /**
 14:  * Component is the base class for all components.
 15:  *
 16:  * Components are objects implementing IComponent. They has parent component and own name.
 17:  *
 18:  * @property-read string $name
 19:  * @property-read IContainer|null $parent
 20:  */
 21: abstract class Component implements IComponent
 22: {
 23:     use Nette\SmartObject;
 24: 
 25:     /** @var IContainer|null */
 26:     private $parent;
 27: 
 28:     /** @var string|null */
 29:     private $name;
 30: 
 31:     /** @var array of [type => [obj, depth, path, array of [attached, detached]]] */
 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:      * Finds the closest ancestor specified by class or interface name.
 51:      * @param  string|null  $type
 52:      * @param  bool  $throw
 53:      * @return IComponent|null
 54:      */
 55:     public function lookup($type, $throw = true)
 56:     {
 57:         if (!isset($this->monitors[$type])) { // not monitored or not processed yet
 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; // IComponent::getParent()
 69:                 if ($obj === $this) {
 70:                     $obj = null; // prevent cycling
 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, []]; // not found
 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:      * Finds the closest ancestor specified by class or interface name and returns backtrace path.
 92:      * A path is the concatenation of component names separated by self::NAME_SEPARATOR.
 93:      * @param  string|null  $type
 94:      * @param  bool  $throw
 95:      * @return string|null
 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:      * Starts monitoring of ancestors.
106:      * @param  string  $type
107:      * @return void
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]; // mark as monitored
119:     }
120: 
121: 
122:     /**
123:      * Stops monitoring of ancestors.
124:      * @param  string  $type
125:      * @return void
126:      */
127:     public function unmonitor($type)
128:     {
129:         unset($this->monitors[$type]);
130:     }
131: 
132: 
133:     /**
134:      * This method will be called when the component (or component's parent)
135:      * becomes attached to a monitored object. Do not call this method yourself.
136:      * @param  IComponent  $obj
137:      * @return void
138:      * @deprecated  use monitor($type, $attached)
139:      */
140:     protected function attached($obj)
141:     {
142:     }
143: 
144: 
145:     /**
146:      * This method will be called before the component (or component's parent)
147:      * becomes detached from a monitored object. Do not call this method yourself.
148:      * @param  IComponent  $obj
149:      * @return void
150:      * @deprecated  use monitor($type, null, $detached)
151:      */
152:     protected function detached($obj)
153:     {
154:     }
155: 
156: 
157:     /********************* interface IComponent ****************d*g**/
158: 
159: 
160:     /**
161:      * @return string|null
162:      */
163:     public function getName()
164:     {
165:         return $this->name;
166:     }
167: 
168: 
169:     /**
170:      * Returns the parent container if any.
171:      * @return IContainer|null
172:      */
173:     public function getParent()
174:     {
175:         return $this->parent;
176:     }
177: 
178: 
179:     /**
180:      * Sets or removes the parent of this component. This method is managed by containers and should
181:      * not be called by applications
182:      * @param  string  $name
183:      * @return static
184:      * @throws Nette\InvalidStateException
185:      * @internal
186:      */
187:     public function setParent(IContainer $parent = null, $name = null)
188:     {
189:         if ($parent === null && $this->parent === null && $name !== null) {
190:             $this->name = $name; // just rename
191:             return $this;
192: 
193:         } elseif ($parent === $this->parent && $name === null) {
194:             return $this; // nothing to do
195:         }
196: 
197:         // A component cannot be given a parent if it already has a parent.
198:         if ($this->parent !== null && $parent !== null) {
199:             throw new Nette\InvalidStateException("Component '$this->name' already has a parent.");
200:         }
201: 
202:         // remove from parent?
203:         if ($parent === null) {
204:             $this->refreshMonitors(0);
205:             $this->parent = null;
206: 
207:         } else { // add to parent
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:      * Is called by a component when it is about to be set new parent. Descendant can
223:      * override this method to disallow a parent change by throwing an Nette\InvalidStateException
224:      * @return void
225:      * @throws Nette\InvalidStateException
226:      */
227:     protected function validateParent(IContainer $parent)
228:     {
229:     }
230: 
231: 
232:     /**
233:      * Refreshes monitors.
234:      * @param  int  $depth
235:      * @param  array|null  $missing (array = attaching, null = detaching)
236:      * @param  array  $listeners
237:      * @return void
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) { // detaching
250:             foreach ($this->monitors as $type => $rec) {
251:                 if (isset($rec[1]) && $rec[1] > $depth) {
252:                     if ($rec[3]) { // monitored
253:                         $this->monitors[$type] = [null, null, null, $rec[3]];
254:                         foreach ($rec[3] as $pair) {
255:                             $listeners[] = [$pair[1], $rec[0]];
256:                         }
257:                     } else { // not monitored, just randomly cached
258:                         unset($this->monitors[$type]);
259:                     }
260:                 }
261:             }
262: 
263:         } else { // attaching
264:             foreach ($this->monitors as $type => $rec) {
265:                 if (isset($rec[0])) { // is in cache yet
266:                     continue;
267: 
268:                 } elseif (!$rec[3]) { // not monitored, just randomly cached
269:                     unset($this->monitors[$type]);
270: 
271:                 } elseif (isset($missing[$type])) { // known from previous lookup
272:                     $this->monitors[$type] = [null, null, null, $rec[3]];
273: 
274:                 } else {
275:                     $this->monitors[$type] = null; // forces re-lookup
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]; // mark as monitored
284:                 }
285:             }
286:         }
287: 
288:         if ($depth === 0) { // call listeners
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:     /********************* cloneable, serializable ****************d*g**/
301: 
302: 
303:     /**
304:      * Object cloning.
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) { // not cloning
314:                 $this->refreshMonitors(0);
315:             }
316: 
317:         } else {
318:             $this->parent = null;
319:             $this->refreshMonitors(0);
320:         }
321:     }
322: 
323: 
324:     /**
325:      * Prevents serialization.
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:      * Prevents unserialization.
335:      */
336:     public function __wakeup()
337:     {
338:         throw new Nette\NotImplementedException('Object unserialization is not supported by class ' . get_class($this));
339:     }
340: }
341: 
Nette 2.4-20180918 API API documentation generated by ApiGen 2.8.0