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: 27: 28: 29: 30: 31: 32: 33: 34:
35: abstract class Presenter extends Control implements IPresenter
36: {
37:
38: const INVALID_LINK_SILENT = 1,
39: INVALID_LINK_WARNING = 2,
40: INVALID_LINK_EXCEPTION = 3;
41:
42:
43: const SIGNAL_KEY = 'do',
44: ACTION_KEY = 'action',
45: FLASH_KEY = '_fid',
46: DEFAULT_ACTION = 'default';
47:
48:
49: public $invalidLinkMode;
50:
51:
52: public $onShutdown;
53:
54:
55: private $request;
56:
57:
58: private $response;
59:
60:
61: public $autoCanonicalize = TRUE;
62:
63:
64: public $absoluteUrls = FALSE;
65:
66:
67: private $globalParams;
68:
69:
70: private $globalState;
71:
72:
73: private $globalStateSinces;
74:
75:
76: private $action;
77:
78:
79: private $view;
80:
81:
82: private $layout;
83:
84:
85: private $payload;
86:
87:
88: private $signalReceiver;
89:
90:
91: private $signal;
92:
93:
94: private $ajaxMode;
95:
96:
97: private $startupCheck;
98:
99:
100: private $lastCreatedRequest;
101:
102:
103: private $lastCreatedRequestFlag;
104:
105:
106: private $context;
107:
108:
109:
110: public function __construct(IDIContainer $context)
111: {
112: $this->context = $context;
113: if ($this->invalidLinkMode === NULL) {
114: $this->invalidLinkMode = $context->parameters['productionMode'] ? self::INVALID_LINK_SILENT : self::INVALID_LINK_WARNING;
115: }
116: }
117:
118:
119:
120: 121: 122:
123: final public function getRequest()
124: {
125: return $this->request;
126: }
127:
128:
129:
130: 131: 132: 133:
134: final public function getPresenter($need = TRUE)
135: {
136: return $this;
137: }
138:
139:
140:
141: 142: 143: 144:
145: final public function getUniqueId()
146: {
147: return '';
148: }
149:
150:
151:
152:
153:
154:
155:
156: 157: 158: 159:
160: public function run(PresenterRequest $request)
161: {
162: try {
163:
164: $this->request = $request;
165: $this->payload = (object) NULL;
166: $this->setParent($this->getParent(), $request->getPresenterName());
167:
168: $this->initGlobalParameters();
169: $this->checkRequirements($this->getReflection());
170: $this->startup();
171: if (!$this->startupCheck) {
172: $class = $this->getReflection()->getMethod('startup')->getDeclaringClass()->getName();
173: throw new InvalidStateException("Method $class::startup() or its descendant doesn't call parent::startup().");
174: }
175:
176: $this->tryCall($this->formatActionMethod($this->getAction()), $this->params);
177:
178: if ($this->autoCanonicalize) {
179: $this->canonicalize();
180: }
181: if ($this->getHttpRequest()->isMethod('head')) {
182: $this->terminate();
183: }
184:
185:
186:
187: $this->processSignal();
188:
189:
190: $this->beforeRender();
191:
192: $this->tryCall($this->formatRenderMethod($this->getView()), $this->params);
193: $this->afterRender();
194:
195:
196: $this->saveGlobalState();
197: if ($this->isAjax()) {
198: $this->payload->state = $this->getGlobalState();
199: }
200:
201:
202: $this->sendTemplate();
203:
204: } catch (AbortException $e) {
205:
206: if ($this->isAjax()) try {
207: $hasPayload = (array) $this->payload; unset($hasPayload['state']);
208: if ($this->response instanceof TextResponse && $this->isControlInvalid()) {
209: $this->snippetMode = TRUE;
210: $this->response->send($this->getHttpRequest(), $this->getHttpResponse());
211: $this->sendPayload();
212:
213: } elseif (!$this->response && $hasPayload) {
214: $this->sendPayload();
215: }
216: } catch (AbortException $e) { }
217:
218: if ($this->hasFlashSession()) {
219: $this->getFlashSession()->setExpiration($this->response instanceof RedirectResponse ? '+ 30 seconds': '+ 3 seconds');
220: }
221:
222:
223: $this->onShutdown($this, $this->response);
224: $this->shutdown($this->response);
225:
226: return $this->response;
227: }
228: }
229:
230:
231:
232: 233: 234:
235: protected function startup()
236: {
237: $this->startupCheck = TRUE;
238: }
239:
240:
241:
242: 243: 244: 245:
246: protected function beforeRender()
247: {
248: }
249:
250:
251:
252: 253: 254: 255:
256: protected function afterRender()
257: {
258: }
259:
260:
261:
262: 263: 264: 265:
266: protected function shutdown($response)
267: {
268: }
269:
270:
271:
272: 273: 274: 275:
276: public function checkRequirements($element)
277: {
278: $user = (array) $element->getAnnotation('User');
279: if (in_array('loggedIn', $user) && !$this->getUser()->isLoggedIn()) {
280: throw new ForbiddenRequestException;
281: }
282: }
283:
284:
285:
286:
287:
288:
289:
290: 291: 292: 293:
294: public function processSignal()
295: {
296: if ($this->signal === NULL) {
297: return;
298: }
299:
300: $component = $this->signalReceiver === '' ? $this : $this->getComponent($this->signalReceiver, FALSE);
301: if ($component === NULL) {
302: throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not found.");
303:
304: } elseif (!$component instanceof ISignalReceiver) {
305: throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not ISignalReceiver implementor.");
306: }
307:
308: $component->signalReceived($this->signal);
309: $this->signal = NULL;
310: }
311:
312:
313:
314: 315: 316: 317:
318: final public function getSignal()
319: {
320: return $this->signal === NULL ? NULL : array($this->signalReceiver, $this->signal);
321: }
322:
323:
324:
325: 326: 327: 328: 329: 330:
331: final public function isSignalReceiver($component, $signal = NULL)
332: {
333: if ($component instanceof Component) {
334: $component = $component === $this ? '' : $component->lookupPath(__CLASS__, TRUE);
335: }
336:
337: if ($this->signal === NULL) {
338: return FALSE;
339:
340: } elseif ($signal === TRUE) {
341: return $component === ''
342: || strncmp($this->signalReceiver . '-', $component . '-', strlen($component) + 1) === 0;
343:
344: } elseif ($signal === NULL) {
345: return $this->signalReceiver === $component;
346:
347: } else {
348: return $this->signalReceiver === $component && strcasecmp($signal, $this->signal) === 0;
349: }
350: }
351:
352:
353:
354:
355:
356:
357:
358: 359: 360: 361:
362: final public function getAction($fullyQualified = FALSE)
363: {
364: return $fullyQualified ? ':' . $this->getName() . ':' . $this->action : $this->action;
365: }
366:
367:
368:
369: 370: 371: 372: 373:
374: public function changeAction($action)
375: {
376: if (is_string($action) && Strings::match($action, '#^[a-zA-Z0-9][a-zA-Z0-9_\x7f-\xff]*$#')) {
377: $this->action = $action;
378: $this->view = $action;
379:
380: } else {
381: $this->error('Action name is not alphanumeric string.');
382: }
383: }
384:
385:
386:
387: 388: 389: 390:
391: final public function getView()
392: {
393: return $this->view;
394: }
395:
396:
397:
398: 399: 400: 401: 402:
403: public function setView($view)
404: {
405: $this->view = (string) $view;
406: return $this;
407: }
408:
409:
410:
411: 412: 413: 414:
415: final public function getLayout()
416: {
417: return $this->layout;
418: }
419:
420:
421:
422: 423: 424: 425: 426:
427: public function setLayout($layout)
428: {
429: $this->layout = $layout === FALSE ? FALSE : (string) $layout;
430: return $this;
431: }
432:
433:
434:
435: 436: 437: 438: 439:
440: public function sendTemplate()
441: {
442: $template = $this->getTemplate();
443: if (!$template) {
444: return;
445: }
446:
447: if ($template instanceof IFileTemplate && !$template->getFile()) {
448: $files = $this->formatTemplateFiles();
449: foreach ($files as $file) {
450: if (is_file($file)) {
451: $template->setFile($file);
452: break;
453: }
454: }
455:
456: if (!$template->getFile()) {
457: $file = preg_replace('#^.*([/\\\\].{1,70})$#U', "\xE2\x80\xA6\$1", reset($files));
458: $file = strtr($file, '/', DIRECTORY_SEPARATOR);
459: $this->error("Page not found. Missing template '$file'.");
460: }
461: }
462:
463: if ($this->layout !== FALSE) {
464: $files = $this->formatLayoutTemplateFiles();
465: foreach ($files as $file) {
466: if (is_file($file)) {
467: $template->layout = $file;
468: $template->_extends = $file;
469: break;
470: }
471: }
472:
473: if (empty($template->layout) && $this->layout !== NULL) {
474: $file = preg_replace('#^.*([/\\\\].{1,70})$#U', "\xE2\x80\xA6\$1", reset($files));
475: $file = strtr($file, '/', DIRECTORY_SEPARATOR);
476: throw new FileNotFoundException("Layout not found. Missing template '$file'.");
477: }
478: }
479:
480: $this->sendResponse(new TextResponse($template));
481: }
482:
483:
484:
485: 486: 487: 488:
489: public function formatLayoutTemplateFiles()
490: {
491: $name = $this->getName();
492: $presenter = substr($name, strrpos(':' . $name, ':'));
493: $layout = $this->layout ? $this->layout : 'layout';
494: $dir = dirname(dirname($this->getReflection()->getFileName()));
495: $list = array(
496: "$dir/templates/$presenter/@$layout.latte",
497: "$dir/templates/$presenter.@$layout.latte",
498: "$dir/templates/$presenter/@$layout.phtml",
499: "$dir/templates/$presenter.@$layout.phtml",
500: );
501: do {
502: $list[] = "$dir/templates/@$layout.latte";
503: $list[] = "$dir/templates/@$layout.phtml";
504: $dir = dirname($dir);
505: } while ($dir && ($name = substr($name, 0, strrpos($name, ':'))));
506: return $list;
507: }
508:
509:
510:
511: 512: 513: 514:
515: public function formatTemplateFiles()
516: {
517: $name = $this->getName();
518: $presenter = substr($name, strrpos(':' . $name, ':'));
519: $dir = dirname(dirname($this->getReflection()->getFileName()));
520: return array(
521: "$dir/templates/$presenter/$this->view.latte",
522: "$dir/templates/$presenter.$this->view.latte",
523: "$dir/templates/$presenter/$this->view.phtml",
524: "$dir/templates/$presenter.$this->view.phtml",
525: );
526: }
527:
528:
529:
530: 531: 532: 533: 534:
535: protected static function formatActionMethod($action)
536: {
537: return 'action' . $action;
538: }
539:
540:
541:
542: 543: 544: 545: 546:
547: protected static function formatRenderMethod($view)
548: {
549: return 'render' . $view;
550: }
551:
552:
553:
554:
555:
556:
557:
558: 559: 560:
561: final public function getPayload()
562: {
563: return $this->payload;
564: }
565:
566:
567:
568: 569: 570: 571:
572: public function isAjax()
573: {
574: if ($this->ajaxMode === NULL) {
575: $this->ajaxMode = $this->getHttpRequest()->isAjax();
576: }
577: return $this->ajaxMode;
578: }
579:
580:
581:
582: 583: 584: 585: 586:
587: public function sendPayload()
588: {
589: $this->sendResponse(new JsonResponse($this->payload));
590: }
591:
592:
593:
594:
595:
596:
597:
598: 599: 600: 601: 602: 603:
604: public function sendResponse(IPresenterResponse $response)
605: {
606: $this->response = $response;
607: $this->terminate();
608: }
609:
610:
611:
612: 613: 614: 615: 616:
617: public function terminate()
618: {
619: if (func_num_args() !== 0) {
620: trigger_error(__METHOD__ . ' is not intended to send a Application\Response; use sendResponse() instead.', E_USER_WARNING);
621: $this->sendResponse(func_get_arg(0));
622: }
623: throw new AbortException();
624: }
625:
626:
627:
628: 629: 630: 631: 632: 633: 634:
635: public function forward($destination, $args = array())
636: {
637: if ($destination instanceof PresenterRequest) {
638: $this->sendResponse(new ForwardResponse($destination));
639:
640: } elseif (!is_array($args)) {
641: $args = func_get_args();
642: array_shift($args);
643: }
644:
645: $this->createRequest($this, $destination, $args, 'forward');
646: $this->sendResponse(new ForwardResponse($this->lastCreatedRequest));
647: }
648:
649:
650:
651: 652: 653: 654: 655: 656: 657:
658: public function redirectUrl($url, $code = NULL)
659: {
660: if ($this->isAjax()) {
661: $this->payload->redirect = (string) $url;
662: $this->sendPayload();
663:
664: } elseif (!$code) {
665: $code = $this->getHttpRequest()->isMethod('post')
666: ? IHttpResponse::S303_POST_GET
667: : IHttpResponse::S302_FOUND;
668: }
669: $this->sendResponse(new RedirectResponse($url, $code));
670: }
671:
672:
673: function redirectUri($url, $code = NULL)
674: {
675: trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::redirectUrl() instead.', E_USER_WARNING);
676: $this->redirectUrl($url, $code);
677: }
678:
679:
680:
681: 682: 683: 684: 685: 686: 687:
688: public function error($message = NULL, $code = IHttpResponse::S404_NOT_FOUND)
689: {
690: throw new BadRequestException($message, $code);
691: }
692:
693:
694:
695: 696: 697: 698:
699: public function backlink()
700: {
701: return $this->getAction(TRUE);
702: }
703:
704:
705:
706: 707: 708: 709:
710: public function getLastCreatedRequest()
711: {
712: return $this->lastCreatedRequest;
713: }
714:
715:
716:
717: 718: 719: 720: 721:
722: public function getLastCreatedRequestFlag($flag)
723: {
724: return !empty($this->lastCreatedRequestFlag[$flag]);
725: }
726:
727:
728:
729: 730: 731: 732: 733:
734: public function canonicalize()
735: {
736: if (!$this->isAjax() && ($this->request->isMethod('get') || $this->request->isMethod('head'))) {
737: try {
738: $url = $this->createRequest($this, $this->action, $this->getGlobalState() + $this->request->getParameters(), 'redirectX');
739: } catch (InvalidLinkException $e) {}
740: if (isset($url) && !$this->getHttpRequest()->getUrl()->isEqual($url)) {
741: $this->sendResponse(new RedirectResponse($url, IHttpResponse::S301_MOVED_PERMANENTLY));
742: }
743: }
744: }
745:
746:
747:
748: 749: 750: 751: 752: 753: 754: 755: 756:
757: public function lastModified($lastModified, $etag = NULL, $expire = NULL)
758: {
759: if ($expire !== NULL) {
760: $this->getHttpResponse()->setExpiration($expire);
761: }
762:
763: if (!$this->getHttpContext()->isModified($lastModified, $etag)) {
764: $this->terminate();
765: }
766: }
767:
768:
769:
770: 771: 772: 773: 774: 775: 776: 777: 778: 779:
780: final protected function createRequest($component, $destination, array $args, $mode)
781: {
782:
783:
784:
785: static $presenterFactory, $router, $refUrl;
786: if ($presenterFactory === NULL) {
787: $presenterFactory = $this->getApplication()->getPresenterFactory();
788: $router = $this->getApplication()->getRouter();
789: $refUrl = new Url($this->getHttpRequest()->getUrl());
790: $refUrl->setPath($this->getHttpRequest()->getUrl()->getScriptPath());
791: }
792:
793: $this->lastCreatedRequest = $this->lastCreatedRequestFlag = NULL;
794:
795:
796:
797: $a = strpos($destination, '#');
798: if ($a === FALSE) {
799: $fragment = '';
800: } else {
801: $fragment = substr($destination, $a);
802: $destination = substr($destination, 0, $a);
803: }
804:
805:
806: $a = strpos($destination, '?');
807: if ($a !== FALSE) {
808: parse_str(substr($destination, $a + 1), $args);
809: $destination = substr($destination, 0, $a);
810: }
811:
812:
813: $a = strpos($destination, '//');
814: if ($a === FALSE) {
815: $scheme = FALSE;
816: } else {
817: $scheme = substr($destination, 0, $a);
818: $destination = substr($destination, $a + 2);
819: }
820:
821:
822: if (!$component instanceof Presenter || substr($destination, -1) === '!') {
823: $signal = rtrim($destination, '!');
824: $a = strrpos($signal, ':');
825: if ($a !== FALSE) {
826: $component = $component->getComponent(strtr(substr($signal, 0, $a), ':', '-'));
827: $signal = (string) substr($signal, $a + 1);
828: }
829: if ($signal == NULL) {
830: throw new InvalidLinkException("Signal must be non-empty string.");
831: }
832: $destination = 'this';
833: }
834:
835: if ($destination == NULL) {
836: throw new InvalidLinkException("Destination must be non-empty string.");
837: }
838:
839:
840: $current = FALSE;
841: $a = strrpos($destination, ':');
842: if ($a === FALSE) {
843: $action = $destination === 'this' ? $this->action : $destination;
844: $presenter = $this->getName();
845: $presenterClass = get_class($this);
846:
847: } else {
848: $action = (string) substr($destination, $a + 1);
849: if ($destination[0] === ':') {
850: if ($a < 2) {
851: throw new InvalidLinkException("Missing presenter name in '$destination'.");
852: }
853: $presenter = substr($destination, 1, $a - 1);
854:
855: } else {
856: $presenter = $this->getName();
857: $b = strrpos($presenter, ':');
858: if ($b === FALSE) {
859: $presenter = substr($destination, 0, $a);
860: } else {
861: $presenter = substr($presenter, 0, $b + 1) . substr($destination, 0, $a);
862: }
863: }
864: try {
865: $presenterClass = $presenterFactory->getPresenterClass($presenter);
866: } catch (InvalidPresenterException $e) {
867: throw new InvalidLinkException($e->getMessage());
868: }
869: }
870:
871:
872: if (isset($signal)) {
873: $reflection = new PresenterComponentReflection(get_class($component));
874: if ($signal === 'this') {
875: $signal = '';
876: if (array_key_exists(0, $args)) {
877: throw new InvalidLinkException("Unable to pass parameters to 'this!' signal.");
878: }
879:
880: } elseif (strpos($signal, self::NAME_SEPARATOR) === FALSE) {
881:
882: $method = $component->formatSignalMethod($signal);
883: if (!$reflection->hasCallableMethod($method)) {
884: throw new InvalidLinkException("Unknown signal '$signal', missing handler {$reflection->name}::$method()");
885: }
886: if ($args) {
887: self::argsToParams(get_class($component), $method, $args);
888: }
889: }
890:
891:
892: if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
893: $component->saveState($args);
894: }
895:
896: if ($args && $component !== $this) {
897: $prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
898: foreach ($args as $key => $val) {
899: unset($args[$key]);
900: $args[$prefix . $key] = $val;
901: }
902: }
903: }
904:
905:
906: if (is_subclass_of($presenterClass, __CLASS__)) {
907: if ($action === '') {
908: $action = self::DEFAULT_ACTION;
909: }
910:
911: $current = ($action === '*' || strcasecmp($action, $this->action) === 0) && $presenterClass === get_class($this);
912:
913: $reflection = new PresenterComponentReflection($presenterClass);
914: if ($args || $destination === 'this') {
915:
916: $method = call_user_func(array($presenterClass, 'formatActionMethod'), $action);
917: if (!$reflection->hasCallableMethod($method)) {
918: $method = call_user_func(array($presenterClass, 'formatRenderMethod'), $action);
919: if (!$reflection->hasCallableMethod($method)) {
920: $method = NULL;
921: }
922: }
923:
924:
925: if ($method === NULL) {
926: if (array_key_exists(0, $args)) {
927: throw new InvalidLinkException("Unable to pass parameters to action '$presenter:$action', missing corresponding method.");
928: }
929:
930: } elseif ($destination === 'this') {
931: self::argsToParams($presenterClass, $method, $args, $this->params);
932:
933: } else {
934: self::argsToParams($presenterClass, $method, $args);
935: }
936: }
937:
938:
939: if ($args && array_intersect_key($args, $reflection->getPersistentParams())) {
940: $this->saveState($args, $reflection);
941: }
942:
943: $globalState = $this->getGlobalState($destination === 'this' ? NULL : $presenterClass);
944: if ($current && $args) {
945: $tmp = $globalState + $this->params;
946: foreach ($args as $key => $val) {
947: if ((string) $val !== (isset($tmp[$key]) ? (string) $tmp[$key] : '')) {
948: $current = FALSE;
949: break;
950: }
951: }
952: }
953: $args += $globalState;
954: }
955:
956:
957: $args[self::ACTION_KEY] = $action;
958: if (!empty($signal)) {
959: $args[self::SIGNAL_KEY] = $component->getParameterId($signal);
960: $current = $current && $args[self::SIGNAL_KEY] === $this->getParameter(self::SIGNAL_KEY);
961: }
962: if (($mode === 'redirect' || $mode === 'forward') && $this->hasFlashSession()) {
963: $args[self::FLASH_KEY] = $this->getParameter(self::FLASH_KEY);
964: }
965:
966: $this->lastCreatedRequest = new PresenterRequest(
967: $presenter,
968: PresenterRequest::FORWARD,
969: $args,
970: array(),
971: array()
972: );
973: $this->lastCreatedRequestFlag = array('current' => $current);
974:
975: if ($mode === 'forward') {
976: return;
977: }
978:
979:
980: $url = $router->constructUrl($this->lastCreatedRequest, $refUrl);
981: if ($url === NULL) {
982: unset($args[self::ACTION_KEY]);
983: $params = urldecode(http_build_query($args, NULL, ', '));
984: throw new InvalidLinkException("No route for $presenter:$action($params)");
985: }
986:
987:
988: if ($mode === 'link' && $scheme === FALSE && !$this->absoluteUrls) {
989: $hostUrl = $refUrl->getHostUrl();
990: if (strncmp($url, $hostUrl, strlen($hostUrl)) === 0) {
991: $url = substr($url, strlen($hostUrl));
992: }
993: }
994:
995: return $url . $fragment;
996: }
997:
998:
999:
1000: 1001: 1002: 1003: 1004: 1005: 1006: 1007: 1008:
1009: private static function argsToParams($class, $method, & $args, $supplemental = array())
1010: {
1011: $i = 0;
1012: $rm = new ReflectionMethod($class, $method);
1013: foreach ($rm->getParameters() as $param) {
1014: $name = $param->getName();
1015: if (array_key_exists($i, $args)) {
1016: $args[$name] = $args[$i];
1017: unset($args[$i]);
1018: $i++;
1019:
1020: } elseif (array_key_exists($name, $args)) {
1021:
1022:
1023: } elseif (array_key_exists($name, $supplemental)) {
1024: $args[$name] = $supplemental[$name];
1025:
1026: } else {
1027: continue;
1028: }
1029:
1030:
1031: $def = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : NULL;
1032: $val = $args[$name];
1033: if ($val === NULL) {
1034: continue;
1035: } elseif ($param->isArray() || is_array($def)) {
1036: if (!is_array($val)) {
1037: throw new InvalidLinkException("Invalid value for parameter '$name', expected array.");
1038: }
1039: } elseif ($param->getClass() || is_object($val)) {
1040:
1041: } elseif (!is_scalar($val)) {
1042: throw new InvalidLinkException("Invalid value for parameter '$name', expected scalar.");
1043:
1044: } elseif ($def === NULL) {
1045: if ((string) $val === '') {
1046: $args[$name] = NULL;
1047: }
1048: continue;
1049: } else {
1050: settype($args[$name], gettype($def));
1051: if ((string) $args[$name] !== (string) $val) {
1052: throw new InvalidLinkException("Invalid value for parameter '$name', expected ".gettype($def).".");
1053: }
1054: }
1055:
1056: if ($args[$name] === $def) {
1057: $args[$name] = NULL;
1058: }
1059: }
1060:
1061: if (array_key_exists($i, $args)) {
1062: $method = $rm->getName();
1063: throw new InvalidLinkException("Passed more parameters than method $class::$method() expects.");
1064: }
1065: }
1066:
1067:
1068:
1069: 1070: 1071: 1072: 1073: 1074:
1075: protected function handleInvalidLink($e)
1076: {
1077: if ($this->invalidLinkMode === self::INVALID_LINK_SILENT) {
1078: return '#';
1079:
1080: } elseif ($this->invalidLinkMode === self::INVALID_LINK_WARNING) {
1081: return 'error: ' . $e->getMessage();
1082:
1083: } else {
1084: throw $e;
1085: }
1086: }
1087:
1088:
1089:
1090:
1091:
1092:
1093:
1094: 1095: 1096: 1097: 1098:
1099: public static function getPersistentComponents()
1100: {
1101: $arg = func_get_arg(0);
1102: return (array) ClassReflection::from($arg)
1103: ->getAnnotation('persistent');
1104: }
1105:
1106:
1107:
1108: 1109: 1110: 1111:
1112: private function getGlobalState($forClass = NULL)
1113: {
1114: $sinces = & $this->globalStateSinces;
1115:
1116: if ($this->globalState === NULL) {
1117: $state = array();
1118: foreach ($this->globalParams as $id => $params) {
1119: $prefix = $id . self::NAME_SEPARATOR;
1120: foreach ($params as $key => $val) {
1121: $state[$prefix . $key] = $val;
1122: }
1123: }
1124: $this->saveState($state, $forClass ? new PresenterComponentReflection($forClass) : NULL);
1125:
1126: if ($sinces === NULL) {
1127: $sinces = array();
1128: foreach ($this->getReflection()->getPersistentParams() as $nm => $meta) {
1129: $sinces[$nm] = $meta['since'];
1130: }
1131: }
1132:
1133: $components = $this->getReflection()->getPersistentComponents();
1134: $iterator = $this->getComponents(TRUE, 'IStatePersistent');
1135:
1136: foreach ($iterator as $name => $component) {
1137: if ($iterator->getDepth() === 0) {
1138:
1139: $since = isset($components[$name]['since']) ? $components[$name]['since'] : FALSE;
1140: }
1141: $prefix = $component->getUniqueId() . self::NAME_SEPARATOR;
1142: $params = array();
1143: $component->saveState($params);
1144: foreach ($params as $key => $val) {
1145: $state[$prefix . $key] = $val;
1146: $sinces[$prefix . $key] = $since;
1147: }
1148: }
1149:
1150: } else {
1151: $state = $this->globalState;
1152: }
1153:
1154: if ($forClass !== NULL) {
1155: $since = NULL;
1156: foreach ($state as $key => $foo) {
1157: if (!isset($sinces[$key])) {
1158: $x = strpos($key, self::NAME_SEPARATOR);
1159: $x = $x === FALSE ? $key : substr($key, 0, $x);
1160: $sinces[$key] = isset($sinces[$x]) ? $sinces[$x] : FALSE;
1161: }
1162: if ($since !== $sinces[$key]) {
1163: $since = $sinces[$key];
1164: $ok = $since && (is_subclass_of($forClass, $since) || $forClass === $since);
1165: }
1166: if (!$ok) {
1167: unset($state[$key]);
1168: }
1169: }
1170: }
1171:
1172: return $state;
1173: }
1174:
1175:
1176:
1177: 1178: 1179: 1180:
1181: protected function saveGlobalState()
1182: {
1183:
1184: foreach ($this->globalParams as $id => $foo) {
1185: $this->getComponent($id, FALSE);
1186: }
1187:
1188: $this->globalParams = array();
1189: $this->globalState = $this->getGlobalState();
1190: }
1191:
1192:
1193:
1194: 1195: 1196: 1197: 1198:
1199: private function initGlobalParameters()
1200: {
1201:
1202: $this->globalParams = array();
1203: $selfParams = array();
1204:
1205: $params = $this->request->getParameters();
1206: if ($this->isAjax()) {
1207: $params += $this->request->getPost();
1208: }
1209:
1210: foreach ($params as $key => $value) {
1211: $a = strlen($key) > 2 ? strrpos($key, self::NAME_SEPARATOR, -2) : FALSE;
1212: if (!$a) {
1213: $selfParams[$key] = $value;
1214: } else {
1215: $this->globalParams[substr($key, 0, $a)][substr($key, $a + 1)] = $value;
1216: }
1217: }
1218:
1219:
1220: $this->changeAction(isset($selfParams[self::ACTION_KEY]) ? $selfParams[self::ACTION_KEY] : self::DEFAULT_ACTION);
1221:
1222:
1223: $this->signalReceiver = $this->getUniqueId();
1224: if (isset($selfParams[self::SIGNAL_KEY])) {
1225: $param = $selfParams[self::SIGNAL_KEY];
1226: if (!is_string($param)) {
1227: $this->error('Signal name is not string.');
1228: }
1229: $pos = strrpos($param, '-');
1230: if ($pos) {
1231: $this->signalReceiver = substr($param, 0, $pos);
1232: $this->signal = substr($param, $pos + 1);
1233: } else {
1234: $this->signalReceiver = $this->getUniqueId();
1235: $this->signal = $param;
1236: }
1237: if ($this->signal == NULL) {
1238: $this->signal = NULL;
1239: }
1240: }
1241:
1242: $this->loadState($selfParams);
1243: }
1244:
1245:
1246:
1247: 1248: 1249: 1250: 1251:
1252: final public function popGlobalParameters($id)
1253: {
1254: if (isset($this->globalParams[$id])) {
1255: $res = $this->globalParams[$id];
1256: unset($this->globalParams[$id]);
1257: return $res;
1258:
1259: } else {
1260: return array();
1261: }
1262: }
1263:
1264:
1265:
1266:
1267:
1268:
1269:
1270: 1271: 1272: 1273:
1274: public function hasFlashSession()
1275: {
1276: return !empty($this->params[self::FLASH_KEY])
1277: && $this->getSession()->hasSection('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]);
1278: }
1279:
1280:
1281:
1282: 1283: 1284: 1285:
1286: public function getFlashSession()
1287: {
1288: if (empty($this->params[self::FLASH_KEY])) {
1289: $this->params[self::FLASH_KEY] = Strings::random(4);
1290: }
1291: return $this->getSession('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]);
1292: }
1293:
1294:
1295:
1296:
1297:
1298:
1299:
1300: 1301: 1302: 1303:
1304: final public function getContext()
1305: {
1306: return $this->context;
1307: }
1308:
1309:
1310:
1311: 1312: 1313: 1314: 1315:
1316: final public function getService($name)
1317: {
1318: return $this->context->getService($name);
1319: }
1320:
1321:
1322:
1323: 1324: 1325:
1326: protected function getHttpRequest()
1327: {
1328: return $this->context->getByType('IHttpRequest');
1329: }
1330:
1331:
1332:
1333: 1334: 1335:
1336: protected function getHttpResponse()
1337: {
1338: return $this->context->getByType('IHttpResponse');
1339: }
1340:
1341:
1342:
1343: 1344: 1345:
1346: protected function getHttpContext()
1347: {
1348: return $this->context->getByType('HttpContext');
1349: }
1350:
1351:
1352:
1353: 1354: 1355:
1356: public function getApplication()
1357: {
1358: return $this->context->getByType('Application');
1359: }
1360:
1361:
1362:
1363: 1364: 1365:
1366: public function getSession($namespace = NULL)
1367: {
1368: $handler = $this->context->getByType('Session');
1369: return $namespace === NULL ? $handler : $handler->getSection($namespace);
1370: }
1371:
1372:
1373:
1374: 1375: 1376:
1377: public function getUser()
1378: {
1379: return $this->context->getByType('User');
1380: }
1381:
1382: }
1383: