~ubuntu-branches/debian/experimental/php-nette/experimental

« back to all changes in this revision

Viewing changes to Nette-2.0.13/Nette/Application/UI/PresenterComponent.php

  • Committer: Package Import Robot
  • Author(s): David Prévot
  • Date: 2013-11-30 08:47:54 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20131130084754-4udf1xsu9085tnfc
Tags: 2.1.0~rc-1
* New upstream branch
* Update copyright

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
<?php
2
 
 
3
 
/**
4
 
 * This file is part of the Nette Framework (http://nette.org)
5
 
 *
6
 
 * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
7
 
 *
8
 
 * For the full copyright and license information, please view
9
 
 * the file license.txt that was distributed with this source code.
10
 
 */
11
 
 
12
 
namespace Nette\Application\UI;
13
 
 
14
 
use Nette;
15
 
 
16
 
 
17
 
/**
18
 
 * PresenterComponent is the base class for all Presenter components.
19
 
 *
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.
23
 
 *
24
 
 * @author     David Grudl
25
 
 *
26
 
 * @property-read Presenter $presenter
27
 
 * @property-read string $uniqueId
28
 
 */
29
 
abstract class PresenterComponent extends Nette\ComponentModel\Container implements ISignalReceiver, IStatePersistent, \ArrayAccess
30
 
{
31
 
        /** @var array */
32
 
        protected $params = array();
33
 
 
34
 
 
35
 
        /**
36
 
         * Returns the presenter where this component belongs to.
37
 
         * @param  bool   throw exception if presenter doesn't exist?
38
 
         * @return Presenter|NULL
39
 
         */
40
 
        public function getPresenter($need = TRUE)
41
 
        {
42
 
                return $this->lookup('Nette\Application\UI\Presenter', $need);
43
 
        }
44
 
 
45
 
 
46
 
        /**
47
 
         * Returns a fully-qualified name that uniquely identifies the component
48
 
         * within the presenter hierarchy.
49
 
         * @return string
50
 
         */
51
 
        public function getUniqueId()
52
 
        {
53
 
                return $this->lookupPath('Nette\Application\UI\Presenter', TRUE);
54
 
        }
55
 
 
56
 
 
57
 
        /**
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
61
 
         * @return void
62
 
         */
63
 
        protected function attached($presenter)
64
 
        {
65
 
                if ($presenter instanceof Presenter) {
66
 
                        $this->loadState($presenter->popGlobalParameters($this->getUniqueId()));
67
 
                }
68
 
        }
69
 
 
70
 
 
71
 
        /**
72
 
         * @return void
73
 
         */
74
 
        protected function validateParent(Nette\ComponentModel\IContainer $parent)
75
 
        {
76
 
                parent::validateParent($parent);
77
 
                $this->monitor('Nette\Application\UI\Presenter');
78
 
        }
79
 
 
80
 
 
81
 
        /**
82
 
         * Calls public method if exists.
83
 
         * @param  string
84
 
         * @param  array
85
 
         * @return bool  does method exist?
86
 
         */
87
 
        protected function tryCall($method, array $params)
88
 
        {
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));
95
 
                                return TRUE;
96
 
                        }
97
 
                }
98
 
                return FALSE;
99
 
        }
100
 
 
101
 
 
102
 
        /**
103
 
         * Checks for requirements such as authorization.
104
 
         * @return void
105
 
         */
106
 
        public function checkRequirements($element)
107
 
        {
108
 
        }
109
 
 
110
 
 
111
 
        /**
112
 
         * Access to reflection.
113
 
         * @return PresenterComponentReflection
114
 
         */
115
 
        public static function getReflection()
116
 
        {
117
 
                return new PresenterComponentReflection(get_called_class());
118
 
        }
119
 
 
120
 
 
121
 
        /********************* interface IStatePersistent ****************d*g**/
122
 
 
123
 
 
124
 
        /**
125
 
         * Loads state informations.
126
 
         * @param  array
127
 
         * @return void
128
 
         */
129
 
        public function loadState(array $params)
130
 
        {
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) . ".");
137
 
                                }
138
 
                                $this->$name = & $params[$name];
139
 
                        } else {
140
 
                                $params[$name] = & $this->$name;
141
 
                        }
142
 
                }
143
 
                $this->params = $params;
144
 
        }
