PageRenderTime 57ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/Nette/Application/UI/PresenterComponent.php

https://github.com/vojtech-dobes/nette
PHP | 398 lines | 177 code | 72 blank | 149 comment | 29 complexity | ba7863745cb77d9b41e6fae861b25975 MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the Nette Framework (http://nette.org)
  4. * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  5. */
  6. namespace Nette\Application\UI;
  7. use Nette;
  8. /**
  9. * PresenterComponent is the base class for all Presenter components.
  10. *
  11. * Components are persistent objects located on a presenter. They have ability to own
  12. * other child components, and interact with user. Components have properties
  13. * for storing their status, and responds to user command.
  14. *
  15. * @author David Grudl
  16. *
  17. * @property-read Presenter $presenter
  18. * @property-read string $uniqueId
  19. */
  20. abstract class PresenterComponent extends Nette\ComponentModel\Container implements ISignalReceiver, IStatePersistent, \ArrayAccess
  21. {
  22. /** @var array */
  23. protected $params = array();
  24. /**
  25. * Returns the presenter where this component belongs to.
  26. * @param bool throw exception if presenter doesn't exist?
  27. * @return Presenter|NULL
  28. */
  29. public function getPresenter($need = TRUE)
  30. {
  31. return $this->lookup('Nette\Application\UI\Presenter', $need);
  32. }
  33. /**
  34. * Returns a fully-qualified name that uniquely identifies the component
  35. * within the presenter hierarchy.
  36. * @return string
  37. */
  38. public function getUniqueId()
  39. {
  40. return $this->lookupPath('Nette\Application\UI\Presenter', TRUE);
  41. }
  42. /**
  43. * This method will be called when the component (or component's parent)
  44. * becomes attached to a monitored object. Do not call this method yourself.
  45. * @param Nette\ComponentModel\IComponent
  46. * @return void
  47. */
  48. protected function attached($presenter)
  49. {
  50. if ($presenter instanceof Presenter) {
  51. $this->loadState($presenter->popGlobalParameters($this->getUniqueId()));
  52. }
  53. }
  54. /**
  55. * @return void
  56. */
  57. protected function validateParent(Nette\ComponentModel\IContainer $parent)
  58. {
  59. parent::validateParent($parent);
  60. $this->monitor('Nette\Application\UI\Presenter');
  61. }
  62. /**
  63. * Calls public method if exists.
  64. * @param string
  65. * @param array
  66. * @return bool does method exist?
  67. */
  68. protected function tryCall($method, array $params)
  69. {
  70. $rc = $this->getReflection();
  71. if ($rc->hasMethod($method)) {
  72. $rm = $rc->getMethod($method);
  73. if ($rm->isPublic() && !$rm->isAbstract() && !$rm->isStatic()) {
  74. $this->checkRequirements($rm);
  75. $rm->invokeArgs($this, $rc->combineArgs($rm, $params));
  76. return TRUE;
  77. }
  78. }
  79. return FALSE;
  80. }
  81. /**
  82. * Checks for requirements such as authorization.
  83. * @return void
  84. */
  85. public function checkRequirements($element)
  86. {
  87. }
  88. /**
  89. * Access to reflection.
  90. * @return PresenterComponentReflection
  91. */
  92. public static function getReflection()
  93. {
  94. return new PresenterComponentReflection(get_called_class());
  95. }
  96. /********************* interface IStatePersistent ****************d*g**/
  97. /**
  98. * Loads state informations.
  99. * @param array
  100. * @return void
  101. */
  102. public function loadState(array $params)
  103. {
  104. $reflection = $this->getReflection();
  105. foreach ($reflection->getPersistentParams() as $name => $meta) {
  106. if (isset($params[$name])) { // NULLs are ignored
  107. $type = gettype($meta['def']);
  108. if (!$reflection->convertType($params[$name], $type)) {
  109. throw new Nette\Application\BadRequestException("Invalid value for persistent parameter '$name' in '{$this->getName()}', expected " . ($type === 'NULL' ? 'scalar' : $type) . ".");
  110. }
  111. $this->$name = $params[$name];
  112. } else {
  113. $params[$name] = $this->$name;
  114. }
  115. }
  116. $this->params = $params;
  117. }
  118. /**
  119. * Saves state informations for next request.
  120. * @param array
  121. * @param PresenterComponentReflection (internal, used by Presenter)
  122. * @return void
  123. */
  124. public function saveState(array & $params, $reflection = NULL)
  125. {
  126. $reflection = $reflection === NULL ? $this->getReflection() : $reflection;
  127. foreach ($reflection->getPersistentParams() as $name => $meta) {
  128. if (isset($params[$name])) {
  129. // injected value
  130. } elseif (array_key_exists($name, $params)) { // NULLs are skipped
  131. continue;
  132. } elseif (!isset($meta['since']) || $this instanceof $meta['since']) {
  133. $params[$name] = $this->$name; // object property value
  134. } else {
  135. continue; // ignored parameter
  136. }
  137. $type = gettype($meta['def']);
  138. if (!PresenterComponentReflection::convertType($params[$name], $type)) {
  139. throw new InvalidLinkException("Invalid value for persistent parameter '$name' in '{$this->getName()}', expected " . ($type === 'NULL' ? 'scalar' : $type) . ".");
  140. }
  141. if ($params[$name] === $meta['def'] || ($meta['def'] === NULL && is_scalar($params[$name]) && (string) $params[$name] === '')) {
  142. $params[$name] = NULL; // value transmit is unnecessary
  143. }
  144. }
  145. }
  146. /**
  147. * Returns component param.
  148. * @param string key
  149. * @param mixed default value
  150. * @return mixed
  151. */
  152. public function getParameter($name, $default = NULL)
  153. {
  154. if (isset($this->params[$name])) {
  155. return $this->params[$name];
  156. } else {
  157. return $default;
  158. }
  159. }
  160. /**
  161. * Returns component parameters.
  162. * @return array
  163. */
  164. public function getParameters()
  165. {
  166. return $this->params;
  167. }
  168. /**
  169. * Returns a fully-qualified name that uniquely identifies the parameter.
  170. * @param string
  171. * @return string
  172. */
  173. public function getParameterId($name)
  174. {
  175. $uid = $this->getUniqueId();
  176. return $uid === '' ? $name : $uid . self::NAME_SEPARATOR . $name;
  177. }
  178. /** @deprecated */
  179. function getParam($name = NULL, $default = NULL)
  180. {
  181. //trigger_error(__METHOD__ . '() is deprecated; use getParameter() instead.', E_USER_DEPRECATED);
  182. return func_num_args() ? $this->getParameter($name, $default) : $this->getParameter();
  183. }
  184. /**
  185. * Returns array of classes persistent parameters. They have public visibility and are non-static.
  186. * This default implementation detects persistent parameters by annotation @persistent.
  187. * @return array
  188. */
  189. public static function getPersistentParams()
  190. {
  191. $rc = new Nette\Reflection\ClassType(get_called_class());
  192. $params = array();
  193. foreach ($rc->getProperties(\ReflectionProperty::IS_PUBLIC) as $rp) {
  194. if (!$rp->isStatic() && $rp->hasAnnotation('persistent')) {
  195. $params[] = $rp->getName();
  196. }
  197. }
  198. return $params;
  199. }
  200. /********************* interface ISignalReceiver ****************d*g**/
  201. /**
  202. * Calls signal handler method.
  203. * @param string
  204. * @return void
  205. * @throws BadSignalException if there is not handler method
  206. */
  207. public function signalReceived($signal)
  208. {
  209. if (!$this->tryCall($this->formatSignalMethod($signal), $this->params)) {
  210. $class = get_class($this);
  211. throw new BadSignalException("There is no handler for signal '$signal' in class $class.");
  212. }
  213. }
  214. /**
  215. * Formats signal handler method name -> case sensitivity doesn't matter.
  216. * @param string
  217. * @return string
  218. */
  219. public static function formatSignalMethod($signal)
  220. {
  221. return $signal == NULL ? NULL : 'handle' . $signal; // intentionally ==
  222. }
  223. /********************* navigation ****************d*g**/
  224. /**
  225. * Generates URL to presenter, action or signal.
  226. * @param string destination in format "[[module:]presenter:]action" or "signal!" or "this"
  227. * @param array|mixed
  228. * @return string
  229. * @throws InvalidLinkException
  230. */
  231. public function link($destination, $args = array())
  232. {
  233. try {
  234. return $this->getPresenter()->createRequest($this, $destination, is_array($args) ? $args : array_slice(func_get_args(), 1), 'link');
  235. } catch (InvalidLinkException $e) {
  236. return $this->getPresenter()->handleInvalidLink($e);
  237. }
  238. }
  239. /**
  240. * Returns destination as Link object.
  241. * @param string destination in format "[[module:]presenter:]view" or "signal!"
  242. * @param array|mixed
  243. * @return Link
  244. */
  245. public function lazyLink($destination, $args = array())
  246. {
  247. return new Link($this, $destination, is_array($args) ? $args : array_slice(func_get_args(), 1));
  248. }
  249. /**
  250. * Determines whether it links to the current page.
  251. * @param string destination in format "[[module:]presenter:]action" or "signal!" or "this"
  252. * @param array|mixed
  253. * @return bool
  254. * @throws InvalidLinkException
  255. */
  256. public function isLinkCurrent($destination = NULL, $args = array())
  257. {
  258. if ($destination !== NULL) {
  259. $this->getPresenter()->createRequest($this, $destination, is_array($args) ? $args : array_slice(func_get_args(), 1), 'test');
  260. }
  261. return $this->getPresenter()->getLastCreatedRequestFlag('current');
  262. }
  263. /**
  264. * Redirect to another presenter, action or signal.
  265. * @param int [optional] HTTP error code
  266. * @param string destination in format "[[module:]presenter:]view" or "signal!"
  267. * @param array|mixed
  268. * @return void
  269. * @throws Nette\Application\AbortException
  270. */
  271. public function redirect($code, $destination = NULL, $args = array())
  272. {
  273. if (!is_numeric($code)) { // first parameter is optional
  274. $args = $destination;
  275. $destination = $code;
  276. $code = NULL;
  277. }
  278. if (!is_array($args)) {
  279. $args = array_slice(func_get_args(), is_numeric($code) ? 2 : 1);
  280. }
  281. $presenter = $this->getPresenter();
  282. $presenter->redirectUrl($presenter->createRequest($this, $destination, $args, 'redirect'), $code);
  283. }
  284. /********************* interface \ArrayAccess ****************d*g**/
  285. /**
  286. * Adds the component to the container.
  287. * @param string component name
  288. * @param Nette\ComponentModel\IComponent
  289. * @return void
  290. */
  291. public function offsetSet($name, $component)
  292. {
  293. $this->addComponent($component, $name);
  294. }
  295. /**
  296. * Returns component specified by name. Throws exception if component doesn't exist.
  297. * @param string component name
  298. * @return Nette\ComponentModel\IComponent
  299. * @throws Nette\InvalidArgumentException
  300. */
  301. public function offsetGet($name)
  302. {
  303. return $this->getComponent($name, TRUE);
  304. }
  305. /**
  306. * Does component specified by name exists?
  307. * @param string component name
  308. * @return bool
  309. */
  310. public function offsetExists($name)
  311. {
  312. return $this->getComponent($name, FALSE) !== NULL;
  313. }
  314. /**
  315. * Removes component from the container.
  316. * @param string component name
  317. * @return void
  318. */
  319. public function offsetUnset($name)
  320. {
  321. $component = $this->getComponent($name, FALSE);
  322. if ($component !== NULL) {
  323. $this->removeComponent($component);
  324. }
  325. }
  326. }