Namespaces

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Diagnostics
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
      • Diagnostics
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • PhpGenerator
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
  • NetteModule
  • none

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