145
 
 
146
 
 
147
 
        /**
148
 
         * Saves state informations for next request.
149
 
         * @param  array
150
 
         * @param  PresenterComponentReflection (internal, used by Presenter)
151
 
         * @return void
152
 
         */
153
 
        public function saveState(array & $params, $reflection = NULL)
154
 
        {
155
 
                $reflection = $reflection === NULL ? $this->getReflection() : $reflection;
156
 
                foreach ($reflection->getPersistentParams() as $name => $meta) {
157
 
 
158
 
                        if (isset($params[$name])) {
159
 
                                // injected value
160
 
 
161
 
                        } elseif (array_key_exists($name, $params)) { // NULLs are skipped
162
 
                                continue;
163
 
 
164
 
                        } elseif (!isset($meta['since']) || $this instanceof $meta['since']) {
165
 
                                $params[$name] = $this->$name; // object property value
166
 
 
167
 
                        } else {
168
 
                                continue; // ignored parameter
169
 
                        }
170
 
 
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) . ".");
174
 
                        }
175
 
 
176
 
                        if ($params[$name] === $meta['def'] || ($meta['def'] === NULL && is_scalar($params[$name]) && (string) $params[$name] === '')) {
177
 
                                $params[$name] = NULL; // value transmit is unnecessary
178
 
                        }
179
 
                }
180
 
        }
181
 
 
182
 
 
183
 
        /**
184
 
         * Returns component param.
185
 
         * If no key is passed, returns the entire array.
186
 
         * @param  string key
187
 
         * @param  mixed  default value
188
 
         * @return mixed
189
 
         */
190
 
        final public function getParameter($name = NULL, $default = NULL)
191
 
        {
192
 
                if (func_num_args() === 0) {
193
 
                        return $this->params;
194
 
 
195
 
                } elseif (isset($this->params[$name])) {
196
 
                        return $this->params[$name];
197
 
 
198
 
                } else {
199
 
                        return $default;
200
 
                }
201
 
        }
202
 
 
203
 
 
204
 
        /**
205
 
         * Returns a fully-qualified name that uniquely identifies the parameter.
206
 
         * @param  string
207
 
         * @return string
208
 
         */
209
 
        final public function getParameterId($name)
210
 
        {
211
 
                $uid = $this->getUniqueId();
212
 
                return $uid === '' ? $name : $uid . self::NAME_SEPARATOR . $name;
213
 
        }
214
 
 
215
 
 
216
 
        /** @deprecated */
217
 
        function getParam($name = NULL, $default = NULL)
218
 
        {
219
 
                //trigger_error(__METHOD__ . '() is deprecated; use getParameter() instead.', E_USER_WARNING);
220
 
                return func_num_args() ? $this->getParameter($name, $default) : $this->getParameter();
221
 
        }
222
 
 
223
 
 
224
 
        /** @deprecated */
225
 
        function getParamId($name)
226
 
        {
227
 
                trigger_error(__METHOD__ . '() is deprecated; use getParameterId() instead.', E_USER_WARNING);
228
 
                return $this->getParameterId($name);
229
 
        }
230
 
 
231
 
 
232
 
        /**
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.
235
 
         * @return array
236
 
         */
237
 
        public static function getPersistentParams()
238
 
        {
239
 
                $rc = new Nette\Reflection\ClassType(get_called_class());
240
 
                $params = array();
241
 
                foreach ($rc->getProperties(\ReflectionProperty::IS_PUBLIC) as $rp) {
242
 
                        if (!$rp->isStatic() && $rp->hasAnnotation('persistent')) {
243
 
                                $params[] = $rp->getName();
244
 
                        }
245
 
                }
246
 
                return $params;
247
 
        }
248
 
 
249
 
 
250
 
        /********************* interface ISignalReceiver ****************d*g**/
251
 
 
252
 
 
253
 
        /**
254
 
         * Calls signal handler method.
255
 
         * @param  string
256
 
         * @return void
257
 
         * @throws BadSignalException if there is not handler method
258
 
         */
259
 
        public function signalReceived($signal)
260
 
        {
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.");
264
 
                }
265
 
        }
266
 
 
267
 
 
268
 
        /**
269
 
         * Formats signal handler method name -> case sensitivity doesn't matter.
270
 
         * @param  string
271
 
         * @return string
272
 
         */
273
 
        public function formatSignalMethod($signal)
