1 <?php
2 3 4 5 6 7
8
9 namespace Cross\MVC;
10
11 use Cross\Exception\CoreException;
12 use Cross\Lib\Document\HTML;
13 use Cross\Lib\Array2XML;
14 use Cross\Core\FrameBase;
15 use Cross\Core\Config;
16 use Cross\Core\Router;
17 use Cross\Core\Helper;
18 use Exception;
19
20 21 22 23 24
25 class View extends FrameBase
26 {
27 28 29 30 31
32 private $tpl_dir;
33
34 35 36 37 38
39 private $res_list;
40
41 42 43 44 45
46 private $tpl_base_path;
47
48 49 50 51 52
53 private $link_base = null;
54
55 56 57
58 private $wrap_stack = array();
59
60 61 62 63 64
65 protected $data;
66
67 68 69 70 71 72 73 74 75 76 77 78 79
80 protected $set = array(
81 'title' => '',
82 'keywords' => '',
83 'description' => '',
84
85 'layer' => 'default',
86 'load_layer' => true,
87 );
88
89 90 91 92 93
94 protected $tpl_file_suffix = '.tpl.php';
95
96 97 98 99 100
101 protected static $url_config_cache = array();
102
103 104 105 106 107 108 109
110 function display($data = null, $method = null)
111 {
112 $this->data = $data;
113 if ($method === null) {
114 $display_type = $this->config->get('sys', 'display');
115 if ($display_type && strcasecmp($display_type, 'html') !== 0) {
116 $this->set['load_layer'] = false;
117 $method = trim($display_type);
118 } else if ($this->action) {
119 $method = $this->action;
120 } else {
121 $method = Router::DEFAULT_ACTION;
122 }
123 }
124
125 $this->obRenderAction($data, $method);
126 }
127
128 129 130 131 132 133 134 135 136
137 function url($controller = null, $params = null, $encrypt_params = false)
138 {
139 static $link_base = null;
140 if ($link_base === null) {
141 $link_base = $this->getLinkBase();
142 }
143
144 if ($controller === null && $params === null) {
145 return $link_base;
146 } else {
147 $uri = $this->makeUri($this->getAppName(), false, $controller, $params, $encrypt_params);
148 return $link_base . $uri;
149 }
150 }
151
152 153 154 155 156 157 158
159 function sUrl($controller = null, $params = null)
160 {
161 return $this->url($controller, $params, true);
162 }
163
164 165 166 167 168 169 170 171
172 function a($content, $href = '', array $element_tags = array())
173 {
174 $element_tags['href'] = $href;
175 $element_tags['@content'] = $content;
176 return $this->wrapTag('a', $element_tags);
177 }
178
179 180 181 182 183 184 185
186 function img($src, array $element_tags = array())
187 {
188 $element_tags['src'] = $src;
189 $element_tags += array('border' => 0, 'alt' => 'image');
190 return $this->wrapTag('img', $element_tags);
191 }
192
193 194 195 196 197 198 199
200 function input($type, array $element_tags = array())
201 {
202 $element_tags['type'] = $type;
203 return $this->wrapTag('input', $element_tags);
204 }
205
206 207 208 209 210 211 212 213 214 215
216 function radio(array $data, $default_value = '', array $radio_tags = array(), array $label_tags = array())
217 {
218 $default_value = array($default_value => true);
219 return $this->buildRadioOrCheckbox('radio', $data, $default_value, $radio_tags, $label_tags);
220 }
221
222 223 224 225 226 227 228 229 230 231
232 function checkbox(array $data, $default_value = '', array $checkbox_tags = array(), array $label_tags = array())
233 {
234 if (is_array($default_value)) {
235 $default_value = array_flip($default_value);
236 } else {
237 $default_value = array($default_value => true);
238 }
239
240 return $this->buildRadioOrCheckbox('checkbox', $data, $default_value, $checkbox_tags, $label_tags);
241 }
242
243 244 245 246 247 248 249 250 251
252 function select(array $options_data, $default_value = null, array $select_params = array(), array $user_option_params = array())
253 {
254 $content = '';
255 if (is_array($default_value)) {
256 $default_value = array_flip($default_value);
257 } else {
258 $default_value = array($default_value => true);
259 }
260
261 foreach ($options_data as $value => $option) {
262 $option_params = array();
263 if (!empty($user_option_params)) {
264 $option_params = $user_option_params;
265 }
266
267 if (is_array($option)) {
268 $opt_content = '';
269 foreach ($option as $opt_value => $opt_option) {
270 unset($option_params['selected']);
271 $option_params['value'] = $opt_value;
272 $option_params['@content'] = $opt_option;
273 if (isset($default_value[$opt_value])) {
274 $option_params['selected'] = true;
275 }
276
277 $opt_content .= self::htmlTag('option', $option_params);
278 }
279
280 $opt_params['label'] = $value;
281 $opt_params['@content'] = $opt_content;
282 $content .= self::htmlTag('optgroup', $opt_params);
283 } else {
284 $option_params['value'] = $value;
285 $option_params['@content'] = $option;
286 if (isset($default_value[$value])) {
287 $option_params['selected'] = true;
288 }
289
290 $content .= self::htmlTag('option', $option_params);
291 }
292 }
293
294 $select_params['@content'] = $content;
295 return $this->wrapContent(self::htmlTag('select', $select_params), false);
296 }
297
298 299 300 301 302 303 304 305 306 307 308
309 function html($content, $encode = true)
310 {
311 return $this->wrapContent($content, $encode);
312 }
313
314 315 316 317 318 319 320
321 static function htmlTag($element, $element_tags = array())
322 {
323 return HTML::$element($element_tags);
324 }
325
326 327 328 329 330 331 332 333
334 function wrap($element, $element_tags = array(), $content_rear = false)
335 {
336 if (!is_array($element_tags)) {
337 $element_tags = array('@content' => $element_tags);
338 }
339
340 array_unshift($this->wrap_stack, array($element, $element_tags, $content_rear));
341 return $this;
342 }
343
344 345 346 347 348 349 350 351
352 function block($content, array $element_tags = array(), $element = 'div')
353 {
354 $element_tags['@content'] = $content;
355 return $this->wrapTag($element, $element_tags);
356 }
357
358 359 360 361 362 363 364 365 366
367 function section($tpl_name, array $tpl_data = array(), array $element_tags = array(), $element = 'div')
368 {
369 return $this->block($this->obRenderTpl($tpl_name, $tpl_data), $element_tags, $element);
370 }
371
372 373 374 375 376 377 378 379 380 381 382
383 function buildForm($tpl_name, array $form_tags = array(), array $tpl_data = array())
384 {
385 $content = $this->delegate->getClosureContainer()->run('buildForm', array($this));
386 $content .= $this->obRenderTpl($tpl_name, $tpl_data);
387
388 $form_tags += array('action' => '', 'method' => 'post');
389 $form_tags['@content'] = $content;
390
391 return $this->wrapTag('form', $form_tags);
392 }
393
394 395 396 397 398 399
400 function renderTpl($tpl_name, $data = array())
401 {
402 include $this->tpl($tpl_name);
403 }
404
405 406 407 408 409 410
411 function renderFile($file, $data = array())
412 {
413 include $file;
414 }
415
416 417 418 419 420 421 422 423
424 function obRenderTpl($tpl_name, array $data = array(), $encode = false)
425 {
426 ob_start();
427 $this->renderTpl($tpl_name, $data);
428 return $this->wrapContent(ob_get_clean(), $encode);
429 }
430
431 432 433 434 435 436 437 438
439 function obRenderFile($file, $data = array(), $encode = false)
440 {
441 ob_start();
442 $this->renderFile($file, $data);
443 return $this->wrapContent(ob_get_clean(), $encode);
444 }
445
446 447 448 449 450 451 452 453
454 function tpl($tpl_name, $get_content = false, $auto_append_suffix = true)
455 {
456 $file_path = $this->getTplPath() . $tpl_name;
457 if ($auto_append_suffix) {
458 $file_path .= $this->tpl_file_suffix;
459 }
460
461 if (true === $get_content) {
462 return file_get_contents($file_path, true);
463 }
464
465 return $file_path;
466 }
467
468 469 470 471 472
473 function JSON($data)
474 {
475 $this->set['load_layer'] = false;
476 $this->delegate->getResponse()->setContentType('json');
477 echo json_encode($data);
478 }
479
480 481 482 483 484 485 486
487 function XML($data, $root_name = 'root')
488 {
489 $this->set['load_layer'] = false;
490 $this->delegate->getResponse()->setContentType('xml');
491 echo Array2XML::createXML($root_name, $data)->saveXML();
492 }
493
494 495 496 497 498 499 500 501
502 function e(array $data, $key, $default_value = '')
503 {
504 if (isset($data[$key])) {
505 return $data[$key];
506 }
507
508 return $default_value;
509 }
510
511 512 513 514 515 516 517 518 519 520 521
522 function ee(array $data, $key, $default_value = '')
523 {
524 if (!empty($data[$key])) {
525 return $data[$key];
526 }
527
528 return $default_value;
529 }
530
531 532 533 534 535 536 537
538 final public function set($name, $value = null)
539 {
540 if (is_array($name)) {
541 $this->set = array_merge($this->set, $name);
542 } else {
543 $this->set[$name] = $value;
544 }
545
546 return $this;
547 }
548
549 550 551 552 553 554 555
556 function res($res_url, $use_static_url = true)
557 {
558 static $res_base_url = null;
559 if (!isset($res_base_url[$use_static_url])) {
560 if ($use_static_url) {
561 $base_url = $this->config->get('static', 'url');
562 } else {
563 $base_url = $this->config->get('url', 'full_request');
564 }
565
566 $res_base_url[$use_static_url] = rtrim($base_url, '/') . '/';
567 }
568
569 return $res_base_url[$use_static_url] . $res_url;
570 }
571
572 573 574 575 576 577 578
579 function relRes($res_url, $res_dir = 'static')
580 {
581 static $res_base_url = null;
582 if (null === $res_base_url) {
583 $res_base_url = rtrim($this->config->get('url', 'request'), '/') . '/' . $res_dir . '/';
584 }
585
586 return $res_base_url . $res_url;
587 }
588
589 590 591 592 593 594 595
596 function link($controller = null, $params = null)
597 {
598 return $this->url($controller, $params);
599 }
600
601 602 603 604 605 606 607
608 function slink($controller = null, $params = null)
609 {
610 return $this->url($controller, $params, true);
611 }
612
613 614 615 616 617 618 619 620 621 622 623
624 function appUrl($base_link, $app_name, $controller = null, $params = null, $encrypt_params = null)
625 {
626 $base_link = rtrim($base_link, '/') . '/';
627 if ($controller === null && $params === null) {
628 return $base_link;
629 } else {
630 $uri = $this->makeUri($app_name, true, $controller, $params, $encrypt_params);
631 return $base_link . $uri;
632 }
633 }
634
635 636 637
638 function cleanLinkCache()
639 {
640 unset(self::$url_config_cache[$this->getAppName()]);
641 }
642
643 644 645 646 647
648 function setTplDir($dir_name)
649 {
650 $this->tpl_dir = $dir_name;
651 }
652
653 654 655 656 657
658 function getLinkBase()
659 {
660 if (null === $this->link_base) {
661 $this->setLinkBase($this->config->get('url', 'full_request'));
662 }
663
664 return $this->link_base;
665 }
666
667 668 669 670 671
672 function getAppName()
673 {
674 static $app_name = null;
675 if ($app_name === null) {
676 $app_name = $this->config->get('app', 'name');
677 }
678
679 return $app_name;
680 }
681
682 683 684 685 686
687 function setLinkBase($link_base)
688 {
689 $this->link_base = rtrim($link_base, '/') . '/';
690 }
691
692 693 694 695 696
697 function getTplPath()
698 {
699 static $tpl_path;
700 $app_name = $this->getAppName();
701 if (!isset($tpl_path[$app_name])) {
702 $tpl_path[$app_name] = $this->getTplBasePath() . $this->getTplDir() . DIRECTORY_SEPARATOR;
703 }
704
705 return $tpl_path[$app_name];
706 }
707
708 709 710 711 712
713 function setTplBasePath($tpl_base_path)
714 {
715 $this->tpl_base_path = rtrim($tpl_base_path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
716 }
717
718 719 720 721 722
723 function getTplBasePath()
724 {
725 if (!$this->tpl_base_path) {
726 $this->setTplBasePath($this->config->get('app', 'path') . 'templates' . DIRECTORY_SEPARATOR);
727 }
728
729 return $this->tpl_base_path;
730 }
731
732 733 734 735 736
737 function getTplDir()
738 {
739 if (!$this->tpl_dir) {
740 $default_tpl_dir = $this->config->get('sys', 'default_tpl_dir');
741 if (!$default_tpl_dir) {
742 $default_tpl_dir = 'default';
743 }
744 $this->setTplDir($default_tpl_dir);
745 }
746
747 return $this->tpl_dir;
748 }
749
750 751 752 753 754 755 756
757 function addRes($res_url, $location = 'header', $convert = true)
758 {
759 $this->res_list[$location][] = array(
760 'url' => $res_url,
761 'convert' => $convert
762 );
763 }
764
765 766 767 768 769 770
771 function loadRes($location = 'header')
772 {
773 $result = '';
774 if (empty($this->res_list) || empty($this->res_list[$location])) {
775 return $result;
776 }
777
778 if (isset($this->res_list[$location]) && !empty($this->res_list[$location])) {
779 $data = $this->res_list[$location];
780 }
781
782 if (!empty($data)) {
783 if (is_array($data)) {
784 foreach ($data as $r) {
785 $result .= $this->outputResLink($r['url'], $r['convert']);
786 }
787 } else {
788 $result .= $this->outputResLink($data);
789 }
790 }
791
792 return $result;
793 }
794
795 796 797 798 799 800 801 802 803 804 805
806 protected function makeUri($app_name, $check_app_name, $controller = null, $params = null, $encrypt_params = null)
807 {
808 $uri = '';
809 $enable_controller_cache = false;
810
811 if (!isset(self::$url_config_cache[$app_name])) {
812 $this_app_name = $app_name;
813 if ($check_app_name) {
814 $this_app_name = $this->getAppName();
815 }
816
817 if ($check_app_name && $app_name != $this_app_name) {
818 $config = Config::load(APP_PATH_DIR . $app_name . DIRECTORY_SEPARATOR . 'init.php');
819 } else {
820 $config = $this->config;
821 }
822
823 $url_router_config = array(
824 'url' => $config->get('url'),
825 'router' => $config->get('router')
826 );
827 self::$url_config_cache[$app_name] = $url_router_config;
828 } else {
829 $enable_controller_cache = true;
830 $url_router_config = self::$url_config_cache[$app_name];
831 }
832
833 $url_params = '';
834 $has_controller_string = true;
835 $url_controller = $this->makeControllerUri($app_name, $enable_controller_cache, $controller, $url_router_config);
836 if ($url_controller === '') {
837 $has_controller_string = false;
838 }
839
840 $url_config = &$url_router_config['url'];
841 if (!empty($params)) {
842 $url_params = $this->makeParams($params, $url_config, $encrypt_params, $has_controller_string);
843 }
844
845 if (!empty($url_config['ext'])) {
846 switch ($url_config['type']) {
847 case 2:
848 if ($has_controller_string) {
849 $uri .= $url_controller . $url_config['ext'];
850 }
851
852 $uri .= $url_params;
853 break;
854 case 1:
855 case 3:
856 case 4:
857 case 5:
858 $uri .= $url_controller . $url_params . $url_config['ext'];
859 break;
860 }
861 } else {
862 $uri .= $url_controller . $url_params;
863 }
864
865 return $uri;
866 }
867
868 869 870 871 872 873 874 875 876 877
878 protected function makeControllerUri($app_name, $use_cache, $controller, array $url_config)
879 {
880 static $path_cache;
881 if (isset($path_cache[$app_name][$controller]) && $use_cache) {
882 return $path_cache[$app_name][$controller];
883 }
884
885 $app_alias_config = $this->parseControllerAlias($app_name, $url_config['router']);
886 if (isset($app_alias_config[$controller])) {
887 $real_controller = $app_alias_config[$controller];
888 } else {
889 $real_controller = $controller;
890 }
891
892 $action_name = null;
893 if (false !== strpos($real_controller, ':')) {
894 list($controller_name, $action_name) = explode(':', $real_controller);
895 } else {
896 $controller_name = $real_controller;
897 }
898
899 $url = &$url_config['url'];
900 $index = $this->makeIndex($url, true);
901 $controller_path = $index . $controller_name;
902
903 if (null !== $action_name) {
904 $controller_path .= $url['dot'] . $action_name;
905 }
906
907 $path_cache[$app_name][$controller] = $controller_path;
908 return $controller_path;
909 }
910
911 912 913 914 915 916 917 918
919 protected function makeIndex(array $url_config, $have_controller = false)
920 {
921 static $cache = array();
922 if (isset($cache[$have_controller])) {
923 return $cache[$have_controller];
924 }
925
926 $index = $url_config['index'];
927 $is_default_index = (0 === strcasecmp($index, 'index.php'));
928
929 switch ($url_config['type']) {
930 case 1:
931 case 3:
932 $index_dot = '?';
933 $addition_dot = '/';
934 if ($is_default_index) {
935 $index = '';
936 }
937 break;
938
939 case 2:
940 case 4:
941 case 5:
942 $index_dot = '/';
943 $addition_dot = '';
944 break;
945
946 default:
947 throw new CoreException('Type does not support!');
948 }
949
950 if ($url_config['rewrite']) {
951 $index = $index_dot = $addition_dot = '';
952 }
953
954 $virtual_path = &$url_config['virtual_path'];
955 if ($have_controller) {
956 $index .= $index_dot . $addition_dot;
957 if ($virtual_path) {
958 $index .= $virtual_path . $url_config['dot'];
959 }
960 } else {
961 if ($is_default_index) {
962 $index = '';
963 }
964
965 if ($virtual_path) {
966 $index .= $index_dot . $addition_dot . $virtual_path;
967 if ($url_config['ext']) {
968 $index .= $url_config['ext'];
969 }
970 }
971 }
972
973 $cache[$have_controller] = $index;
974 return $index;
975 }
976
977 978 979 980 981 982 983 984 985
986 protected function makeParams(array $params, array $url_config, $encrypt_params = false, $add_prefix_dot = true)
987 {
988 $url_dot = &$url_config['dot'];
989 $params_dot = &$url_config['params_dot'];
990 if ($params_dot) {
991 $dot = $params_dot;
992 } else {
993 $dot = $url_dot;
994 }
995
996 $url_params = '';
997 if ($params) {
998 switch ($url_config['type']) {
999 case 1:
1000 case 5:
1001 $url_params = implode($dot, $params);
1002 break;
1003
1004 case 2:
1005 $url_dot = '?';
1006 $add_prefix_dot = true;
1007 $url_params = http_build_query($params);
1008 break;
1009
1010 case 3:
1011 case 4:
1012 foreach ($params as $p_key => $p_val) {
1013 $url_params .= $p_key . $dot . $p_val . $dot;
1014 }
1015 $url_params = rtrim($url_params, $dot);
1016 break;
1017 }
1018
1019 if (true === $encrypt_params) {
1020 $url_params = $this->urlEncrypt($url_params);
1021 }
1022 }
1023
1024 if ($add_prefix_dot) {
1025 return $url_dot . $url_params;
1026 }
1027
1028 return $url_params;
1029 }
1030
1031 1032 1033 1034 1035 1036 1037
1038 protected function outputResLink($res_link, $make_link = true)
1039 {
1040 $t = Helper::getExt($res_link);
1041 switch (strtolower($t)) {
1042 case 'js' :
1043 $tpl = '<script type="text/javascript" src="%s"></script>';
1044 break;
1045
1046 case 'css' :
1047 $tpl = '<link rel="stylesheet" type="text/css" href="%s"/>';
1048 break;
1049
1050 default :
1051 $tpl = null;
1052 }
1053
1054 if (null !== $tpl) {
1055 if ($make_link) {
1056 $res_link = $this->res($res_link);
1057 }
1058
1059 return sprintf("{$tpl}\n", $res_link);
1060 }
1061
1062 return null;
1063 }
1064
1065 1066 1067 1068 1069 1070 1071
1072 protected function loadLayer($content, $layer_ext = '.layer.php')
1073 {
1074 $layer_file = $this->getTplPath() . $this->set['layer'] . $layer_ext;
1075 if (!is_file($layer_file)) {
1076 throw new CoreException($layer_file . ' layer Not found!');
1077 }
1078
1079 extract($this->set, EXTR_PREFIX_SAME, 'USER_DEFINED');
1080 include $layer_file;
1081 }
1082
1083 1084 1085 1086 1087 1088 1089 1090
1091 private function obRenderAction($data, $method)
1092 {
1093 ob_start();
1094 $this->$method($data);
1095 $content = ob_get_clean();
1096
1097 if ($this->set['load_layer']) {
1098 $this->loadLayer($content);
1099 } else {
1100 echo $content;
1101 }
1102 }
1103
1104 1105 1106 1107 1108 1109 1110
1111 private function parseControllerAlias($app_name, array $router)
1112 {
1113 static $router_alias_cache;
1114 if (!isset($router_alias_cache[$app_name])) {
1115 $router_alias_cache[$app_name] = array();
1116 if (!empty($router)) {
1117 foreach ($router as $controller_alias => $alias_config) {
1118 $router_alias_cache[$app_name][$alias_config] = $controller_alias;
1119 }
1120 }
1121 }
1122
1123 return $router_alias_cache[$app_name];
1124 }
1125
1126 1127 1128 1129 1130 1131 1132 1133 1134 1135
1136 private function buildRadioOrCheckbox($type, array $data, array $default_value = array(), array $input_tags = array(), array $label_tags = array())
1137 {
1138 $content = '';
1139 foreach ($data as $value => $label_text) {
1140 $build_input_tags = array();
1141 if (!empty($input_tags)) {
1142 $build_input_tags = $input_tags;
1143 }
1144
1145 $build_input_tags['type'] = $type;
1146 $build_input_tags['value'] = $value;
1147 if (isset($default_value[$value])) {
1148 $build_input_tags['checked'] = true;
1149 }
1150
1151 $label_tags['@content'] = self::htmlTag('input', $build_input_tags) . $label_text;
1152 $content .= self::htmlTag('label', $label_tags);
1153 }
1154
1155 return $this->wrapContent($content, false);
1156 }
1157
1158 1159 1160 1161 1162 1163
1164 private function buildWrapTags(array &$wrap_tags)
1165 {
1166 $i = 0;
1167 $wrap_content = '';
1168 $stack_size = count($wrap_tags) - 1;
1169 while ($wrap_config = array_shift($wrap_tags)) {
1170 list($wrap_element, $wrap_element_tags, $after) = $wrap_config;
1171 if ($wrap_content === '') {
1172 $wrap_content = HTML::$wrap_element($wrap_element_tags);
1173 } else {
1174 if (isset($wrap_element_tags['@content']) && $after) {
1175 $wrap_element_tags['@content'] = $wrap_content . $wrap_element_tags['@content'];
1176 } else if (isset($wrap_element_tags['@content'])) {
1177 $wrap_element_tags['@content'] .= $wrap_content;
1178 } else {
1179 $wrap_element_tags['@content'] = $wrap_content;
1180 }
1181
1182 if ($i == $stack_size) {
1183 return self::htmlTag($wrap_element, $wrap_element_tags);
1184 } else {
1185 $wrap_content = HTML::$wrap_element($wrap_element_tags);
1186 }
1187 }
1188 $i++;
1189 }
1190
1191 return $wrap_content;
1192 }
1193
1194 1195 1196 1197 1198 1199 1200
1201 private function wrapContent($content, $encode = true)
1202 {
1203 if (!empty($this->wrap_stack)) {
1204 $stack_top = &$this->wrap_stack[0];
1205 if (isset($stack_top[1]['@content']) && $stack_top[2]) {
1206 $content .= $stack_top[1]['@content'];
1207 } else if (isset($stack_top[1]['@content'])) {
1208 $content = $stack_top[1]['@content'] . $content;
1209 }
1210
1211 $stack_top[1]['@content'] = $content;
1212 return $this->buildWrapTags($this->wrap_stack);
1213 } else if ($encode) {
1214 return htmlentities($content, ENT_IGNORE);
1215 } else {
1216 return $content;
1217 }
1218 }
1219
1220 1221 1222 1223 1224 1225 1226
1227 private function wrapTag($element, $element_tags = array())
1228 {
1229 if (!empty($this->wrap_stack)) {
1230 $this->wrap($element, $element_tags);
1231 return $this->buildWrapTags($this->wrap_stack);
1232 }
1233
1234 return self::htmlTag($element, $element_tags);
1235 }
1236 }
1237