Source for file Component.php
Documentation is available at Component.php
6: * @copyright Copyright (c) 2004, 2010 David Grudl
7: * @license http://nettephp.com/license Nette license
8: * @link http://nettephp.com
16: * Component is the base class for all components.
18: * Components are objects implementing IComponent. They has parent component and own name.
20: * @copyright Copyright (c) 2004, 2010 David Grudl
23: * @property-read string $name
24: * @property IComponentContainer $parent
28: /** @var IComponentContainer */
34: /** @var array of [type => [obj, depth, path, is_monitored?]] */
35: private $monitors =
array();
41: public function __construct(IComponentContainer $parent =
NULL, $name =
NULL)
43: if ($parent !==
NULL) {
44: $parent->addComponent($this, $name);
54: * Lookup hierarchy for component specified by class or interface name.
55: * @param string class/interface type
56: * @param bool throw exception if component doesn't exist?
59: public function lookup($type, $need =
TRUE)
63: if (!isset($this->monitors[$type])) { // not monitored or not processed yet
64: $obj =
$this->parent;
65: $path =
self::NAME_SEPARATOR .
$this->name;
67: while ($obj !==
NULL) {
68: if ($obj instanceof
$type) break;
69: $path =
self::NAME_SEPARATOR .
$obj->getName() .
$path;
71: $obj =
$obj->getParent(); // IComponent::getParent()
72: if ($obj ===
$this) $obj =
NULL; // prevent cycling
76: $this->monitors[$type] =
array($obj, $depth, substr($path, 1), FALSE);
79: $this->monitors[$type] =
array(NULL, NULL, NULL, FALSE); // not found
83: if ($need &&
$this->monitors[$type][0] ===
NULL) {
87: return $this->monitors[$type][0];
93: * Lookup for component specified by class or interface name. Returns backtrace path.
94: * A path is the concatenation of component names separated by self::NAME_SEPARATOR.
95: * @param string class/interface type
96: * @param bool throw exception if component doesn't exist?
103: return $this->monitors[$type][2];
109: * Starts monitoring.
110: * @param string class/interface type
116: if (empty($this->monitors[$type][3])) {
120: $this->monitors[$type][3] =
TRUE; // mark as monitored
128: * @param string class/interface type
134: unset($this->monitors[$type]);
140: * This method will be called when the component (or component's parent)
141: * becomes attached to a monitored object. Do not call this method yourself.
152: * This method will be called before the component (or component's parent)
153: * becomes detached from a monitored object. Do not call this method yourself.
163: /********************* interface IComponent ****************d*g**/
178: * Returns the container if any.
179: * @return IComponentContainer|NULL
183: return $this->parent;
189: * Sets the parent of this component. This method is managed by containers and should.
190: * not be called by applications
192: * @param IComponentContainer New parent or null if this component is being removed from a parent
194: * @return Component provides a fluent interface
195: * @throws InvalidStateException
197: public function setParent(IComponentContainer $parent =
NULL, $name =
NULL)
199: if ($parent ===
NULL &&
$this->parent ===
NULL &&
$name !==
NULL) {
200: $this->name =
$name; // just rename
203: } elseif ($parent ===
$this->parent &&
$name ===
NULL) {
204: return $this; // nothing to do
207: // A component cannot be given a parent if it already has a parent.
208: if ($this->parent !==
NULL &&
$parent !==
NULL) {
212: // remove from parent?
213: if ($parent ===
NULL) {
214: $this->refreshMonitors(0);
215: $this->parent =
NULL;
217: } else { // add to parent
219: $this->parent =
$parent;
220: if ($name !==
NULL) $this->name =
$name;
223: $this->refreshMonitors(0, $tmp);
231: * Is called by a component when it is about to be set new parent. Descendant can
232: * override this method to disallow a parent change by throwing an \InvalidStateException
233: * @param IComponentContainer
235: * @throws InvalidStateException
244: * Refreshes monitors.
246: * @param array|NULL(array = attaching, NULL = detaching)
250: private function refreshMonitors($depth, & $missing =
NULL, & $listeners =
array())
253: foreach ($this->getComponents() as $component) {
255: $component->refreshMonitors($depth +
1, $missing, $listeners);
260: if ($missing ===
NULL) { // detaching
261: foreach ($this->monitors as $type =>
$rec) {
262: if (isset($rec[1]) &&
$rec[1] >
$depth) {
263: if ($rec[3]) { // monitored
264: $this->monitors[$type] =
array(NULL, NULL, NULL, TRUE);
265: $listeners[] =
array($this, $rec[0]);
266: } else { // not monitored, just randomly cached
267: unset($this->monitors[$type]);
272: } else { // attaching
273: foreach ($this->monitors as $type =>
$rec) {
274: if (isset($rec[0])) { // is in cache yet
277: } elseif (!$rec[3]) { // not monitored, just randomly cached
278: unset($this->monitors[$type]);
280: } elseif (isset($missing[$type])) { // known from previous lookup
281: $this->monitors[$type] =
array(NULL, NULL, NULL, TRUE);
284: $this->monitors[$type] =
NULL; // forces re-lookup
286: $listeners[] =
array($this, $obj);
288: $missing[$type] =
TRUE;
290: $this->monitors[$type][3] =
TRUE; // mark as monitored
295: if ($depth ===
0) { // call listeners
296: $method =
$missing ===
NULL ?
'detached' :
'attached';
297: foreach ($listeners as $item) {
298: $item[0]->$method($item[1]);
305: /********************* cloneable, serializable ****************d*g**/
314: if ($this->parent ===
NULL) {
318: $this->parent =
$this->parent->_isCloning();
319: if ($this->parent ===
NULL) { // not cloning
320: $this->refreshMonitors(0);
324: $this->parent =
NULL;
325: $this->refreshMonitors(0);
332: * Prevents unserialization.