Packages

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

Classes

Interfaces

  • Overview
  • Package
  • 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:  * @package Nette\ComponentModel
  7:  */
  8: 
  9: 
 10: 
 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:  * @author     David Grudl
 17:  *
 18:  * @property-read string $name
 19:  * @property-read IComponentContainer|NULL $parent
 20:  * @package Nette\ComponentModel
 21:  */
 22: abstract class Component extends Object implements IComponent
 23: {
 24:     /** @var IComponentContainer */
 25:     private $parent;
 26: 
 27:     /** @var string */
 28:     private $name;
 29: 
 30:     /** @var array of [type => [obj, depth, path, is_monitored?]] */
 31:     private $monitors = array();
 32: 
 33: 
 34:     public function __construct(IComponentContainer $parent = NULL, $name = NULL)
 35:     {
 36:         if ($parent !== NULL) {
 37:             $parent->addComponent($this, $name);
 38: 
 39:         } elseif (is_string($name)) {
 40:             $this->name = $name;
 41:         }
 42:     }
 43: 
 44: 
 45:     /**
 46:      * Lookup hierarchy for component specified by class or interface name.
 47:      * @param  string class/interface type
 48:      * @param  bool   throw exception if component doesn't exist?
 49:      * @return IComponent
 50:      */
 51:     public function lookup($type, $need = TRUE)
 52:     {
 53:         if (!isset($this->monitors[$type])) { // not monitored or not processed yet
 54:             $obj = $this->parent;
 55:             $path = self::NAME_SEPARATOR . $this->name;
 56:             $depth = 1;
 57:             while ($obj !== NULL) {
 58:                 if ($obj instanceof $type) {
 59:                     break;
 60:                 }
 61:                 $path = self::NAME_SEPARATOR . $obj->getName() . $path;
 62:                 $depth++;
 63:                 $obj = $obj->getParent(); // 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 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, $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 IComponentContainer|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  IComponentContainer  New parent or null if this component is being removed from a parent
174:      * @param  string
175:      * @return self
176:      * @throws InvalidStateException
177:      * @internal
178:      */
179:     public function setParent(IComponentContainer $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 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 InvalidStateException
216:      * @return void
217:      * @throws InvalidStateException
218:      */
219:     protected function validateParent(IComponentContainer $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 IComponentContainer) {
234:             foreach ($this->getComponents() as $component) {
235:                 if ($component instanceof Component) {
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:             foreach ($listeners as $item) {
279:                 $item[0]->$method($item[1]);
280:             }
281:         }
282:     }
283: 
284: 
285:     /********************* cloneable, serializable ****************d*g**/
286: 
287: 
288:     /**
289:      * Object cloning.
290:      */
291:     public function __clone()
292:     {
293:         if ($this->parent === NULL) {
294:             return;
295: 
296:         } elseif ($this->parent instanceof ComponentContainer) {
297:             $this->parent = $this->parent->_isCloning();
298:             if ($this->parent === NULL) { // not cloning
299:                 $this->refreshMonitors(0);
300:             }
301: 
302:         } else {
303:             $this->parent = NULL;
304:             $this->refreshMonitors(0);
305:         }
306:     }
307: 
308: 
309:     /**
310:      * Prevents serialization.
311:      */
312:     public function __sleep()
313:     {
314:         throw new NotImplementedException('Object serialization is not supported by class ' . get_class($this));
315:     }
316: 
317: 
318:     /**
319:      * Prevents unserialization.
320:      */
321:     public function __wakeup()
322:     {
323:         throw new NotImplementedException('Object unserialization is not supported by class ' . get_class($this));
324:     }
325: 
326: }
327: 
Nette Framework 2.0.18 (for PHP 5.2, un-prefixed) API documentation generated by ApiGen 2.8.0