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