PageRenderTime 43ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/www/libs/nette-dev/Component.php

https://github.com/bazo/Mokuji
PHP | 339 lines | 165 code | 74 blank | 100 comment | 36 complexity | a6fb04bcf934ae33b86c27053b143879 MD5 | raw file
Possible License(s): BSD-3-Clause, MIT
  1. <?php
  2. /**
  3. * Nette Framework
  4. *
  5. * @copyright Copyright (c) 2004, 2010 David Grudl
  6. * @license http://nettephp.com/license Nette license
  7. * @link http://nettephp.com
  8. * @category Nette
  9. * @package Nette
  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. * @copyright Copyright (c) 2004, 2010 David Grudl
  17. * @package Nette
  18. *
  19. * @property-read string $name
  20. * @property IComponentContainer $parent
  21. */
  22. abstract class Component extends Object implements IComponent
  23. {
  24. /** @var IComponentContainer */
  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(IComponentContainer $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. Framework::fixNamespace($type);
  49. if (!isset($this->monitors[$type])) { // not monitored or not processed yet
  50. $obj = $this->parent;
  51. $path = self::NAME_SEPARATOR . $this->name;
  52. $depth = 1;
  53. while ($obj !== NULL) {
  54. if ($obj instanceof $type) break;
  55. $path = self::NAME_SEPARATOR . $obj->getName() . $path;
  56. $depth++;
  57. $obj = $obj->getParent(); // IComponent::getParent()
  58. if ($obj === $this) $obj = NULL; // prevent cycling
  59. }
  60. if ($obj) {
  61. $this->monitors[$type] = array($obj, $depth, substr($path, 1), FALSE);
  62. } else {
  63. $this->monitors[$type] = array(NULL, NULL, NULL, FALSE); // not found
  64. }
  65. }
  66. if ($need && $this->monitors[$type][0] === NULL) {
  67. throw new InvalidStateException("Component '$this->name' is not attached to '$type'.");
  68. }
  69. return $this->monitors[$type][0];
  70. }
  71. /**
  72. * Lookup for component specified by class or interface name. Returns backtrace path.
  73. * A path is the concatenation of component names separated by self::NAME_SEPARATOR.
  74. * @param string class/interface type
  75. * @param bool throw exception if component doesn't exist?
  76. * @return string
  77. */
  78. public function lookupPath($type, $need = TRUE)
  79. {
  80. Framework::fixNamespace($type);
  81. $this->lookup($type, $need);
  82. return $this->monitors[$type][2];
  83. }
  84. /**
  85. * Starts monitoring.
  86. * @param string class/interface type
  87. * @return void
  88. */
  89. public function monitor($type)
  90. {
  91. Framework::fixNamespace($type);
  92. if (empty($this->monitors[$type][3])) {
  93. if ($obj = $this->lookup($type, FALSE)) {
  94. $this->attached($obj);
  95. }
  96. $this->monitors[$type][3] = TRUE; // mark as monitored
  97. }
  98. }
  99. /**
  100. * Stops monitoring.
  101. * @param string class/interface type
  102. * @return void
  103. */
  104. public function unmonitor($type)
  105. {
  106. Framework::fixNamespace($type);
  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 IComponentContainer|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. *
  147. * @param IComponentContainer New parent or null if this component is being removed from a parent
  148. * @param string
  149. * @return Component provides a fluent interface
  150. * @throws InvalidStateException
  151. */
  152. public function setParent(IComponentContainer $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 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) $this->name = $name;
  172. $tmp = array();
  173. $this->refreshMonitors(0, $tmp);
  174. }
  175. return $this;
  176. }
  177. /**
  178. * Is called by a component when it is about to be set new parent. Descendant can
  179. * override this method to disallow a parent change by throwing an \InvalidStateException
  180. * @param IComponentContainer
  181. * @return void
  182. * @throws InvalidStateException
  183. */
  184. protected function validateParent(IComponentContainer $parent)
  185. {
  186. }
  187. /**
  188. * Refreshes monitors.
  189. * @param int
  190. * @param array|NULL (array = attaching, NULL = detaching)
  191. * @param array
  192. * @return void
  193. */
  194. private function refreshMonitors($depth, & $missing = NULL, & $listeners = array())
  195. {
  196. if ($this instanceof IComponentContainer) {
  197. foreach ($this->getComponents() as $component) {
  198. if ($component instanceof Component) {
  199. $component->refreshMonitors($depth + 1, $missing, $listeners);
  200. }
  201. }
  202. }
  203. if ($missing === NULL) { // detaching
  204. foreach ($this->monitors as $type => $rec) {
  205. if (isset($rec[1]) && $rec[1] > $depth) {
  206. if ($rec[3]) { // monitored
  207. $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
  208. $listeners[] = array($this, $rec[0]);
  209. } else { // not monitored, just randomly cached
  210. unset($this->monitors[$type]);
  211. }
  212. }
  213. }
  214. } else { // attaching
  215. foreach ($this->monitors as $type => $rec) {
  216. if (isset($rec[0])) { // is in cache yet
  217. continue;
  218. } elseif (!$rec[3]) { // not monitored, just randomly cached
  219. unset($this->monitors[$type]);
  220. } elseif (isset($missing[$type])) { // known from previous lookup
  221. $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
  222. } else {
  223. $this->monitors[$type] = NULL; // forces re-lookup
  224. if ($obj = $this->lookup($type, FALSE)) {
  225. $listeners[] = array($this, $obj);
  226. } else {
  227. $missing[$type] = TRUE;
  228. }
  229. $this->monitors[$type][3] = TRUE; // mark as monitored
  230. }
  231. }
  232. }
  233. if ($depth === 0) { // call listeners
  234. $method = $missing === NULL ? 'detached' : 'attached';
  235. foreach ($listeners as $item) {
  236. $item[0]->$method($item[1]);
  237. }
  238. }
  239. }
  240. /********************* cloneable, serializable ****************d*g**/
  241. /**
  242. * Object cloning.
  243. */
  244. public function __clone()
  245. {
  246. if ($this->parent === NULL) {
  247. return;
  248. } elseif ($this->parent instanceof ComponentContainer) {
  249. $this->parent = $this->parent->_isCloning();
  250. if ($this->parent === NULL) { // not cloning
  251. $this->refreshMonitors(0);
  252. }
  253. } else {
  254. $this->parent = NULL;
  255. $this->refreshMonitors(0);
  256. }
  257. }
  258. /**
  259. * Prevents unserialization.
  260. */
  261. final public function __wakeup()
  262. {
  263. throw new NotImplementedException;
  264. }
  265. }