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
    • Reflection
    • Security
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

  • Component
  • Container

Interfaces

  • IComponent
  • IContainer
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  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 */
 26:     private $parent;
 27: 
 28:     /** @var string */
 29:     private $name;
 30: 
 31:     /** @var array of [type => [obj, depth, path, is_monitored?]] */
 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:      * Lookup hierarchy for component specified by class or interface name.
 49:      * @param  string|null
 50:      * @param  bool
 51:      * @return IComponent|null
 52:      */
 53:     public function lookup($type, $throw = true)
 54:     {
 55:         if (!isset($this->monitors[$type])) { // not monitored or not processed yet
 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; // IComponent::getParent()
 67:                 if ($obj === $this) {
 68:                     $obj = null; // prevent cycling
 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]; // not found
 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:      * Lookup for component specified by class or interface name. Returns backtrace path.
 90:      * A path is the concatenation of component names separated by self::NAME_SEPARATOR.
 91:      * @param  string|null
 92:      * @param  bool
 93:      * @return string|null
 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:      * Starts monitoring.
104:      * @param  string
105:      * @return void
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; // mark as monitored
114:         }
115:     }
116: 
117: 
118:     /**
119:      * Stops monitoring.
120:      * @param  string
121:      * @return void
122:      */
123:     public function unmonitor($type)
124:     {
125:         unset($this->monitors[$type]);
126:     }
127: 
128: 
129:     /**
130:      * This method will be called when the component (or component's parent)
131:      * becomes attached to a monitored object. Do not call this method yourself.
132:      * @param  IComponent
133:      * @return void
134:      */
135:     protected function attached($obj)
136:     {
137:     }
138: 
139: 
140:     /**
141:      * This method will be called before the component (or component's parent)
142:      * becomes detached from a monitored object. Do not call this method yourself.
143:      * @param  IComponent
144:      * @return void
145:      */
146:     protected function detached($obj)
147:     {
148:     }
149: 
150: 
151:     /********************* interface IComponent ****************d*g**/
152: 
153: 
154:     /**
155:      * @return string|null
156:      */
157:     public function getName()
158:     {
159:         return $this->name;
160:     }
161: 
162: 
163:     /**
164:      * Returns the container if any.
165:      * @return IContainer|null
166:      */
167:     public function getParent()
168:     {
169:         return $this->parent;
170:     }
171: 
172: 
173:     /**
174:      * Sets or removes the parent of this component. This method is managed by containers and should
175:      * not be called by applications
176:      * @param  IContainer
177:      * @param  string
178:      * @return static
179:      * @throws Nette\InvalidStateException
180:      * @internal
181:      */
182:     public function setParent(IContainer $parent = null, $name = null)
183:     {
184:         if ($parent === null && $this->parent === null && $name !== null) {
185:             $this->name = $name; // just rename
186:             return $this;
187: 
188:         } elseif ($parent === $this->parent && $name === null) {
189:             return $this; // nothing to do
190:         }
191: 
192:         // A component cannot be given a parent if it already has a parent.
193:         if ($this->parent !== null && $parent !== null) {
194:             throw new Nette\InvalidStateException("Component '$this->name' already has a parent.");
195:         }
196: 
197:         // remove from parent?
198:         if ($parent === null) {
199:             $this->refreshMonitors(0);
200:             $this->parent = null;
201: 
202:         } else { // add to parent
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:      * Is called by a component when it is about to be set new parent. Descendant can
218:      * override this method to disallow a parent change by throwing an Nette\InvalidStateException
219:      * @return void
220:      * @throws Nette\InvalidStateException
221:      */
222:     protected function validateParent(IContainer $parent)
223:     {
224:     }
225: 
226: 
227:     /**
228:      * Refreshes monitors.
229:      * @param  int
230:      * @param  array|null (array = attaching, null = detaching)
231:      * @param  array
232:      * @return void
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) { // detaching
245:             foreach ($this->monitors as $type => $rec) {
246:                 if (isset($rec[1]) && $rec[1] > $depth) {
247:                     if ($rec[3]) { // monitored
248:                         $this->monitors[$type] = [null, null, null, true];
249:                         $listeners[] = [$this, $rec[0]];
250:                     } else { // not monitored, just randomly cached
251:                         unset($this->monitors[$type]);
252:                     }
253:                 }
254:             }
255: 
256:         } else { // attaching
257:             foreach ($this->monitors as $type => $rec) {
258:                 if (isset($rec[0])) { // is in cache yet
259:                     continue;
260: 
261:                 } elseif (!$rec[3]) { // not monitored, just randomly cached
262:                     unset($this->monitors[$type]);
263: 
264:                 } elseif (isset($missing[$type])) { // known from previous lookup
265:                     $this->monitors[$type] = [null, null, null, true];
266: 
267:                 } else {
268:                     $this->monitors[$type] = null; // forces re-lookup
269:                     if ($obj = $this->lookup($type, false)) {
270:                         $listeners[] = [$this, $obj];
271:                     } else {
272:                         $missing[$type] = true;
273:                     }
274:                     $this->monitors[$type][3] = true; // mark as monitored
275:                 }
276:             }
277:         }
278: 
279:         if ($depth === 0) { // call listeners
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:     /********************* cloneable, serializable ****************d*g**/
293: 
294: 
295:     /**
296:      * Object cloning.
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) { // not cloning
306:                 $this->refreshMonitors(0);
307:             }
308: 
309:         } else {
310:             $this->parent = null;
311:             $this->refreshMonitors(0);
312:         }
313:     }
314: 
315: 
316:     /**
317:      * Prevents serialization.
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:      * Prevents unserialization.
327:      */
328:     public function __wakeup()
329:     {
330:         throw new Nette\NotImplementedException('Object unserialization is not supported by class ' . get_class($this));
331:     }
332: }
333: 
Nette 2.4-20170829 API API documentation generated by ApiGen 2.8.0