Source for file Component.php

Documentation is available at Component.php

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