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