4
* This file is part of the Nette Framework (http://nette.org)
6
* Copyright (c) 2004 David Grudl (http://davidgrudl.com)
8
* For the full copyright and license information, please view
9
* the file license.txt that was distributed with this source code.
12
namespace Nette\Application\UI;
18
* PresenterComponent is the base class for all Presenter components.
20
* Components are persistent objects located on a presenter. They have ability to own
21
* other child components, and interact with user. Components have properties
22
* for storing their status, and responds to user command.
26
* @property-read Presenter $presenter
27
* @property-read string $uniqueId
29
abstract class PresenterComponent extends Nette\ComponentModel\Container implements ISignalReceiver, IStatePersistent, \ArrayAccess
32
protected $params = array();
36
* Returns the presenter where this component belongs to.
37
* @param bool throw exception if presenter doesn't exist?
38
* @return Presenter|NULL
40
public function getPresenter($need = TRUE)
42
return $this->lookup('Nette\Application\UI\Presenter', $need);
47
* Returns a fully-qualified name that uniquely identifies the component
48
* within the presenter hierarchy.
51
public function getUniqueId()
53
return $this->lookupPath('Nette\Application\UI\Presenter', TRUE);
58
* This method will be called when the component (or component's parent)
59
* becomes attached to a monitored object. Do not call this method yourself.
60
* @param Nette\ComponentModel\IComponent
63
protected function attached($presenter)
65
if ($presenter instanceof Presenter) {
66
$this->loadState($presenter->popGlobalParameters($this->getUniqueId()));
74
protected function validateParent(Nette\ComponentModel\IContainer $parent)
76
parent::validateParent($parent);
77
$this->monitor('Nette\Application\UI\Presenter');
82
* Calls public method if exists.
85
* @return bool does method exist?
87
protected function tryCall($method, array $params)
89
$rc = $this->getReflection();
90
if ($rc->hasMethod($method)) {
91
$rm = $rc->getMethod($method);
92
if ($rm->isPublic() && !$rm->isAbstract() && !$rm->isStatic()) {
93
$this->checkRequirements($rm);
94
$rm->invokeArgs($this, $rc->combineArgs($rm, $params));
103
* Checks for requirements such as authorization.
106
public function checkRequirements($element)
112
* Access to reflection.
113
* @return PresenterComponentReflection
115
public static function getReflection()
117
return new PresenterComponentReflection(get_called_class());
121
/********************* interface IStatePersistent ****************d*g**/
125
* Loads state informations.
129
public function loadState(array $params)
131
$reflection = $this->getReflection();
132
foreach ($reflection->getPersistentParams() as $name => $meta) {
133
if (isset($params[$name])) { // NULLs are ignored
134
$type = gettype($meta['def'] === NULL ? $params[$name] : $meta['def']); // compatible with 2.0.x
135
if (!$reflection->convertType($params[$name], $type)) {
136
throw new Nette\Application\BadRequestException("Invalid value for persistent parameter '$name' in '{$this->getName()}', expected " . ($type === 'NULL' ? 'scalar' : $type) . ".");
138
$this->$name = & $params[$name];
140
$params[$name] = & $this->$name;
143
$this->params = $params;
148
* Saves state informations for next request.
150
* @param PresenterComponentReflection (internal, used by Presenter)
153
public function saveState(array & $params, $reflection = NULL)
155
$reflection = $reflection === NULL ? $this->getReflection() : $reflection;
156
foreach ($reflection->getPersistentParams() as $name => $meta) {
158
if (isset($params[$name])) {
161
} elseif (array_key_exists($name, $params)) { // NULLs are skipped
164
} elseif (!isset($meta['since']) || $this instanceof $meta['since']) {
165
$params[$name] = $this->$name; // object property value
168
continue; // ignored parameter
171
$type = gettype($meta['def'] === NULL ? $params[$name] : $meta['def']); // compatible with 2.0.x
172
if (!PresenterComponentReflection::convertType($params[$name], $type)) {
173
throw new InvalidLinkException("Invalid value for persistent parameter '$name' in '{$this->getName()}', expected " . ($type === 'NULL' ? 'scalar' : $type) . ".");
176
if ($params[$name] === $meta['def'] || ($meta['def'] === NULL && is_scalar($params[$name]) && (string) $params[$name] === '')) {
177
$params[$name] = NULL; // value transmit is unnecessary
184
* Returns component param.
185
* If no key is passed, returns the entire array.
187
* @param mixed default value
190
final public function getParameter($name = NULL, $default = NULL)
192
if (func_num_args() === 0) {
193
return $this->params;
195
} elseif (isset($this->params[$name])) {
196
return $this->params[$name];
205
* Returns a fully-qualified name that uniquely identifies the parameter.
209
final public function getParameterId($name)
211
$uid = $this->getUniqueId();
212
return $uid === '' ? $name : $uid . self::NAME_SEPARATOR . $name;
217
function getParam($name = NULL, $default = NULL)
219
//trigger_error(__METHOD__ . '() is deprecated; use getParameter() instead.', E_USER_WARNING);
220
return func_num_args() ? $this->getParameter($name, $default) : $this->getParameter();
225
function getParamId($name)
227
trigger_error(__METHOD__ . '() is deprecated; use getParameterId() instead.', E_USER_WARNING);
228
return $this->getParameterId($name);
233
* Returns array of classes persistent parameters. They have public visibility and are non-static.
234
* This default implementation detects persistent parameters by annotation @persistent.
237
public static function getPersistentParams()
239
$rc = new Nette\Reflection\ClassType(get_called_class());
241
foreach ($rc->getProperties(\ReflectionProperty::IS_PUBLIC) as $rp) {
242
if (!$rp->isStatic() && $rp->hasAnnotation('persistent')) {
243
$params[] = $rp->getName();
250
/********************* interface ISignalReceiver ****************d*g**/
254
* Calls signal handler method.
257
* @throws BadSignalException if there is not handler method
259
public function signalReceived($signal)
261
if (!$this->tryCall($this->formatSignalMethod($signal), $this->params)) {
262
$class = get_class($this);
263
throw new BadSignalException("There is no handler for signal '$signal' in class $class.");
269
* Formats signal handler method name -> case sensitivity doesn't matter.
273
public function formatSignalMethod($signal)
275
return $signal == NULL ? NULL : 'handle' . $signal; // intentionally ==
279
/********************* navigation ****************d*g**/
283
* Generates URL to presenter, action or signal.
284
* @param string destination in format "[[module:]presenter:]action" or "signal!" or "this"
287
* @throws InvalidLinkException
289
public function link($destination, $args = array())
292
return $this->getPresenter()->createRequest($this, $destination, is_array($args) ? $args : array_slice(func_get_args(), 1), 'link');
294
} catch (InvalidLinkException $e) {
295
return $this->getPresenter()->handleInvalidLink($e);
301
* Returns destination as Link object.
302
* @param string destination in format "[[module:]presenter:]view" or "signal!"
306
public function lazyLink($destination, $args = array())
308
return new Link($this, $destination, is_array($args) ? $args : array_slice(func_get_args(), 1));
313
* Determines whether it links to the current page.
314
* @param string destination in format "[[module:]presenter:]action" or "signal!" or "this"
317
* @throws InvalidLinkException
319
public function isLinkCurrent($destination = NULL, $args = array())
321
if ($destination !== NULL) {
322
$this->getPresenter()->createRequest($this, $destination, is_array($args) ? $args : array_slice(func_get_args(), 1), 'test');
324
return $this->getPresenter()->getLastCreatedRequestFlag('current');
329
* Redirect to another presenter, action or signal.
330
* @param int [optional] HTTP error code
331
* @param string destination in format "[[module:]presenter:]view" or "signal!"
334
* @throws Nette\Application\AbortException
336
public function redirect($code, $destination = NULL, $args = array())
338
if (!is_numeric($code)) { // first parameter is optional
339
$args = $destination;
340
$destination = $code;
344
if (!is_array($args)) {
345
$args = array_slice(func_get_args(), is_numeric($code) ? 2 : 1);
348
$presenter = $this->getPresenter();
349
$presenter->redirectUrl($presenter->createRequest($this, $destination, $args, 'redirect'), $code);
353
/********************* interface \ArrayAccess ****************d*g**/
357
* Adds the component to the container.
358
* @param string component name
359
* @param Nette\ComponentModel\IComponent
362
final public function offsetSet($name, $component)
364
$this->addComponent($component, $name);
369
* Returns component specified by name. Throws exception if component doesn't exist.
370
* @param string component name
371
* @return Nette\ComponentModel\IComponent
372
* @throws Nette\InvalidArgumentException
374
final public function offsetGet($name)
376
return $this->getComponent($name, TRUE);
381
* Does component specified by name exists?
382
* @param string component name
385
final public function offsetExists($name)
387
return $this->getComponent($name, FALSE) !== NULL;
392
* Removes component from the container.
393
* @param string component name
396
final public function offsetUnset($name)
398
$component = $this->getComponent($name, FALSE);
399
if ($component !== NULL) {
400
$this->removeComponent($component);