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

/libs/Nette/Application/UI/PresenterComponent.php

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