1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14:
15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
26: abstract class PresenterComponent extends ComponentContainer implements ISignalReceiver, IStatePersistent, ArrayAccess
27: {
28:
29: protected $params = array();
30:
31:
32:
33: 34:
35: public function __construct(IComponentContainer $parent = NULL, $name = NULL)
36: {
37: $this->monitor('Presenter');
38: parent::__construct($parent, $name);
39: }
40:
41:
42:
43: 44: 45: 46: 47:
48: public function getPresenter($need = TRUE)
49: {
50: return $this->lookup('Presenter', $need);
51: }
52:
53:
54:
55: 56: 57: 58: 59:
60: public function getUniqueId()
61: {
62: return $this->lookupPath('Presenter', TRUE);
63: }
64:
65:
66:
67: 68: 69: 70: 71: 72:
73: protected function attached($presenter)
74: {
75: if ($presenter instanceof Presenter) {
76: $this->loadState($presenter->popGlobalParams($this->getUniqueId()));
77: }
78: }
79:
80:
81:
82: 83: 84: 85: 86: 87:
88: protected function tryCall($method, array $params)
89: {
90: $rc = $this->getReflection();
91: if ($rc->hasMethod($method)) {
92: $rm = $rc->getMethod($method);
93: if ($rm->isPublic() && !$rm->isAbstract() && !$rm->isStatic()) {
94: $rm->invokeNamedArgs($this, $params);
95: return TRUE;
96: }
97: }
98: return FALSE;
99: }
100:
101:
102:
103: 104: 105: 106:
107: public function getReflection()
108: {
109: return new PresenterComponentReflection($this);
110: }
111:
112:
113:
114:
115:
116:
117:
118: 119: 120: 121: 122:
123: public function loadState(array $params)
124: {
125: foreach ($this->getReflection()->getPersistentParams() as $nm => $meta) {
126: if (isset($params[$nm])) { 127: if (isset($meta['def'])) {
128: if (is_array($params[$nm]) && !is_array($meta['def'])) {
129: $params[$nm] = $meta['def']; 130: } else {
131: settype($params[$nm], gettype($meta['def']));
132: }
133: }
134: $this->$nm = & $params[$nm];
135: }
136: }
137: $this->params = $params;
138: }
139:
140:
141:
142: 143: 144: 145: 146: 147:
148: public function saveState(array & $params, $reflection = NULL)
149: {
150: $reflection = $reflection === NULL ? $this->getReflection() : $reflection;
151: foreach ($reflection->getPersistentParams() as $nm => $meta) {
152:
153: if (isset($params[$nm])) {
154: $val = $params[$nm]; 155:
156: } elseif (array_key_exists($nm, $params)) { 157: continue; 158:
159: } elseif (!isset($meta['since']) || $this instanceof $meta['since']) {
160: $val = $this->$nm; 161:
162: } else {
163: continue; 164: }
165:
166: if (is_object($val)) {
167: throw new InvalidStateException("Persistent parameter must be scalar or array, {$this->reflection->name}::\$$nm is " . gettype($val));
168:
169: } else {
170: if (isset($meta['def'])) {
171: settype($val, gettype($meta['def']));
172: if ($val === $meta['def']) $val = NULL;
173: } else {
174: if ((string) $val === '') $val = NULL;
175: }
176: $params[$nm] = $val;
177: }
178: }
179: }
180:
181:
182:
183: 184: 185: 186: 187: 188: 189:
190: final public function getParam($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: 206: 207: 208:
209: final public function getParamId($name)
210: {
211: $uid = $this->getUniqueId();
212: return $uid === '' ? $name : $uid . self::NAME_SEPARATOR . $name;
213: }
214:
215:
216:
217: 218: 219: 220: 221:
222: public static function getPersistentParams()
223: {
224: $rc = new ClassReflection(func_get_arg(0));
225: $params = array();
226: foreach ($rc->getProperties(ReflectionProperty::IS_PUBLIC) as $rp) {
227: if (!$rp->isStatic() && $rp->hasAnnotation('persistent')) {
228: $params[] = $rp->getName();
229: }
230: }
231: return $params;
232: }
233:
234:
235:
236:
237:
238:
239:
240: 241: 242: 243: 244: 245:
246: public function signalReceived($signal)
247: {
248: if (!$this->tryCall($this->formatSignalMethod($signal), $this->params)) {
249: throw new BadSignalException("There is no handler for signal '$signal' in class {$this->reflection->name}.");
250: }
251: }
252:
253:
254:
255: 256: 257: 258: 259:
260: public function formatSignalMethod($signal)
261: {
262: return $signal == NULL ? NULL : 'handle' . $signal; 263: }
264:
265:
266:
267:
268:
269:
270:
271: 272: 273: 274: 275: 276: 277:
278: public function link($destination, $args = array())
279: {
280: if (!is_array($args)) {
281: $args = func_get_args();
282: array_shift($args);
283: }
284:
285: try {
286: return $this->getPresenter()->createRequest($this, $destination, $args, 'link');
287:
288: } catch (InvalidLinkException $e) {
289: return $this->getPresenter()->handleInvalidLink($e);
290: }
291: }
292:
293:
294:
295: 296: 297: 298: 299: 300:
301: public function lazyLink($destination, $args = array())
302: {
303: if (!is_array($args)) {
304: $args = func_get_args();
305: array_shift($args);
306: }
307:
308: return new Link($this, $destination, $args);
309: }
310:
311:
312:
313: 314: 315: 316: 317: 318: 319:
320: public function isLinkCurrent($destination = NULL, $args = array())
321: {
322: if ($destination !== NULL) {
323: if (!is_array($args)) {
324: $args = func_get_args();
325: array_shift($args);
326: }
327: $this->link($destination, $args);
328: }
329: return $this->getPresenter()->getLastCreatedRequestFlag('current');
330: }
331:
332:
333:
334: 335: 336: 337: 338: 339: 340: 341:
342: public function redirect($code, $destination = NULL, $args = array())
343: {
344: if (!is_numeric($code)) { 345: $args = $destination;
346: $destination = $code;
347: $code = NULL;
348: }
349:
350: if (!is_array($args)) {
351: $args = func_get_args();
352: if (is_numeric(array_shift($args))) array_shift($args);
353: }
354:
355: $presenter = $this->getPresenter();
356: $presenter->redirectUri($presenter->createRequest($this, $destination, $args, 'redirect'), $code);
357: }
358:
359:
360:
361:
362:
363:
364:
365: 366: 367: 368: 369: 370:
371: final public function offsetSet($name, $component)
372: {
373: $this->addComponent($component, $name);
374: }
375:
376:
377:
378: 379: 380: 381: 382: 383:
384: final public function offsetGet($name)
385: {
386: return $this->getComponent($name, TRUE);
387: }
388:
389:
390:
391: 392: 393: 394: 395:
396: final public function offsetExists($name)
397: {
398: return $this->getComponent($name, FALSE) !== NULL;
399: }
400:
401:
402:
403: 404: 405: 406: 407:
408: final public function offsetUnset($name)
409: {
410: $component = $this->getComponent($name, FALSE);
411: if ($component !== NULL) {
412: $this->removeComponent($component);
413: }
414: }
415:
416: }
417: