/vendor/nette/nette/Nette/ComponentModel/Component.php

https://bitbucket.org/iiic/iszp · PHP · 351 lines · 173 code · 78 blank · 100 comment · 36 complexity · 55bfd779294f4debe3b64f79f03ae256 MD5 · raw file

  1. <?php
  2. /**
  3. * This file is part of the Nette Framework (http://nette.org)
  4. *
  5. * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  6. *
  7. * For the full copyright and license information, please view
  8. * the file license.txt that was distributed with this source code.
  9. */
  10. namespace Nette\ComponentModel;
  11. use Nette;
  12. /**
  13. * Component is the base class for all components.
  14. *
  15. * Components are objects implementing IComponent. They has parent component and own name.
  16. *
  17. * @author David Grudl
  18. *
  19. * @property-read string $name
  20. * @property-read IContainer|NULL $parent
  21. */
  22. abstract class Component extends Nette\Object implements IComponent
  23. {
  24. /** @var IContainer */
  25. private $parent;
  26. /** @var string */
  27. private $name;
  28. /** @var array of [type => [obj, depth, path, is_monitored?]] */
  29. private $monitors = array();
  30. /**
  31. */
  32. public function __construct(IContainer $parent = NULL, $name = NULL)
  33. {
  34. if ($parent !== NULL) {
  35. $parent->addComponent($this, $name);
  36. } elseif (is_string($name)) {
  37. $this->name = $name;
  38. }
  39. }
  40. /**
  41. * Lookup hierarchy for component specified by class or interface name.
  42. * @param string class/interface type
  43. * @param bool throw exception if component doesn't exist?
  44. * @return IComponent
  45. */
  46. public function lookup($type, $need = TRUE)
  47. {
  48. if (!isset($this->monitors[$type])) { // not monitored or not processed yet
  49. $obj = $this->parent;
  50. $path = self::NAME_SEPARATOR . $this->name;
  51. $depth = 1;
  52. while ($obj !== NULL) {
  53. if ($obj instanceof $type) {
  54. break;
  55. }
  56. $path = self::NAME_SEPARATOR . $obj->getName() . $path;
  57. $depth++;
  58. $obj = $obj->getParent(); // IComponent::getParent()
  59. if ($obj === $this) {
  60. $obj = NULL; // prevent cycling
  61. }
  62. }
  63. if ($obj) {
  64. $this->monitors[$type] = array($obj, $depth, substr($path, 1), FALSE);
  65. } else {
  66. $this->monitors[$type] = array(NULL, NULL, NULL, FALSE); // not found
  67. }
  68. }
  69. if ($need && $this->monitors[$type][0] === NULL) {
  70. throw new Nette\InvalidStateException("Component '$this->name' is not attached to '$type'.");
  71. }
  72. return $this->monitors[$type][0];
  73. }
  74. /**
  75. * Lookup for component specified by class or interface name. Returns backtrace path.
  76. * A path is the concatenation of component names separated by self::NAME_SEPARATOR.
  77. * @param string class/interface type
  78. * @param bool throw exception if component doesn't exist?
  79. * @return string
  80. */
  81. public function lookupPath($type, $need = TRUE)
  82. {
  83. $this->lookup($type, $need);
  84. return $this->monitors[$type][2];
  85. }
  86. /**
  87. * Starts monitoring.
  88. * @param string class/interface type
  89. * @return void
  90. */
  91. public function monitor($type)
  92. {
  93. if (empty($this->monitors[$type][3])) {
  94. if ($obj = $this->lookup($type, FALSE)) {
  95. $this->attached($obj);
  96. }
  97. $this->monitors[$type][3] = TRUE; // mark as monitored
  98. }
  99. }
  100. /**
  101. * Stops monitoring.
  102. * @param string class/interface type
  103. * @return void
  104. */
  105. public function unmonitor($type)
  106. {
  107. unset($this->monitors[$type]);
  108. }
  109. /**
  110. * This method will be called when the component (or component's parent)
  111. * becomes attached to a monitored object. Do not call this method yourself.
  112. * @param IComponent
  113. * @return void
  114. */
  115. protected function attached($obj)
  116. {
  117. }
  118. /**
  119. * This method will be called before the component (or component's parent)
  120. * becomes detached from a monitored object. Do not call this method yourself.
  121. * @param IComponent
  122. * @return void
  123. */
  124. protected function detached($obj)
  125. {
  126. }
  127. /********************* interface IComponent ****************d*g**/
  128. /**
  129. * @return string
  130. */
  131. final public function getName()
  132. {
  133. return $this->name;
  134. }
  135. /**
  136. * Returns the container if any.
  137. * @return IContainer|NULL
  138. */
  139. final public function getParent()
  140. {
  141. return $this->parent;
  142. }
  143. /**
  144. * Sets the parent of this component. This method is managed by containers and should
  145. * not be called by applications
  146. * @param IContainer New parent or null if this component is being removed from a parent
  147. * @param string
  148. * @return Component provides a fluent interface
  149. * @throws Nette\InvalidStateException
  150. * @internal
  151. */
  152. public function setParent(IContainer $parent = NULL, $name = NULL)
  153. {
  154. if ($parent === NULL && $this->parent === NULL && $name !== NULL) {
  155. $this->name = $name; // just rename
  156. return $this;
  157. } elseif ($parent === $this->parent && $name === NULL) {
  158. return $this; // nothing to do
  159. }
  160. // A component cannot be given a parent if it already has a parent.
  161. if ($this->parent !== NULL && $parent !== NULL) {
  162. throw new Nette\InvalidStateException("Component '$this->name' already has a parent.");
  163. }
  164. // remove from parent?
  165. if ($parent === NULL) {
  166. $this->refreshMonitors(0);
  167. $this->parent = NULL;
  168. } else { // add to parent
  169. $this->validateParent($parent);
  170. $this->parent = $parent;
  171. if ($name !== NULL) {
  172. $this->name = $name;
  173. }
  174. $tmp = array();
  175. $this->refreshMonitors(0, $tmp);
  176. }
  177. return $this;
  178. }
  179. /**
  180. * Is called by a component when it is about to be set new parent. Descendant can
  181. * override this method to disallow a parent change by throwing an Nette\InvalidStateException
  182. * @return void
  183. * @throws Nette\InvalidStateException
  184. */
  185. protected function validateParent(IContainer $parent)
  186. {
  187. }
  188. /**
  189. * Refreshes monitors.
  190. * @param int
  191. * @param array|NULL (array = attaching, NULL = detaching)
  192. * @param array
  193. * @return void
  194. */
  195. private function refreshMonitors($depth, & $missing = NULL, & $listeners = array())
  196. {
  197. if ($this instanceof IContainer) {
  198. foreach ($this->getComponents() as $component) {
  199. if ($component instanceof Component) {
  200. $component->refreshMonitors($depth + 1, $missing, $listeners);
  201. }
  202. }
  203. }
  204. if ($missing === NULL) { // detaching
  205. foreach ($this->monitors as $type => $rec) {
  206. if (isset($rec[1]) && $rec[1] > $depth) {
  207. if ($rec[3]) { // monitored
  208. $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
  209. $listeners[] = array($this, $rec[0]);
  210. } else { // not monitored, just randomly cached
  211. unset($this->monitors[$type]);
  212. }
  213. }
  214. }
  215. } else { // attaching
  216. foreach ($this->monitors as $type => $rec) {
  217. if (isset($rec[0])) { // is in cache yet
  218. continue;
  219. } elseif (!$rec[3]) { // not monitored, just randomly cached
  220. unset($this->monitors[$type]);
  221. } elseif (isset($missing[$type])) { // known from previous lookup
  222. $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
  223. } else {
  224. $this->monitors[$type] = NULL; // forces re-lookup
  225. if ($obj = $this->lookup($type, FALSE)) {
  226. $listeners[] = array($this, $obj);
  227. } else {
  228. $missing[$type] = TRUE;
  229. }
  230. $this->monitors[$type][3] = TRUE; // mark as monitored
  231. }
  232. }
  233. }
  234. if ($depth === 0) { // call listeners
  235. $method = $missing === NULL ? 'detached' : 'attached';
  236. foreach ($listeners as $item) {
  237. $item[0]->$method($item[1]);
  238. }
  239. }
  240. }
  241. /********************* cloneable, serializable ****************d*g**/
  242. /**
  243. * Object cloning.
  244. */
  245. public function __clone()
  246. {
  247. if ($this->parent === NULL) {
  248. return;
  249. } elseif ($this->parent instanceof Container) {
  250. $this->parent = $this->parent->_isCloning();
  251. if ($this->parent === NULL) { // not cloning
  252. $this->refreshMonitors(0);
  253. }
  254. } else {
  255. $this->parent = NULL;
  256. $this->refreshMonitors(0);
  257. }
  258. }
  259. /**
  260. * Prevents serialization.
  261. */
  262. final public function __sleep()
  263. {
  264. throw new Nette\NotImplementedException('Object serialization is not supported by class ' . get_class($this));
  265. }
  266. /**
  267. * Prevents unserialization.
  268. */
  269. final public function __wakeup()
  270. {
  271. throw new Nette\NotImplementedException('Object unserialization is not supported by class ' . get_class($this));
  272. }
  273. }