Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationLatte
      • ApplicationTracy
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsLatte
      • Framework
      • HttpTracy
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Templating
    • Utils
  • NetteModule
  • none
  • Tracy

Classes

  • Component
  • Container

Interfaces

  • IComponent
  • IContainer
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  • Nette homepage
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  * Copyright (c) 2004 David Grudl (http://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 extends Nette\Object implements IComponent
 22: {
 23:     /** @var IContainer */
 24:     private $parent;
 25: 
 26:     /** @var string */
 27:     private $name;
 28: 
 29:     /** @var array of [type => [obj, depth, path, is_monitored?]] */
 30:     private $monitors = array();
 31: 
 32: 
 33:     public function __construct(IContainer $parent = NULL, $name = NULL)
 34:     {
 35:         if ($parent !== NULL) {
 36:             $parent->addComponent($this, $name);
 37: 
 38:         } elseif (is_string($name)) {
 39:             $this->name = $name;
 40:         }
 41:     }
 42: 
 43: 
 44:     /**
 45:      * Lookup hierarchy for component specified by class or interface name.
 46:      * @param  string class/interface type
 47:      * @param  bool   throw exception if component doesn't exist?
 48:      * @return IComponent
 49:      */
 50:     public function lookup($type, $need = TRUE)
 51:     {
 52:         if (!isset($this->monitors[$type])) { // not monitored or not processed yet
 53:             $obj = $this->parent;
 54:             $path = self::NAME_SEPARATOR . $this->name;
 55:             $depth = 1;
 56:             while ($obj !== NULL) {
 57:                 $parent = $obj->getParent();
 58:                 if ($type ? $obj instanceof $type : $parent === NULL) {
 59:                     break;
 60:                 }
 61:                 $path = self::NAME_SEPARATOR . $obj->getName() . $path;
 62:                 $depth++;
 63:                 $obj = $parent; // IComponent::getParent()
 64:                 if ($obj === $this) {
 65:                     $obj = NULL; // prevent cycling
 66:                 }
 67:             }
 68: 
 69:             if ($obj) {
 70:                 $this->monitors[$type] = array($obj, $depth, substr($path, 1), FALSE);
 71: 
 72:             } else {
 73:                 $this->monitors[$type] = array(NULL, NULL, NULL, FALSE); // not found
 74:             }
 75:         }
 76: 
 77:         if ($need && $this->monitors[$type][0] === NULL) {
 78:             throw new Nette\InvalidStateException("Component '$this->name' is not attached to '$type'.");
 79:         }
 80: 
 81:         return $this->monitors[$type][0];
 82:     }
 83: 
 84: 
 85:     /**
 86:      * Lookup for component specified by class or interface name. Returns backtrace path.
 87:      * A path is the concatenation of component names separated by self::NAME_SEPARATOR.
 88:      * @param  string class/interface type
 89:      * @param  bool   throw exception if component doesn't exist?
 90:      * @return string
 91:      */
 92:     public function lookupPath($type = NULL, $need = TRUE)
 93:     {
 94:         $this->lookup($type, $need);
 95:         return $this->monitors[$type][2];
 96:     }
 97: 
 98: 
 99:     /**
100:      * Starts monitoring.
101:      * @param  string class/interface type
102:      * @return void
103:      */
104:     public function monitor($type)
105:     {
106:         if (empty($this->monitors[$type][3])) {
107:             if ($obj = $this->lookup($type, FALSE)) {
108:                 $this->attached($obj);
109:             }
110:             $this->monitors[$type][3] = TRUE; // mark as monitored
111:         }
112:     }
113: 
114: 
115:     /**
116:      * Stops monitoring.
117:      * @param  string class/interface type
118:      * @return void
119:      */
120:     public function unmonitor($type)
121:     {
122:         unset($this->monitors[$type]);
123:     }
124: 
125: 
126:     /**
127:      * This method will be called when the component (or component's parent)
128:      * becomes attached to a monitored object. Do not call this method yourself.
129:      * @param  IComponent
130:      * @return void
131:      */
132:     protected function attached($obj)
133:     {
134:     }
135: 
136: 
137:     /**
138:      * This method will be called before the component (or component's parent)
139:      * becomes detached from a monitored object. Do not call this method yourself.
140:      * @param  IComponent
141:      * @return void
142:      */
143:     protected function detached($obj)
144:     {
145:     }
146: 
147: 
148:     /********************* interface IComponent ****************d*g**/
149: 
150: 
151:     /**
152:      * @return string
153:      */
154:     public function getName()
155:     {
156:         return $this->name;
157:     }
158: 
159: 
160:     /**
161:      * Returns the container if any.
162:      * @return IContainer|NULL
163:      */
164:     public function getParent()
165:     {
166:         return $this->parent;
167:     }
168: 
169: 
170:     /**
171:      * Sets the parent of this component. This method is managed by containers and should
172:      * not be called by applications
173:      * @param  IContainer  New parent or null if this component is being removed from a parent
174:      * @param  string
175:      * @return self
176:      * @throws Nette\InvalidStateException
177:      * @internal
178:      */
179:     public function setParent(IContainer $parent = NULL, $name = NULL)
180:     {
181:         if ($parent === NULL && $this->parent === NULL && $name !== NULL) {
182:             $this->name = $name; // just rename
183:             return $this;
184: 
185:         } elseif ($parent === $this->parent && $name === NULL) {
186:             return $this; // nothing to do
187:         }
188: 
189:         // A component cannot be given a parent if it already has a parent.
190:         if ($this->parent !== NULL && $parent !== NULL) {
191:             throw new Nette\InvalidStateException("Component '$this->name' already has a parent.");
192:         }
193: 
194:         // remove from parent?
195:         if ($parent === NULL) {
196:             $this->refreshMonitors(0);
197:             $this->parent = NULL;
198: 
199:         } else { // add to parent
200:             $this->validateParent($parent);
201:             $this->parent = $parent;
202:             if ($name !== NULL) {
203:                 $this->name = $name;
204:             }
205: 
206:             $tmp = array();
207:             $this->refreshMonitors(0, $tmp);
208:         }
209:         return $this;
210:     }
211: 
212: 
213:     /**
214:      * Is called by a component when it is about to be set new parent. Descendant can
215:      * override this method to disallow a parent change by throwing an Nette\InvalidStateException
216:      * @return void
217:      * @throws Nette\InvalidStateException
218:      */
219:     protected function validateParent(IContainer $parent)
220:     {
221:     }
222: 
223: 
224:     /**
225:      * Refreshes monitors.
226:      * @param  int
227:      * @param  array|NULL (array = attaching, NULL = detaching)
228:      * @param  array
229:      * @return void
230:      */
231:     private function refreshMonitors($depth, & $missing = NULL, & $listeners = array())
232:     {
233:         if ($this instanceof IContainer) {
234:             foreach ($this->getComponents() as $component) {
235:                 if ($component instanceof self) {
236:                     $component->refreshMonitors($depth + 1, $missing, $listeners);
237:                 }
238:             }
239:         }
240: 
241:         if ($missing === NULL) { // detaching
242:             foreach ($this->monitors as $type => $rec) {
243:                 if (isset($rec[1]) && $rec[1] > $depth) {
244:                     if ($rec[3]) { // monitored
245:                         $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
246:                         $listeners[] = array($this, $rec[0]);
247:                     } else { // not monitored, just randomly cached
248:                         unset($this->monitors[$type]);
249:                     }
250:                 }
251:             }
252: 
253:         } else { // attaching
254:             foreach ($this->monitors as $type => $rec) {
255:                 if (isset($rec[0])) { // is in cache yet
256:                     continue;
257: 
258:                 } elseif (!$rec[3]) { // not monitored, just randomly cached
259:                     unset($this->monitors[$type]);
260: 
261:                 } elseif (isset($missing[$type])) { // known from previous lookup
262:                     $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
263: 
264:                 } else {
265:                     $this->monitors[$type] = NULL; // forces re-lookup
266:                     if ($obj = $this->lookup($type, FALSE)) {
267:                         $listeners[] = array($this, $obj);
268:                     } else {
269:                         $missing[$type] = TRUE;
270:                     }
271:                     $this->monitors[$type][3] = TRUE; // mark as monitored
272:                 }
273:             }
274:         }
275: 
276:         if ($depth === 0) { // call listeners
277:             $method = $missing === NULL ? 'detached' : 'attached';
278:             $prev = array();
279:             foreach ($listeners as $item) {
280:                 if (!in_array($item, $prev, TRUE)) {
281:                     $item[0]->$method($item[1]);
282:                     $prev[] = $item;
283:                 }
284:             }
285:         }
286:     }
287: 
288: 
289:     /********************* cloneable, serializable ****************d*g**/
290: 
291: 
292:     /**
293:      * Object cloning.
294:      */
295:     public function __clone()
296:     {
297:         if ($this->parent === NULL) {
298:             return;
299: 
300:         } elseif ($this->parent instanceof Container) {
301:             $this->parent = $this->parent->_isCloning();
302:             if ($this->parent === NULL) { // not cloning
303:                 $this->refreshMonitors(0);
304:             }
305: 
306:         } else {
307:             $this->parent = NULL;
308:             $this->refreshMonitors(0);
309:         }
310:     }
311: 
312: 
313:     /**
314:      * Prevents serialization.
315:      */
316:     public function __sleep()
317:     {
318:         throw new Nette\NotImplementedException('Object serialization is not supported by class ' . get_class($this));
319:     }
320: 
321: 
322:     /**
323:      * Prevents unserialization.
324:      */
325:     public function __wakeup()
326:     {
327:         throw new Nette\NotImplementedException('Object unserialization is not supported by class ' . get_class($this));
328:     }
329: 
330: }
331: 
Nette 2.2 API documentation generated by ApiGen 2.8.0