/
Controller.php
1130 lines (970 loc) · 32 KB
/
Controller.php
1
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?php
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 这个文件是 JeCat PHP框架的一部分,该项目和此文件 均遵循 GNU 自由软件协议
//
// Copyleft 2008-2012 JeCat.cn(http://team.JeCat.cn)
//
//
// JeCat PHP框架 的正式全名是:Jellicle Cat PHP Framework。
// “Jellicle Cat”出自 Andrew Lloyd Webber的音乐剧《猫》(《Prologue:Jellicle Songs for Jellicle Cats》)。
// JeCat 是一个开源项目,它像音乐剧中的猫一样自由,你可以毫无顾忌地使用JCAT PHP框架。JCAT 由中国团队开发维护。
// 正在使用的这个版本是:0.7.1
//
//
//
// 相关的链接:
// [主页] http://www.JeCat.cn
// [源代码] https://github.com/JeCat/framework
// [下载(http)] https://nodeload.github.com/JeCat/framework/zipball/master
// [下载(git)] git clone git://github.com/JeCat/framework.git jecat
// 不很相关:
// [MP3] http://www.google.com/search?q=jellicle+songs+for+jellicle+cats+Andrew+Lloyd+Webber
// [VCD/DVD] http://www.google.com/search?q=CAT+Andrew+Lloyd+Webber+video
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*-- Project Introduce --*/
namespace org\jecat\framework\mvc\controller ;
use org\jecat\framework\util\EventManager;
use org\jecat\framework\auth\IdManager;
use org\jecat\framework\auth\AuthenticationException;
use org\jecat\framework\auth\Authorizer;
use org\jecat\framework\io\IOutputStream;
use org\jecat\framework\mvc\view\View;
use org\jecat\framework\mvc\controller\Response;
use org\jecat\framework\bean\BeanConfException;
use org\jecat\framework\bean\BeanFactory;
use org\jecat\framework\bean\IBean;
use org\jecat\framework\mvc\model\IModel;
use org\jecat\framework\pattern\composite\Container;
use org\jecat\framework\mvc\view\DataExchanger;
use org\jecat\framework\mvc\view\IFormView;
use org\jecat\framework\util\match\RegExp;
use org\jecat\framework\mvc\model\db\orm\Prototype;
use org\jecat\framework\mvc\model\db\orm\PrototypeAssociationMap;
use org\jecat\framework\message\IMessageQueue;
use org\jecat\framework\message\MessageQueue;
use org\jecat\framework\util\DataSrc;
use org\jecat\framework\util\IDataSrc;
use org\jecat\framework\lang\Exception;
use org\jecat\framework\mvc\view\IView;
use org\jecat\framework\pattern\composite\NamableComposite;
/**
*
* @wiki /MVC模式
*
* ===控制器(Controller)===
* 在JeCat中,一个控制器完成一项工作(例如显示一个网页)。多个控制器可以组合起来,几项简单的工作可以组合成一项更复杂的工作。
* JeCat的控制器可以自由组合,控制器之间互不干扰、各自独立工作,又可以彼此配合,浑然一体。
*
* ===视图(View)===
* 视图负责系统的用户界面,在Web开发时,一个视图负责网页上的一个“区域”;
* 一个控制器可以提供多个视图(一个网页常常可以由多个“区块”组成),视图之间可以进行任意位置的布局。
* 当多个控制器组合成一个控制器时,系统会将所有控制器的视图都“堆放”在一起;然后你可以自由地布置这些视图,而无须关心他们的来源。
*
* ==视图窗体(View Widget)==
* 视图窗体是网页上可以重用的“构件”,他们通常有较复杂的行为机制,可是需要在不同网页上(或不同视图中)重复出现。\
* 这样的“构件”通常被封装创意个视图窗体以便于重用。例如常见的文本输入框(连同这个输入框上的用户输入有效性检查等工作)、菜单等。
* 封装的好处是:可以容易地重用,避免反复实现相同(或相似)的功能;同时,这些窗体还可以在以后被替换。
*
* =表单窗体(Form Widget)=
* 表单窗体控件是用于表单的视图窗体,用户可以在这些窗体控件中输入数据,系统可以为这些窗体控件添加数据校验器;并且通过[b]数据交换[/b]可以将模型中的数据复制到窗体控件中,或是将窗体控件中的数据复制到模型中。
*
*
* ===模型(Model)===
* 模型负责维护数据,不同类型的模型对数据进行不同方式的存储和载入,在Web开发中,最常用的模型是关系型数据库模型。
* 一个控制器可以提供多个模型。模型和视图之间可以建立“关联”。一个模型可以被多个视图关联,但是一个视图只能关联一个模型。他们之间是“观察者”模式。视图是观察者,模型是观察目标。
*
* ==数据交换(Data Exchage)==
* JeCat 提供一种机制,用于模型和视图窗体之间的数据自动交换。
* 。。。
*/
class Controller extends NamableComposite implements IBean
{
const afterMainRun = 'afterMainRun' ;
function __construct ($params=null,$sName=null,$bBuildAtonce=true)
{
$this->setName($sName) ;
parent::__construct("org\\jecat\\framework\\mvc\\controller\\Controller") ;
$this->buildParams($params) ;
// auto build bean config
if( $bBuildAtonce and $arrConfig = $this->createBeanConfig() )
{
$this->buildBean($arrConfig) ;
}
$this->init() ;
}
public function name()
{
if(($sName=parent::name())===null)
{
$sName = get_class($this) ;
if( ($nLastSlashPos=strrpos($sName,"\\"))!==false )
{
$sName = substr($sName,$nLastSlashPos+1) ;
}
parent::setName($sName) ;
}
return $sName;
}
protected function init()
{}
public function createBeanConfig()
{
return null ;
}
/**
* @example /Bean/合并Bean配置
* @forwiki /Bean/合并Bean配置
*/
static public function createBean(array & $arrConfig,$sNamespace='*',$bBuildAtOnce,\org\jecat\framework\bean\BeanFactory $aBeanFactory=null)
{
$sClass = get_called_class() ;
$aBean = new $sClass(null,null,false) ;
// 将传入的 bean 配置 和 controller 提供的默认bean配置合并
if( $arrDefaultConfig = $aBean->createBeanConfig() )
{
BeanFactory::mergeConfig($arrDefaultConfig,$arrConfig) ;
$arrConfig = $arrDefaultConfig ;
}
if($bBuildAtOnce)
{
$aBean->buildBean($arrConfig,$sNamespace,$aBeanFactory) ;
}
return $aBean ;
}
/**
* @wiki /MVC模式/控制器/控制器的Bean配置数组
* ==Bean配置数组==
* {|
* !属性
* !
* !格式
* !说明
* |--- ---
* |name
* |可选
* |string
* |控制器的名称
* |--- ---
* |params
* |可选
* |array,DataSrc
* |控制器执行的参数
* |--- ---
* |title
* |可选
* |string
* |控制器的标题,做为网页执行时,用于网页<head> 中的 <title>
* |--- ---
* |description
* |可选
* |string
* |字符串格式,控制器功能的描述文本,做为网页执行时,用于网页<head>中的 <meta:description>
* |--- ---
* |keywords
* |可选
* |string
* |控制器的关键词,做为网页执行时,用于网页<head>中的 <meta:keywords>
* |--- ---
* |model:ooxx
* |可选
* |bean config
* |一个model的配置数组,”ooxx“为model的名称(model配置数组中的 name 属性可以省略)
* |--- ---
* | models
* |可选
* |bean config array
* |多个model配置数组的集合,集合(数组)中的每个元素都是一个model的配置数组;元素的键名可以做为model的名称(对应的model配置数组可以省略name属性)
* |--- ---
* |view:ooxx
* |可选
* |bean config
* |一个视图的配置数组,”ooxx“为视图的名称(视图配置数组中的 name 属性可以省略)
* |--- ---
* |views
* |可选
* |bean config array
* |多个视图配置数组的集合,集合(数组)中的每个元素都是一个视图的配置数组;元素的键名可以做为视图的名称(对应的视图配置数组可以省略name属性)
* |--- ---
* |controller:ooxx
* |可选
* |bean config
* |一个子控制器的配置数组,”ooxx“为子控制器的名称(子控制器配置数组中的 name 属性可以省略)
* |--- ---
* |controllers
* |可选
* |bean config array
* |多个子控制器配置数组的集合,集合(数组)中的每个元素都是一个子控制器的配置数组;元素的键名可以做为子控制器的名称(对应的子控制器配置数组可以省略name属性)
* |--- ---
* |props
* |可选
* |array
* |控制器的属性
* |--- ---
* |frame
* |可选
* |array
* |frame控制器配置,指定控制器的frame,jecat默认情况下有自己的默认frame。
* |--- ---
* |process
* |可选
* |callback
* |一个函数,用来替代 process() 方法。仅在Controller子类没有覆盖父类的process()的情况下有效。在执行 process 回调函数时,所属的Controller对像会作为第一个参数传给回调函数。
* |}
*/
public function buildBean(array & $arrConfig,$sNamespace='*',\org\jecat\framework\bean\BeanFactory $aBeanFactory=null)
{
if( isset($arrConfig['name']) )
{
$this->setName($arrConfig['name']) ;
}
if( isset($arrConfig['params']) )
{
$this->buildParams($arrConfig['params']) ;
}
if( !empty($arrConfig['title']) )
{
$this->setTitle($arrConfig['title']) ;
}
if( !empty($arrConfig['description']) )
{
$this->setDescription($arrConfig['description']) ;
}
if( !empty($arrConfig['keywords']) )
{
$this->setKeywords($arrConfig['keywords']) ;
}
if( isset($arrConfig['param.exclude']) ){
if(!$this->params)
{
$this->params = new DataSrc() ;
}
$this->params->setExclude($arrConfig['param.exclude'] ) ;
}
$aBeanFactory = BeanFactory::singleton() ;
// 将 model:xxxx 转换成 models[] 结构
$aBeanFactory->_typeKeyStruct($arrConfig,array(
'model:'=>'models' ,
'view:'=>'views' ,
'controller:'=>'controllers' ,
)) ;
// model=>models(model), view=>views(view)
if( !empty($arrConfig['model']) and is_array($arrConfig['model']) and empty($arrConfig['models']['model']) )
{
$arrConfig['models']['model'] =& $arrConfig['model'] ;
}
if( array_key_exists('view',$arrConfig) and is_array($arrConfig['view']) )
{
$arrConfig['views']['view'] =& $arrConfig['view'] ;
}
// models --------------------
$aModelContainer = $this->modelContainer() ;
if( !empty($arrConfig['models']) )
{
foreach($arrConfig['models'] as $key=>&$arrBeanConf)
{
// 自动配置缺少的 class, name 属性
$aBeanFactory->_typeProperties( $arrBeanConf, 'model', is_int($key)?null:$key, 'name' ) ;
$aBean = $aBeanFactory->createBean($arrBeanConf,$sNamespace,true) ;
$aModelContainer->add( $aBean, $aBean->name() ) ;
}
}
// views --------------------
if( !empty($arrConfig['views']) )
{
foreach($arrConfig['views'] as $key=>&$arrBeanConf)
{
// 自动配置缺少的 class, name 属性
$aBeanFactory->_typeProperties( $arrBeanConf, 'view', is_int($key)?null:$key, 'name' ) ;
// 创建对象
$aBean = $aBeanFactory->createBean($arrBeanConf,$sNamespace,false) ;
$aBean->setName($arrBeanConf['name']) ;
$this->addView( $aBean ) ;
$aBean->buildBean($arrBeanConf,$sNamespace) ;
if(!empty($arrBeanConf['model']))
{
if( !$aModel=$aModelContainer->getByName($arrBeanConf['model']) )
{
throw new BeanConfException("视图(%s)的Bean配置属性 model 无效,没有指定的模型:%s",array($aBean->name(),$arrBeanConf['model'])) ;
}
$aBean->setModel($aModel) ;
}
}
}
// controllers --------------------
if( !empty($arrConfig['controllers']) )
{
foreach($arrConfig['controllers'] as $key=>&$arrBeanConf)
{
// 自动配置缺少的 class, name 属性
$aBeanFactory->_typeProperties( $arrBeanConf, 'controller', is_int($key)?null:$key, 'name' ) ;
$this->add( $aBeanFactory->createBean($arrBeanConf,$sNamespace,true) ) ;
}
}
// properties --------------------
if( !empty($arrConfig['props']) )
{
$aProperties = $this->properties() ;
foreach($arrConfig['props'] as $key=>&$value)
{
$aProperties->set($key,$value) ;
}
}
// authorizer --------------------
if( !empty($arrConfig['perms']) )
{
$arrAuthorConf = array(
'class' => 'authorizer' ,
'perms' => &$arrConfig['perms'] ,
) ;
$this->setAuthorizer( $aBeanFactory->createBean($arrAuthorConf,$sNamespace) ) ;
}
if( isset($arrConfig['perms.autocheck']) )
{
$this->bAutoCheckPermissions = $arrConfig['perms.autocheck']? true: false ;
}
// process
if( !empty($arrConfig['process']) )
{
$this->fnProcess = $arrConfig['process'] ;
}
$this->arrBeanConfig = $arrConfig ;
}
public function beanConfig()
{
return $this->arrBeanConfig ;
}
public function createModel($prototype,array $arrProperties=array(),$bAgg=false,$sName=null,$sClass='org\\jecat\\framework\\mvc\\model\\db\\Model')
{
if( $prototype instanceof Prototype )
{
$aPrototype = $prototype ;
}
else
{
$aPrototype = PrototypeAssociationMap::singleton()->fragment($prototype,$arrProperties) ;
}
if(!$sName)
{
$sName = $aPrototype->name() ;
$aResSet=self::regexpModelName()->match($sName) ;
for( $aResSet->end(); $aRes=$aResSet->current(); $aResSet->prev() )
{
// 删除单词分隔符
$sName = substr_replace($sName,'',$aRes->position(),1) ;
}
}
$sName = strtolower($sName) ;
return $this->addModel(new $sClass($aPrototype,$bAgg),$sName) ;
}
/**
* @param $formview 该参数可以为:true 则创建一个 FormView 类型的视图; false 则创建一个 View 普通视图; 或是其他视图的类名
* @return IView
*/
public function createView($sName=null,$sSourceFile=null,$formview='org\\jecat\\framework\\mvc\\view\\View')
{
if( is_string($formview) and class_exists($formview) )
{
$sClass = $formview ;
}
else
{
$sClass = $formview? 'org\\jecat\\framework\\mvc\\view\\FormView': 'org\\jecat\\framework\\mvc\\view\\View' ;
}
if( !$sName )
{
$sName = $this->name() ;
}
if( !$sSourceFile )
{
$sSourceFile = $sName . '.html' ;
}
$aView = new $sClass($sName,$sSourceFile) ;
$this->addView($aView,$sName) ;
return $aView ;
}
public function createFormView($sName=null,$sSourceFile=null)
{
return $this->createView($sName,$sSourceFile,'org\\jecat\\framework\\mvc\\view\\FormView') ;
}
/**
* @wiki /MVC模式/控制器/主视图(mainView)
*
* 每个控制器都有一个”隐藏“的主视图(main view),控制器所拥有的视图,实际上都存放在这个主视图里([see /MVC模式/视图/视图的组合模式]),它是管理控制器所有视图的”容器“。
*
* 把一个控制器B做为”子控制器“添加给另一个控制器A的时候,B的主视图,会自动成为A的一个普通视图。这样一来,当控制器组合到一起的时候,他们的视图也自动完成了组合。
*
* [s]主视图是一个特殊的视图类 org\jecat\framework\mvc\view\TransparentViewContainer,它在视图的组合结构中,是透明存在的。[/s]
*
* @return IView
*/
public function mainView()
{
if( !$this->aMainView )
{
$this->setMainView( new View($this->name(),null) ) ;
}
return $this->aMainView ;
}
public function setMainView(IView $aView)
{
if($this->aMainView)
{
$this->messageQueue()->removeChildHolder($this->aMainView) ;
}
$this->messageQueue()->addChildHolder($aView) ;
$this->aMainView = $aView ;
$aView->setController($this) ;
}
/**
* @wiki /MVC模式/控制器/控制器执行
*
* 控制器的执行入口是 mainRun() 方法,在你写一个控制器类的时候,应该将控制器的执行过程写在 process() 函数里,由mainRun()调用你的process()函数,而不是直接重写mainRun()。
* process()是控制器自己的业务逻辑,mainRun()包含了很多系统级的
*
* @see Controller::mainRun()
*/
public function mainRun ()
{
self::processController($this) ;
// 处理 frame
// (先执行自己,后执行 frame)
if( $aFrame=$this->frame() )
{
self::processController($aFrame) ;
}
$this->response()->respond($this) ;
// 触发事件
EventManager::singleton()->emitEvent(__CLASS__,self::afterMainRun, array($this)) ;
}
static protected function processController(Controller $aController)
{
// 执行子控制器
foreach($aController->iterator() as $aChild)
{
self::processController($aChild) ;
}
// 重定向输出
if( $aController->bCatchOutput )
{
ob_start( array($aController->response(),'write') ) ;
}
// 执行自己
try{
// 检查权限
if( $aController->autoCheckPerms() )
{
$aController->checkPermissions() ;
}
$aController->process() ;
}
catch(_ExceptionRelocation $aRelocation)
{}
catch(\Exception $e)
{}
if( $aController->bCatchOutput )
{
ob_end_flush() ;
}
if(!empty($e))
{
throw $e ;
}
}
public function location($sUrl,$nFlashSec=3)
{
// 禁用所有视图
foreach( $this->mainView()->iterator() as $aView )
{
$aView->disable() ;
}
// 建立 relocation 视图
$aViewRelocater = new View("Relocater", "org.jecat.framework:Relocater.html") ;
$this->addView($aViewRelocater) ;
$aViewRelocater->variables()->set('flashSec',$nFlashSec) ;
$aViewRelocater->variables()->set('url',$sUrl) ;
throw new _ExceptionRelocation ;
}
/**
* @return org\jecat\framework\auth\IIdentity
*/
protected function requireLogined($sDenyMessage=null,array $arrDenyArgvs=array())
{
if( !$aId=IdManager::singleton()->currentId() )
{
$this->permissionDenied($sDenyMessage,$arrDenyArgvs) ;
}
return $aId ;
}
protected function checkPermissions($sDenyMessage=null,array $arrDenyArgvs=array())
{
if( !$this->authorizer()->check(IdManager::singleton()) )
{
$this->permissionDenied($sDenyMessage,$arrDenyArgvs) ;
}
}
protected function permissionDenied($sDenyMessage=null,array $arrDenyArgvs=array())
{
throw new AuthenticationException($this,$sDenyMessage,$arrDenyArgvs) ;
}
public function buildParams($Params)
{
if(!$this->params)
{
$this->params = new DataSrc() ;
}
if( $Params instanceof IDataSrc )
{
$this->params->addChild($Params) ;
}
else if( is_array($Params) )
{
foreach($Params as $name=>&$value)
{
$this->params->set($name,$value) ;
};
}
else if( $Params===null )
{
// nothing todo
}
else
{
throw new Exception(__CLASS__."对象传入的 params 参数必须为 array 或 org\\jecat\\framework\\util\\IDataSrc 对象") ;
}
// 为子控制器设置执行参数
foreach($this->iterator() as $aChild)
{
$aChild->buildParams($this->params) ;
}
}
public function process ()
{
if($this->fnProcess)
{
$this->fnProcess($this) ;
}
else
{
$this->doActions() ;
}
}
/**
* @wiki /MVC模式/控制器/控制器的组合模式
* 多个控制器可以组合起来,几项简单的工作可以组合成一项更复杂的工作。JeCat的控制器可以自由组合,控制器之间互不干扰、各自独立工作,又可以彼此配合,浑然一体。
*/
public function add($object,$sName=null,$bTakeover=true)
{
if($sName===null)
{
$sName = $object->name() ;
}
if( $this->hasName($sName) )
{
throw new Exception("名称为:%s 的子控制器在控制器 %s 中已经存在,无法添加同名的子控制器",array($sName,$this->name())) ;
}
// 接管子类的视图
$this->takeOverView($object,$sName) ;
// 子类继承父类的 数据
if( $bTakeover and $object->params()!==$this->params())
{
$object->params()->addChild($this->params()) ;
}
parent::add($object,$sName,$bTakeover) ;
}
/**
* 接管子控制器的视图
*/
protected function takeOverView(Controller $aChild,$sChildName=null)
{
$this->mainView()->add( $aChild->mainView(), $sChildName?:$aChild->name(), true ) ;
}
public function remove($object)
{
parent::remove($object) ;
$object->params()->removeChild( $this->params() ) ;
}
/**
* @return IMessageQueue
*/
public function messageQueue($aAutoCreate=true)
{
if( !$this->aMsgQueue and $aAutoCreate )
{
$this->aMsgQueue = new MessageQueue() ;
}
return $this->aMsgQueue ;
}
public function setMessageQueue(IMessageQueue $aMsgQueue)
{
$this->aMsgQueue = $aMsgQueue ;
}
public function createMessage($sType,$sMessage,$arrMessageArgs=null,$aPoster=null)
{
return $this->messageQueue()->create($sType,$sMessage,$arrMessageArgs,$aPoster) ;
}
/**
* 在自己的 mainView 中显示一段字符串类型的内容
*/
public function renderString(& $sContent)
{
$aView = new View("anonymous",null,$this->mainView()->ui) ;
$this->mainView()->add($aView,null,true) ;
$this->response()->device()->write($sContent) ;
}
/**
* 在自己的 mainView 中渲染自己的消息队列
*/
public function renderMessageQueue($sTemplateFilename=null)
{
if( !$this->messageQueue()->count() )
{
return ;
}
$aView = new View("anonymous",null,$this->mainView()->ui()) ;
$this->mainView()->add($aView,null,true) ;
$this->messageQueue()->display($this->mainView()->ui(),$this->response()->device(),$sTemplateFilename) ;
}
/**
* @return org\jecat\framework\util\IDataSrc
*/
public function setParams(IDataSrc $aParams)
{
$this->params = $aParams ;
return $this ;
}
/**
* @return org\jecat\framework\util\IDataSrc
*/
public function params()
{
return $this->params ;
}
/**
* 当此方法负责常规的表单操作:
* 1、加载控件数据;
* 2、校验控件数据;
* 3、将数据交换到文档;
*
* 返回 true 的时候,传入的表单已经准备就绪。
* @return bool
*/
public function preprocessForm(IFormView $aView)
{
// 加载视图控件数据
$aView->loadWidgets($this->params) ;
// 校验数据
if( !$aView->verifyWidgets() )
{
return false ;
}
$aView->exchangeData(DataExchanger::WIDGET_TO_MODEL) ;
return true ;
}
public function doActions($sActParamName=null)
{
if(!$sActParamName)
{
$sActParamName = 'act' ;
}
if( !$this->params->has($sActParamName) )
{
return false ;
}
$sAct = $this->params[$sActParamName] ;
$sMethod = 'action' . $sAct ;
if( !method_exists($this,$sMethod) )
{
return false ;
}
$this->actionReturn = null ;
call_user_func(array($this,$sMethod)) ;
return true ;
}
public function setActionReturn(&$val)
{
$this->actionReturn =& $val ;
}
public function & actionReturn()
{
return $this->actionReturn ;
}
public function __get($sName)
{
// view
if($child=$this->mainView()->getByName($sName))
{
return $child ;
}
// model
else if($child=$this->modelContainer()->getByName($sName))
{
return $child ;
}
// controller
else if($child=$this->getByName($sName))
{
return $child ;
}
// properties
else if( $aProperties=$this->properties(false) and $value=$aProperties->get($sName) )
{
return $value ;
}
// ----------------
$nNameLen = strlen($sName) ;
if( $nNameLen>4 and substr($sName,0,4)=='view' )
{
$sViewName = substr($sName,4) ;
return $this->mainView()->getByName($sViewName)?: $this->mainView()->getByName(lcfirst($sViewName)) ;
}
else if( $nNameLen>5 and substr($sName,0,5)=='model' )
{
$sModelName = substr($sName,5) ;
return $this->modelContainer()->getByName($sModelName)?: $this->modelContainer()->getByName(lcfirst($sModelName)) ;
}
else if( $nNameLen>10 and substr($sName,0,10)=='controller' )
{
$sControllerName = substr($sName,10) ;
return $this->getByName($sControllerName)?: $this->getByName(lcfirst($sControllerName)) ;
}
else if( $aProperties and $nNameLen>8 and substr($sName,0,8)=='property' )
{
$sPropertyName = substr($sName,8) ;
return $aProperties->get($sPropertyName)?: $aProperties->get(lcfirst($sPropertyName)) ;
}
throw new Exception("正在访问控制器 %s 中不存在的属性:%s",array($this->name(),$sName)) ;
}
protected function defaultFrameConfig()
{
return array('class'=>'org\\jecat\\framework\\mvc\\controller\\WebpageFrame') ;
}
public function frame()
{
if( !$this->aFrame and !$this->params->bool('noframe') )
{
// 补充缺省的 frame 配置
if(empty($this->arrBeanConfig['frame']))
{
$this->arrBeanConfig['frame'] = $this->defaultFrameConfig() ;
}
else
{
$arrDefaultFrameConfig = $this->defaultFrameConfig() ;
BeanFactory::singleton()->mergeConfig($arrDefaultFrameConfig,$this->arrBeanConfig['frame']) ;
$this->arrBeanConfig['frame'] = $arrDefaultFrameConfig ;
}
$this->aFrame = BeanFactory::singleton()->createBean($this->arrBeanConfig['frame'],'*',false) ;
$this->aFrame->buildParams($this->params()) ;
$this->aFrame->buildBean($this->arrBeanConfig['frame']) ;
$this->aFrame->takeOverView($this) ;
}
return $this->aFrame ;
}
public function addModel(IModel $aModel,$sName=null)
{
return $this->modelContainer()->add($aModel,$sName) ;
}
public function removeModel(IModel $aModel)
{
$this->modelContainer()->remove($aModel) ;
return $this ;
}
/**
* @return org\jecat\framework\mvc\model\IModel
*/
public function modelByName($sName)
{
return $this->modelContainer()->getByName($sName) ;
}
public function modelIterator()
{
return $this->modelContainer()->iterator() ;
}
public function modelNameIterator()
{
return $this->modelContainer()->nameIterator() ;
}
public function clearModels()
{
$this->modelContainer()->clear() ;
return $this ;
}
public function addView(IView $aView,$sName=null)
{
if( $aOrController = $aView->controller() )
{
$aOrController->removeView($aView) ;
}
$aView->setController($this) ;
return $this->mainView()->add( $aView, $sName, true ) ;
}
public function removeView(IView $aView)
{
$aView->setController(null) ;
$this->mainView()->remove($aView) ;
return $this ;
}
/**
* @return org\jecat\framework\mvc\view\IView
*/
public function viewByName($sName)
{
return $this->mainView()->getByName($sName) ;
}
public function viewIterator()
{
return $this->mainView()->iterator() ;
}
public function clearViews()
{
$this->mainView()->clear() ;
return $this ;
}
/**
* @return org\jecat\framework\auth\Authorizer
*/
public function authorizer($bAutoCreate=true)
{
if( !$this->aAuthorizer and $bAutoCreate )
{
$sClass = BeanFactory::singleton()->beanClassNameByAlias('authorizer') ;
$this->aAuthorizer = new $sClass() ;
}
return $this->aAuthorizer ;
}
public function setAuthorizer(Authorizer $aAuthorizer)
{
$this->aAuthorizer = $aAuthorizer ;
return $this ;
}
/**
* @return org\jecat\framework\mvc\controller\Response
*/
public function response()
{
if(!$this->aResponse)
{
// 补充缺省的 frame 配置
if(empty($this->arrBeanConfig['rspn']))
{
$this->aResponse = Response::singleton() ;
}
else
{
if(!empty($this->arrBeanConfig['rspn']['class']))
{
$this->arrBeanConfig['rspn']['class'] = 'org\\jecat\\framework\\mvc\\controller\\Response' ;
$this->aResponse = BeanFactory::singleton()->createBean($this->arrBeanConfig['rspn'],'*',false) ;
}
}
}
return $this->aResponse ;
}
protected function modelContainer()
{
if(!$this->aModelContainer)
{
$this->aModelContainer = new Container("org\\jecat\\framework\\mvc\\model\\IModel") ;
}
return $this->aModelContainer ;