274
 
        {
275
 
                return $signal == NULL ? NULL : 'handle' . $signal; // intentionally ==
276
 
        }
277
 
 
278
 
 
279
 
        /********************* navigation ****************d*g**/
280
 
 
281
 
 
282
 
        /**
283
 
         * Generates URL to presenter, action or signal.
284
 
         * @param  string   destination in format "[[module:]presenter:]action" or "signal!" or "this"
285
 
         * @param  array|mixed
286
 
         * @return string
287
 
         * @throws InvalidLinkException
288
 
         */
289
 
        public function link($destination, $args = array())
290
 
        {
291
 
                try {
292
 
                        return $this->getPresenter()->createRequest($this, $destination, is_array($args) ? $args : array_slice(func_get_args(), 1), 'link');
293
 
 
294
 
                } catch (InvalidLinkException $e) {
295
 
                        return $this->getPresenter()->handleInvalidLink($e);
296
 
                }
297
 
        }
298
 
 
299
 
 
300
 
        /**
301
 
         * Returns destination as Link object.
302
 
         * @param  string   destination in format "[[module:]presenter:]view" or "signal!"
303
 
         * @param  array|mixed
304
 
         * @return Link
305
 
         */
306
 
        public function lazyLink($destination, $args = array())
307
 
        {
308
 
                return new Link($this, $destination, is_array($args) ? $args : array_slice(func_get_args(), 1));
309
 
        }
310
 
 
311
 
 
312
 
        /**
313
 
         * Determines whether it links to the current page.
314
 
         * @param  string   destination in format "[[module:]presenter:]action" or "signal!" or "this"
315
 
         * @param  array|mixed
316
 
         * @return bool
317
 
         * @throws InvalidLinkException
318
 
         */
319
 
        public function isLinkCurrent($destination = NULL, $args = array())
320
 
        {
321
 
                if ($destination !== NULL) {
322
 
                        $this->getPresenter()->createRequest($this, $destination, is_array($args) ? $args : array_slice(func_get_args(), 1), 'test');
323
 
                }
324
 
                return $this->getPresenter()->getLastCreatedRequestFlag('current');
325
 
        }
326
 
 
327
 
 
328
 
        /**
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!"
332
 
         * @param  array|mixed
333
 
         * @return void
334
 
         * @throws Nette\Application\AbortException
335
 
         */
336
 
        public function redirect($code, $destination = NULL, $args = array())
337
 
        {
338
 
                if (!is_numeric($code)) { // first parameter is optional
339
 
                        $args = $destination;
340
 
                        $destination = $code;
341
 
                        $code = NULL;
342
 
                }
343
 
 
344
 
                if (!is_array($args)) {
345
 
                        $args = array_slice(func_get_args(), is_numeric($code) ? 2 : 1);
346
 
                }
347
 
 
348
 
                $presenter = $this->getPresenter();
349
 
                $presenter->redirectUrl($presenter->createRequest($this, $destination, $args, 'redirect'), $code);
350
 
        }
351
 
 
352
 
 
353
 
        /********************* interface \ArrayAccess ****************d*g**/
354
 
 
355
 
 
356
 
        /**
357
 
         * Adds the component to the container.
358
 
         * @param  string  component name
359
 
         * @param  Nette\ComponentModel\IComponent
360
 
         * @return void
361
 
         */
362
 
        final public function offsetSet($name, $component)
363
 
        {
364
 
                $this->addComponent($component, $name);
365
 
        }
366
 
 
367
 
 
368
 
        /**
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
373
 
         */
374
 
        final public function offsetGet($name)
375
 
        {
376
 
                return $this->getComponent($name, TRUE);
377
 
        }
378
 
 
379
 
 
380
 
        /**
381
 
         * Does component specified by name exists?
382
 
         * @param  string  component name
383
 
         * @return bool
384
 
         */
385
 
        final public function offsetExists($name)
386
 
        {
387
 
                return $this->getComponent($name, FALSE) !== NULL;
388
 
        }
389
 
 
390
 
 
391
 
        /**
392
 
         * Removes component from the container.
393
 
         * @param  string  component name
394
 
         * @return void
395
 
         */
396
 
        final public function offsetUnset($name)
397
 
        {
398
 
                $component = $this->getComponent($name, FALSE);
399
 
                if ($component !== NULL) {
400
 
                        $this->removeComponent($component);
401
 
                }
402
 
        }
403
 
 
404
 
}