Source for file Component.php
Documentation is available at Component.php
6: * Copyright (c) 2004, 2009 David Grudl (http://davidgrudl.com)
8: * This source file is subject to the "Nette license" that is bundled
9: * with this package in the file license.txt.
11: * For more information please see http://nettephp.com
13: * @copyright Copyright (c) 2004, 2009 David Grudl
14: * @license http://nettephp.com/license Nette license
15: * @link http://nettephp.com
22: require_once dirname(__FILE__) .
'/IComponent.php';
24: require_once dirname(__FILE__) .
'/Object.php';
29: * Component is the base class for all components.
31: * Components are objects implementing IComponent. They has parent component and own name.
33: * @author David Grudl
34: * @copyright Copyright (c) 2004, 2009 David Grudl
37: * @property-read string $name
38: * @property IComponentContainer $parent
42: /** @var IComponentContainer */
48: /** @var array of [type => [obj, depth, path, is_monitored?]] */
49: private $monitors =
array();
55: public function __construct(IComponentContainer $parent =
NULL, $name =
NULL)
57: if ($parent !==
NULL) {
58: $parent->addComponent($this, $name);
68: * Lookup hierarchy for component specified by class or interface name.
69: * @param string class/interface type
70: * @param bool throw exception if component doesn't exist?
73: public function lookup($type, $need =
TRUE)
77: if (!isset($this->monitors[$type])) { // not monitored or not processed yet
78: $obj =
$this->parent;
79: $path =
self::NAME_SEPARATOR .
$this->name;
81: while ($obj !==
NULL) {
82: if ($obj instanceof
$type) break;
83: $path =
self::NAME_SEPARATOR .
$obj->getName() .
$path;
85: $obj =
$obj->getParent(); // IComponent::getParent()
86: if ($obj ===
$this) $obj =
NULL; // prevent cycling
90: $this->monitors[$type] =
array($obj, $depth, substr($path, 1), FALSE);
93: $this->monitors[$type] =
array(NULL, NULL, NULL, FALSE); // not found
97: if ($need &&
$this->monitors[$type][0] ===
NULL) {
101: return $this->monitors[$type][0];
107: * Lookup for component specified by class or interface name. Returns backtrace path.
108: * A path is the concatenation of component names separated by self::NAME_SEPARATOR.
109: * @param string class/interface type
110: * @param bool throw exception if component doesn't exist?
117: return $this->monitors[$type][2];
123: * Starts monitoring.
124: * @param string class/interface type
130: if (empty($this->monitors[$type][3])) {
134: $this->monitors[$type][3] =
TRUE; // mark as monitored
142: * @param string class/interface type
148: unset($this->monitors[$type]);
154: * This method will be called when the component (or component's parent)
155: * becomes attached to a monitored object. Do not call this method yourself.
166: * This method will be called before the component (or component's parent)
167: * becomes detached from a monitored object. Do not call this method yourself.
177: /********************* interface IComponent ****************d*g**/
192: * Returns the container if any.
193: * @return IComponentContainer|NULL
197: return $this->parent;
203: * Sets the parent of this component. This method is managed by containers and should.
204: * not be called by applications
206: * @param IComponentContainer New parent or null if this component is being removed from a parent
208: * @return Component provides a fluent interface
209: * @throws InvalidStateException
211: public function setParent(IComponentContainer $parent =
NULL, $name =
NULL)
213: if ($parent ===
NULL &&
$this->parent ===
NULL &&
$name !==
NULL) {
214: $this->name =
$name; // just rename
217: } elseif ($parent ===
$this->parent &&
$name ===
NULL) {
218: return $this; // nothing to do
221: // A component cannot be given a parent if it already has a parent.
222: if ($this->parent !==
NULL &&
$parent !==
NULL) {
226: // remove from parent?
227: if ($parent ===
NULL) {
228: // parent cannot be removed if is still this component contains
233: $this->refreshMonitors(0);
234: $this->parent =
NULL;
236: } else { // add to parent
237: // Given parent container does not already recognize this component as its child.
238: if ($parent->getComponent($name, FALSE) !==
$this) {
243: $this->parent =
$parent;
244: if ($name !==
NULL) $this->name =
$name;
247: $this->refreshMonitors(0, $tmp);
255: * Is called by a component when it is about to be set new parent. Descendant can
256: * override this method to disallow a parent change by throwing an \InvalidStateException
257: * @param IComponentContainer
259: * @throws InvalidStateException
268: * Refreshes monitors.
270: * @param array|NULL(array = attaching, NULL = detaching)
274: private function refreshMonitors($depth, & $missing =
NULL, & $listeners =
array())
277: foreach ($this->getComponents() as $component) {
279: $component->refreshMonitors($depth +
1, $missing, $listeners);
284: if ($missing ===
NULL) { // detaching
285: foreach ($this->monitors as $type =>
$rec) {
286: if (isset($rec[1]) &&
$rec[1] >
$depth) {
287: if ($rec[3]) { // monitored
288: $this->monitors[$type] =
array(NULL, NULL, NULL, TRUE);
289: $listeners[] =
array($this, $rec[0]);
290: } else { // not monitored, just randomly cached
291: unset($this->monitors[$type]);
296: } else { // attaching
297: foreach ($this->monitors as $type =>
$rec) {
298: if (isset($rec[0])) { // is in cache yet
301: } elseif (!$rec[3]) { // not monitored, just randomly cached
302: unset($this->monitors[$type]);
304: } elseif (isset($missing[$type])) { // known from previous lookup
305: $this->monitors[$type] =
array(NULL, NULL, NULL, TRUE);
308: $this->monitors[$type] =
NULL; // forces re-lookup
310: $listeners[] =
array($this, $obj);
312: $missing[$type] =
TRUE;
314: $this->monitors[$type][3] =
TRUE; // mark as monitored
319: if ($depth ===
0) { // call listeners
320: $method =
$missing ===
NULL ?
'detached' :
'attached';
321: foreach ($listeners as $item) {
322: $item[0]->$method($item[1]);
329: /********************* cloneable, serializable ****************d*g**/
338: if ($this->parent ===
NULL) {
342: $this->parent =
$this->parent->_isCloning();
343: if ($this->parent ===
NULL) { // not cloning
344: $this->refreshMonitors(0);
348: $this->parent =
NULL;
349: $this->refreshMonitors(0);
356: * Prevents unserialization.