Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * TApplication class file
4
 *
5
 * @author Qiang Xue <qiang.xue@gmail.com>
6
 * @link http://www.pradosoft.com/
7
 * @copyright Copyright &copy; 2005-2008 PradoSoft
8
 * @license http://www.pradosoft.com/license/
9
 * @version $Id: TApplication.php 2578 2008-12-01 22:50:15Z carlgmathisen $
10
 * @package System
11
 */
12
 
13
/**
14
 * Includes core interfaces essential for TApplication class
15
 */
16
require_once(PRADO_DIR.'/interfaces.php');
17
 
18
/**
19
 * Includes core classes essential for TApplication class
20
 */
21
Prado::using('System.TApplicationComponent');
22
Prado::using('System.TModule');
23
Prado::using('System.TService');
24
Prado::using('System.Exceptions.TErrorHandler');
25
Prado::using('System.Caching.TCache');
26
Prado::using('System.IO.TTextWriter');
27
Prado::using('System.Collections.TList');
28
Prado::using('System.Collections.TMap');
29
Prado::using('System.Collections.TStack');
30
Prado::using('System.Xml.TXmlDocument');
31
Prado::using('System.Security.TAuthorizationRule');
32
Prado::using('System.Security.TSecurityManager');
33
Prado::using('System.Web.THttpUtility');
34
Prado::using('System.Web.Javascripts.TJavaScript');
35
Prado::using('System.Web.THttpRequest');
36
Prado::using('System.Web.THttpResponse');
37
Prado::using('System.Web.THttpSession');
38
Prado::using('System.Web.Services.TPageService');
39
Prado::using('System.Web.TAssetManager');
40
Prado::using('System.I18N.TGlobalization');
41
 
42
/**
43
 * TApplication class.
44
 *
45
 * TApplication coordinates modules and services, and serves as a configuration
46
 * context for all Prado components.
47
 *
48
 * TApplication uses a configuration file to specify the settings of
49
 * the application, the modules, the services, the parameters, and so on.
50
 *
51
 * TApplication adopts a modular structure. A TApplication instance is a composition
52
 * of multiple modules. A module is an instance of class implementing
53
 * {@link IModule} interface. Each module accomplishes certain functionalities
54
 * that are shared by all Prado components in an application.
55
 * There are default modules and user-defined modules. The latter offers extreme
56
 * flexibility of extending TApplication in a plug-and-play fashion.
57
 * Modules cooperate with each other to serve a user request by following
58
 * a sequence of lifecycles predefined in TApplication.
59
 *
60
 * TApplication has four modes that can be changed by setting {@link setMode Mode}
61
 * property (in the application configuration file).
62
 * - <b>Off</b> mode will prevent the application from serving user requests.
63
 * - <b>Debug</b> mode is mainly used during application development. It ensures
64
 *   the cache is always up-to-date if caching is enabled. It also allows
65
 *   exceptions are displayed with rich context information if they occur.
66
 * - <b>Normal</b> mode is mainly used during production stage. Exception information
67
 *   will only be recorded in system error logs. The cache is ensured to be
68
 *   up-to-date if it is enabled.
69
 * - <b>Performance</b> mode is similar to <b>Normal</b> mode except that it
70
 *   does not ensure the cache is up-to-date.
71
 *
72
 * TApplication dispatches each user request to a particular service which
73
 * finishes the actual work for the request with the aid from the application
74
 * modules.
75
 *
76
 * TApplication maintains a lifecycle with the following stages:
77
 * - [construct] : construction of the application instance
78
 * - [initApplication] : load application configuration and instantiate modules and the requested service
79
 * - onBeginRequest : this event happens right after application initialization
80
 * - onAuthentication : this event happens when authentication is needed for the current request
81
 * - onAuthenticationComplete : this event happens right after the authentication is done for the current request
82
 * - onAuthorization : this event happens when authorization is needed for the current request
83
 * - onAuthorizationComplete : this event happens right after the authorization is done for the current request
84
 * - onLoadState : this event happens when application state needs to be loaded
85
 * - onLoadStateComplete : this event happens right after the application state is loaded
86
 * - onPreRunService : this event happens right before the requested service is to run
87
 * - runService : the requested service runs
88
 * - onSaveState : this event happens when application needs to save its state
89
 * - onSaveStateComplete : this event happens right after the application saves its state
90
 * - onPreFlushOutput : this event happens right before the application flushes output to client side.
91
 * - flushOutput : the application flushes output to client side.
92
 * - onEndRequest : this is the last stage a request is being completed
93
 * - [destruct] : destruction of the application instance
94
 * Modules and services can attach their methods to one or several of the above
95
 * events and do appropriate processing when the events are raised. By this way,
96
 * the application is able to coordinate the activities of modules and services
97
 * in the above order. To terminate an application before the whole lifecycle
98
 * completes, call {@link completeRequest}.
99
 *
100
 * Examples:
101
 * - Create and run a Prado application:
102
 * <code>
103
 * $application=new TApplication($configFile);
104
 * $application->run();
105
 * </code>
106
 *
107
 * @author Qiang Xue <qiang.xue@gmail.com>
108
 * @version $Id: TApplication.php 2578 2008-12-01 22:50:15Z carlgmathisen $
109
 * @package System
110
 * @since 3.0
111
 */
112
class TApplication extends TComponent
113
{
114
	/**
115
	 * possible application mode.
116
	 * @deprecated deprecated since version 3.0.4 (use TApplicationMode constants instead)
117
	 */
118
	const STATE_OFF='Off';
119
	const STATE_DEBUG='Debug';
120
	const STATE_NORMAL='Normal';
121
	const STATE_PERFORMANCE='Performance';
122
 
123
	/**
124
	 * Page service ID
125
	 */
126
	const PAGE_SERVICE_ID='page';
127
	/**
128
	 * Application configuration file name
129
	 */
130
	const CONFIG_FILE='application.xml';
131
	/**
132
	 * File extension for external config files
133
	 */
134
	const CONFIG_FILE_EXT='.xml';
135
	/**
136
	 * Runtime directory name
137
	 */
138
	const RUNTIME_PATH='runtime';
139
	/**
140
	 * Config cache file
141
	 */
142
	const CONFIGCACHE_FILE='config.cache';
143
	/**
144
	 * Global data file
145
	 */
146
	const GLOBAL_FILE='global.cache';
147
 
148
	/**
149
	 * @var array list of events that define application lifecycles
150
	 */
151
	private static $_steps=array(
152
		'onBeginRequest',
153
		'onLoadState',
154
		'onLoadStateComplete',
155
		'onAuthentication',
156
		'onAuthenticationComplete',
157
		'onAuthorization',
158
		'onAuthorizationComplete',
159
		'onPreRunService',
160
		'runService',
161
		'onSaveState',
162
		'onSaveStateComplete',
163
		'onPreFlushOutput',
164
		'flushOutput'
165
	);
166
 
167
	/**
168
	 * @var string application ID
169
	 */
170
	private $_id;
171
	/**
172
	 * @var string unique application ID
173
	 */
174
	private $_uniqueID;
175
	/**
176
	 * @var boolean whether the request is completed
177
	 */
178
	private $_requestCompleted=false;
179
	/**
180
	 * @var integer application state
181
	 */
182
	private $_step;
183
	/**
184
	 * @var array available services and their configurations indexed by service IDs
185
	 */
186
	private $_services;
187
	/**
188
	 * @var IService current service instance
189
	 */
190
	private $_service;
191
	/**
192
	 * @var array list of application modules
193
	 */
194
	private $_modules=array();
195
	/**
196
	 * @var TMap list of application parameters
197
	 */
198
	private $_parameters;
199
	/**
200
	 * @var string configuration file
201
	 */
202
	private $_configFile;
203
	/**
204
	 * @var string application base path
205
	 */
206
	private $_basePath;
207
	/**
208
	 * @var string directory storing application state
209
	 */
210
	private $_runtimePath;
211
	/**
212
	 * @var boolean if any global state is changed during the current request
213
	 */
214
	private $_stateChanged=false;
215
	/**
216
	 * @var array global variables (persistent across sessions, requests)
217
	 */
218
	private $_globals=array();
219
	/**
220
	 * @var string cache file
221
	 */
222
	private $_cacheFile;
223
	/**
224
	 * @var TErrorHandler error handler module
225
	 */
226
	private $_errorHandler;
227
	/**
228
	 * @var THttpRequest request module
229
	 */
230
	private $_request;
231
	/**
232
	 * @var THttpResponse response module
233
	 */
234
	private $_response;
235
	/**
236
	 * @var THttpSession session module, could be null
237
	 */
238
	private $_session;
239
	/**
240
	 * @var ICache cache module, could be null
241
	 */
242
	private $_cache;
243
	/**
244
	 * @var IStatePersister application state persister
245
	 */
246
	private $_statePersister;
247
	/**
248
	 * @var IUser user instance, could be null
249
	 */
250
	private $_user;
251
	/**
252
	 * @var TGlobalization module, could be null
253
	 */
254
	private $_globalization;
255
	/**
256
	 * @var TSecurityManager security manager module
257
	 */
258
	private $_security;
259
	/**
260
	 * @var TAssetManager asset manager module
261
	 */
262
	private $_assetManager;
263
	/**
264
	 * @var TAuthorizationRuleCollection collection of authorization rules
265
	 */
266
	private $_authRules;
267
	/**
268
	 * @var TApplicationMode application mode
269
	 */
270
	private $_mode=TApplicationMode::Debug;
271
 
272
	/**
273
	 * @var string Customizable page service ID
274
	 */
275
	private $_pageServiceID = self::PAGE_SERVICE_ID;
276
 
277
	/**
278
	 * Constructor.
279
	 * Sets application base path and initializes the application singleton.
280
	 * Application base path refers to the root directory storing application
281
	 * data and code not directly accessible by Web users.
282
	 * By default, the base path is assumed to be the <b>protected</b>
283
	 * directory under the directory containing the current running script.
284
	 * @param string application base path or configuration file path.
285
	 *        If the parameter is a file, it is assumed to be the application
286
	 *        configuration file, and the directory containing the file is treated
287
	 *        as the application base path.
288
	 *        If it is a directory, it is assumed to be the application base path,
289
	 *        and within that directory, a file named <b>application.xml</b>
290
	 *        will be looked for. If found, the file is considered as the application
291
	 *        configuration file.
292
	 * @param boolean whether to cache application configuration. Defaults to true.
293
	 * @throws TConfigurationException if configuration file cannot be read or the runtime path is invalid.
294
	 */
295
	public function __construct($basePath='protected',$cacheConfig=true)
296
	{
297
		// register application as a singleton
298
		Prado::setApplication($this);
299
 
300
		$this->resolvePaths($basePath);
301
 
302
		if($cacheConfig)
303
			$this->_cacheFile=$this->_runtimePath.DIRECTORY_SEPARATOR.self::CONFIGCACHE_FILE;
304
 
305
		// generates unique ID by hashing the runtime path
306
		$this->_uniqueID=md5($this->_runtimePath);
307
		$this->_parameters=new TMap;
308
		$this->_services=array($this->getPageServiceID()=>array('TPageService',array(),null));
309
 
310
		Prado::setPathOfAlias('Application',$this->_basePath);
311
	}
312
 
313
	/**
314
	 * Resolves application-relevant paths.
315
	 * This method is invoked by the application constructor
316
	 * to determine the application configuration file,
317
	 * application root path and the runtime path.
318
	 * @param string the application root path or the application configuration file
319
	 * @see setBasePath
320
	 * @see setRuntimePath
321
	 * @see setConfigurationFile
322
	 */
323
	protected function resolvePaths($basePath)
324
	{
325
		// determine configuration path and file
326
		if(empty($basePath) || ($basePath=realpath($basePath))===false)
327
			throw new TConfigurationException('application_basepath_invalid',$basePath);
328
		if(is_file($basePath.DIRECTORY_SEPARATOR.self::CONFIG_FILE))
329
			$configFile=$basePath.DIRECTORY_SEPARATOR.self::CONFIG_FILE;
330
		else if(is_file($basePath))
331
		{
332
			$configFile=$basePath;
333
			$basePath=dirname($configFile);
334
		}
335
		else
336
			$configFile=null;
337
 
338
		// determine runtime path
339
		$runtimePath=$basePath.DIRECTORY_SEPARATOR.self::RUNTIME_PATH;
340
		if(is_writable($runtimePath))
341
		{
342
			if($configFile!==null)
343
			{
344
				$runtimePath.=DIRECTORY_SEPARATOR.basename($configFile).'-'.Prado::getVersion();
345
				if(!is_dir($runtimePath))
346
				{
347
					if(@mkdir($runtimePath)===false)
348
						throw new TConfigurationException('application_runtimepath_failed',$runtimePath);
349
					@chmod($runtimePath, PRADO_CHMOD); //make it deletable
350
				}
351
				$this->setConfigurationFile($configFile);
352
			}
353
			$this->setBasePath($basePath);
354
			$this->setRuntimePath($runtimePath);
355
		}
356
		else
357
			throw new TConfigurationException('application_runtimepath_invalid',$runtimePath);
358
 
359
	}
360
 
361
	/**
362
	 * Executes the lifecycles of the application.
363
	 * This is the main entry function that leads to the running of the whole
364
	 * Prado application.
365
	 */
366
	public function run()
367
	{
368
		try
369
		{
370
			$this->initApplication();
371
			$n=count(self::$_steps);
372
			$this->_step=0;
373
			$this->_requestCompleted=false;
374
			while($this->_step<$n)
375
			{
376
				if($this->_mode===self::STATE_OFF)
377
					throw new THttpException(503,'application_unavailable');
378
				if($this->_requestCompleted)
379
					break;
380
				$method=self::$_steps[$this->_step];
381
				Prado::trace("Executing $method()",'System.TApplication');
382
				$this->$method();
383
				$this->_step++;
384
			}
385
		}
386
		catch(Exception $e)
387
		{
388
			$this->onError($e);
389
		}
390
		$this->onEndRequest();
391
	}
392
 
393
	/**
394
	 * Completes current request processing.
395
	 * This method can be used to exit the application lifecycles after finishing
396
	 * the current cycle.
397
	 */
398
	public function completeRequest()
399
	{
400
		$this->_requestCompleted=true;
401
	}
402
 
403
	/**
404
	 * @return boolean whether the current request is processed.
405
	 */
406
	public function getRequestCompleted()
407
	{
408
		return $this->_requestCompleted;
409
	}
410
 
411
	/**
412
	 * Returns a global value.
413
	 *
414
	 * A global value is one that is persistent across users sessions and requests.
415
	 * @param string the name of the value to be returned
416
	 * @param mixed the default value. If $key is not found, $defaultValue will be returned
417
	 * @return mixed the global value corresponding to $key
418
	 */
419
	public function getGlobalState($key,$defaultValue=null)
420
	{
421
		return isset($this->_globals[$key])?$this->_globals[$key]:$defaultValue;
422
	}
423
 
424
	/**
425
	 * Sets a global value.
426
	 *
427
	 * A global value is one that is persistent across users sessions and requests.
428
	 * Make sure that the value is serializable and unserializable.
429
	 * @param string the name of the value to be set
430
	 * @param mixed the global value to be set
431
	 * @param mixed the default value. If $key is not found, $defaultValue will be returned
432
	 */
433
	public function setGlobalState($key,$value,$defaultValue=null)
434
	{
435
		$this->_stateChanged=true;
436
		if($value===$defaultValue)
437
			unset($this->_globals[$key]);
438
		else
439
			$this->_globals[$key]=$value;
440
	}
441
 
442
	/**
443
	 * Clears a global value.
444
	 *
445
	 * The value cleared will no longer be available in this request and the following requests.
446
	 * @param string the name of the value to be cleared
447
	 */
448
	public function clearGlobalState($key)
449
	{
450
		$this->_stateChanged=true;
451
		unset($this->_globals[$key]);
452
	}
453
 
454
	/**
455
	 * Loads global values from persistent storage.
456
	 * This method is invoked when {@link onLoadState OnLoadState} event is raised.
457
	 * After this method, values that are stored in previous requests become
458
	 * available to the current request via {@link getGlobalState}.
459
	 */
460
	protected function loadGlobals()
461
	{
462
		$this->_globals=$this->getApplicationStatePersister()->load();
463
	}
464
 
465
	/**
466
	 * Saves global values into persistent storage.
467
	 * This method is invoked when {@link onSaveState OnSaveState} event is raised.
468
	 */
469
	protected function saveGlobals()
470
	{
471
		if($this->_stateChanged)
472
		{
473
			$this->_stateChanged=false;
474
			$this->getApplicationStatePersister()->save($this->_globals);
475
		}
476
	}
477
 
478
	/**
479
	 * @return string application ID
480
	 */
481
	public function getID()
482
	{
483
		return $this->_id;
484
	}
485
 
486
	/**
487
	 * @param string application ID
488
	 */
489
	public function setID($value)
490
	{
491
		$this->_id=$value;
492
	}
493
 
494
	/**
495
	 * @return string page service ID
496
	 */
497
	public function getPageServiceID()
498
	{
499
		return $this->_pageServiceID;
500
	}
501
 
502
	/**
503
	 * @param string page service ID
504
	 */
505
	public function setPageServiceID($value)
506
	{
507
		$this->_pageServiceID=$value;
508
	}
509
 
510
	/**
511
	 * @return string an ID that uniquely identifies this Prado application from the others
512
	 */
513
	public function getUniqueID()
514
	{
515
		return $this->_uniqueID;
516
	}
517
 
518
	/**
519
	 * @return TApplicationMode application mode. Defaults to TApplicationMode::Debug.
520
	 */
521
	public function getMode()
522
	{
523
		return $this->_mode;
524
	}
525
 
526
	/**
527
	 * @param TApplicationMode application mode
528
	 */
529
	public function setMode($value)
530
	{
531
		$this->_mode=TPropertyValue::ensureEnum($value,'TApplicationMode');
532
	}
533
 
534
	/**
535
	 * @return string the directory containing the application configuration file (absolute path)
536
	 */
537
	public function getBasePath()
538
	{
539
		return $this->_basePath;
540
	}
541
 
542
	/**
543
	 * @param string the directory containing the application configuration file
544
	 */
545
	public function setBasePath($value)
546
	{
547
		$this->_basePath=$value;
548
	}
549
 
550
	/**
551
	 * @return string the application configuration file (absolute path)
552
	 */
553
	public function getConfigurationFile()
554
	{
555
		return $this->_configFile;
556
	}
557
 
558
	/**
559
	 * @param string the application configuration file (absolute path)
560
	 */
561
	public function setConfigurationFile($value)
562
	{
563
		$this->_configFile=$value;
564
	}
565
 
566
	/**
567
	 * @return string the directory storing cache data and application-level persistent data. (absolute path)
568
	 */
569
	public function getRuntimePath()
570
	{
571
		return $this->_runtimePath;
572
	}
573
 
574
	/**
575
	 * @param string the directory storing cache data and application-level persistent data. (absolute path)
576
	 */
577
	public function setRuntimePath($value)
578
	{
579
		$this->_runtimePath=$value;
580
		if($this->_cacheFile)
581
			$this->_cacheFile=$this->_runtimePath.DIRECTORY_SEPARATOR.self::CONFIGCACHE_FILE;
582
		// generates unique ID by hashing the runtime path
583
		$this->_uniqueID=md5($this->_runtimePath);
584
	}
585
 
586
	/**
587
	 * @return IService the currently requested service
588
	 */
589
	public function getService()
590
	{
591
		return $this->_service;
592
	}
593
 
594
	/**
595
	 * @param IService the currently requested service
596
	 */
597
	public function setService($value)
598
	{
599
		$this->_service=$value;
600
	}
601
 
602
	/**
603
	 * Adds a module to application.
604
	 * Note, this method does not do module initialization.
605
	 * @param string ID of the module
606
	 * @param IModule module object
607
	 */
608
	public function setModule($id,IModule $module)
609
	{
610
		if(isset($this->_modules[$id]))
611
			throw new TConfigurationException('application_moduleid_duplicated',$id);
612
		else
613
			$this->_modules[$id]=$module;
614
	}
615
 
616
	/**
617
	 * @return IModule the module with the specified ID, null if not found
618
	 */
619
	public function getModule($id)
620
	{
621
		return isset($this->_modules[$id])?$this->_modules[$id]:null;
622
	}
623
 
624
	/**
625
	 * @return array list of loaded application modules, indexed by module IDs
626
	 */
627
	public function getModules()
628
	{
629
		return $this->_modules;
630
	}
631
 
632
	/**
633
	 * Returns the list of application parameters.
634
	 * Since the parameters are returned as a {@link TMap} object, you may use
635
	 * the returned result to access, add or remove individual parameters.
636
	 * @return TMap the list of application parameters
637
	 */
638
	public function getParameters()
639
	{
640
		return $this->_parameters;
641
	}
642
 
643
	/**
644
	 * @return THttpRequest the request module
645
	 */
646
	public function getRequest()
647
	{
648
		if(!$this->_request)
649
		{
650
			$this->_request=new THttpRequest;
651
			$this->_request->init(null);
652
		}
653
		return $this->_request;
654
	}
655
 
656
	/**
657
	 * @param THttpRequest the request module
658
	 */
659
	public function setRequest(THttpRequest $request)
660
	{
661
		$this->_request=$request;
662
	}
663
 
664
	/**
665
	 * @return THttpResponse the response module
666
	 */
667
	public function getResponse()
668
	{
669
		if(!$this->_response)
670
		{
671
			$this->_response=new THttpResponse;
672
			$this->_response->init(null);
673
		}
674
		return $this->_response;
675
	}
676
 
677
	/**
678
	 * @param THttpRequest the request module
679
	 */
680
	public function setResponse(THttpResponse $response)
681
	{
682
		$this->_response=$response;
683
	}
684
 
685
	/**
686
	 * @return THttpSession the session module, null if session module is not installed
687
	 */
688
	public function getSession()
689
	{
690
		if(!$this->_session)
691
		{
692
			$this->_session=new THttpSession;
693
			$this->_session->init(null);
694
		}
695
		return $this->_session;
696
	}
697
 
698
	/**
699
	 * @param THttpSession the session module
700
	 */
701
	public function setSession(THttpSession $session)
702
	{
703
		$this->_session=$session;
704
	}
705
 
706
	/**
707
	 * @return TErrorHandler the error handler module
708
	 */
709
	public function getErrorHandler()
710
	{
711
		if(!$this->_errorHandler)
712
		{
713
			$this->_errorHandler=new TErrorHandler;
714
			$this->_errorHandler->init(null);
715
		}
716
		return $this->_errorHandler;
717
	}
718
 
719
	/**
720
	 * @param TErrorHandler the error handler module
721
	 */
722
	public function setErrorHandler(TErrorHandler $handler)
723
	{
724
		$this->_errorHandler=$handler;
725
	}
726
 
727
	/**
728
	 * @return TSecurityManager the security manager module
729
	 */
730
	public function getSecurityManager()
731
	{
732
		if(!$this->_security)
733
		{
734
			$this->_security=new TSecurityManager;
735
			$this->_security->init(null);
736
		}
737
		return $this->_security;
738
	}
739
 
740
	/**
741
	 * @param TSecurityManager the security manager module
742
	 */
743
	public function setSecurityManager(TSecurityManager $sm)
744
	{
745
		$this->_security=$sm;
746
	}
747
 
748
	/**
749
	 * @return TAssetManager asset manager
750
	 */
751
	public function getAssetManager()
752
	{
753
		if(!$this->_assetManager)
754
		{
755
			$this->_assetManager=new TAssetManager;
756
			$this->_assetManager->init(null);
757
		}
758
		return $this->_assetManager;
759
	}
760
 
761
	/**
762
	 * @param TAssetManager asset manager
763
	 */
764
	public function setAssetManager(TAssetManager $value)
765
	{
766
		$this->_assetManager=$value;
767
	}
768
 
769
	/**
770
	 * @return IStatePersister application state persister
771
	 */
772
	public function getApplicationStatePersister()
773
	{
774
		if(!$this->_statePersister)
775
		{
776
			$this->_statePersister=new TApplicationStatePersister;
777
			$this->_statePersister->init(null);
778
		}
779
		return $this->_statePersister;
780
	}
781
 
782
	/**
783
	 * @param IStatePersister  application state persister
784
	 */
785
	public function setApplicationStatePersister(IStatePersister $persister)
786
	{
787
		$this->_statePersister=$persister;
788
	}
789
 
790
	/**
791
	 * @return ICache the cache module, null if cache module is not installed
792
	 */
793
	public function getCache()
794
	{
795
		return $this->_cache;
796
	}
797
 
798
	/**
799
	 * @param ICache the cache module
800
	 */
801
	public function setCache(ICache $cache)
802
	{
803
		$this->_cache=$cache;
804
	}
805
 
806
	/**
807
	 * @return IUser the application user
808
	 */
809
	public function getUser()
810
	{
811
		return $this->_user;
812
	}
813
 
814
	/**
815
	 * @param IUser the application user
816
	 */
817
	public function setUser(IUser $user)
818
	{
819
		$this->_user=$user;
820
	}
821
 
822
	/**
823
	 * @param boolean whether to create globalization if it does not exist
824
	 * @return TGlobalization globalization module
825
	 */
826
	public function getGlobalization($createIfNotExists=true)
827
	{
828
		if($this->_globalization===null && $createIfNotExists)
829
			$this->_globalization=new TGlobalization;
830
		return $this->_globalization;
831
	}
832
 
833
	/**
834
	 * @param TGlobalization globalization module
835
	 */
836
	public function setGlobalization(TGlobalization $glob)
837
	{
838
		$this->_globalization=$glob;
839
	}
840
 
841
	/**
842
	 * @return TAuthorizationRuleCollection list of authorization rules for the current request
843
	 */
844
	public function getAuthorizationRules()
845
	{
846
		if($this->_authRules===null)
847
			$this->_authRules=new TAuthorizationRuleCollection;
848
		return $this->_authRules;
849
	}
850
 
851
	/**
852
	 * Applies an application configuration.
853
	 * @param TApplicationConfiguration the configuration
854
	 * @param boolean whether the configuration is specified within a service.
855
	 */
856
	public function applyConfiguration($config,$withinService=false)
857
	{
858
		if($config->getIsEmpty())
859
			return;
860
 
861
		// set path aliases and using namespaces
862
		foreach($config->getAliases() as $alias=>$path)
863
			Prado::setPathOfAlias($alias,$path);
864
		foreach($config->getUsings() as $using)
865
			Prado::using($using);
866
 
867
		// set application properties
868
		if(!$withinService)
869
		{
870
			foreach($config->getProperties() as $name=>$value)
871
				$this->setSubProperty($name,$value);
872
		}
873
 
874
		if(empty($this->_services))
875
			$this->_services=array($this->getPageServiceID()=>array('TPageService',array(),null));
876
 
877
		// load parameters
878
		foreach($config->getParameters() as $id=>$parameter)
879
		{
880
			if(is_array($parameter))
881
			{
882
				$component=Prado::createComponent($parameter[0]);
883
				foreach($parameter[1] as $name=>$value)
884
					$component->setSubProperty($name,$value);
885
				$this->_parameters->add($id,$component);
886
			}
887
			else
888
				$this->_parameters->add($id,$parameter);
889
		}
890
 
891
		// load and init modules specified in app config
892
		$modules=array();
893
		foreach($config->getModules() as $id=>$moduleConfig)
894
		{
895
			Prado::trace("Loading module $id ({$moduleConfig[0]})",'System.TApplication');
896
			list($moduleClass, $initProperties, $configElement)=$moduleConfig;
897
			$module=Prado::createComponent($moduleClass);
898
			if(!is_string($id))
899
			{
900
				$id='_module'.count($this->_modules);
901
				$initProperties['id']=$id;
902
			}
903
			$this->setModule($id,$module);
904
			foreach($initProperties as $name=>$value)
905
				$module->setSubProperty($name,$value);
906
			$modules[]=array($module,$configElement);
907
		}
908
		foreach($modules as $module)
909
			$module[0]->init($module[1]);
910
 
911
		// load service
912
		foreach($config->getServices() as $serviceID=>$serviceConfig)
913
			$this->_services[$serviceID]=$serviceConfig;
914
 
915
		// external configurations
916
		foreach($config->getExternalConfigurations() as $filePath=>$condition)
917
		{
918
			if($condition!==true)
919
				$condition=$this->evaluateExpression($condition);
920
			if($condition)
921
			{
922
				if(($path=Prado::getPathOfNamespace($filePath,self::CONFIG_FILE_EXT))===null || !is_file($path))
923
					throw new TConfigurationException('application_includefile_invalid',$filePath);
924
				$c=new TApplicationConfiguration;
925
				$c->loadFromFile($path);
926
				$this->applyConfiguration($c,$withinService);
927
			}
928
		}
929
	}
930
 
931
	/**
932
	 * Loads configuration and initializes application.
933
	 * Configuration file will be read and parsed (if a valid cached version exists,
934
	 * it will be used instead). Then, modules are created and initialized;
935
	 * Afterwards, the requested service is created and initialized.
936
	 * @param string configuration file path (absolute or relative to current executing script)
937
	 * @param string cache file path, empty if no present or needed
938
	 * @throws TConfigurationException if module is redefined of invalid type, or service not defined or of invalid type
939
	 */
940
	protected function initApplication()
941
	{
942
		Prado::trace('Initializing application','System.TApplication');
943
 
944
		if($this->_configFile!==null)
945
		{
946
			if($this->_cacheFile===null || @filemtime($this->_cacheFile)<filemtime($this->_configFile))
947
			{
948
				$config=new TApplicationConfiguration;
949
				$config->loadFromFile($this->_configFile);
950
				if($this->_cacheFile!==null)
951
					file_put_contents($this->_cacheFile,Prado::serialize($config),LOCK_EX);
952
			}
953
			else
954
				$config=Prado::unserialize(file_get_contents($this->_cacheFile));
955
 
956
			$this->applyConfiguration($config,false);
957
		}
958
 
959
		if(($serviceID=$this->getRequest()->resolveRequest(array_keys($this->_services)))===null)
960
			$serviceID=$this->getPageServiceID();
961
 
962
		$this->startService($serviceID);
963
	}
964
 
965
	/**
966
	 * Starts the specified service.
967
	 * The service instance will be created. Its properties will be initialized
968
	 * and the configurations will be applied, if any.
969
	 * @param string service ID
970
	 */
971
	public function startService($serviceID)
972
	{
973
		if(isset($this->_services[$serviceID]))
974
		{
975
			list($serviceClass,$initProperties,$configElement)=$this->_services[$serviceID];
976
			$service=Prado::createComponent($serviceClass);
977
			if(!($service instanceof IService))
978
				throw new THttpException(500,'application_service_invalid',$serviceClass);
979
			if(!$service->getEnabled())
980
				throw new THttpException(500,'application_service_unavailable',$serviceClass);
981
			$service->setID($serviceID);
982
			$this->setService($service);
983
 
984
			foreach($initProperties as $name=>$value)
985
				$service->setSubProperty($name,$value);
986
 
987
			if($configElement!==null)
988
			{
989
				$config=new TApplicationConfiguration;
990
				$config->loadFromXml($configElement,$this->getBasePath());
991
				$this->applyConfiguration($config,true);
992
			}
993
 
994
			$service->init($configElement);
995
		}
996
		else
997
			throw new THttpException(500,'application_service_unknown',$serviceID);
998
	}
999
 
1000
	/**
1001
	 * Raises OnError event.
1002
	 * This method is invoked when an exception is raised during the lifecycles
1003
	 * of the application.
1004
	 * @param mixed event parameter
1005
	 */
1006
	public function onError($param)
1007
	{
1008
		Prado::log($param->getMessage(),TLogger::ERROR,'System.TApplication');
1009
		$this->raiseEvent('OnError',$this,$param);
1010
		$this->getErrorHandler()->handleError($this,$param);
1011
	}
1012
 
1013
	/**
1014
	 * Raises OnBeginRequest event.
1015
	 * At the time when this method is invoked, application modules are loaded
1016
	 * and initialized, user request is resolved and the corresponding service
1017
	 * is loaded and initialized. The application is about to start processing
1018
	 * the user request.
1019
	 */
1020
	public function onBeginRequest()
1021
	{
1022
		$this->raiseEvent('OnBeginRequest',$this,null);
1023
	}
1024
 
1025
	/**
1026
	 * Raises OnAuthentication event.
1027
	 * This method is invoked when the user request needs to be authenticated.
1028
	 */
1029
	public function onAuthentication()
1030
	{
1031
		$this->raiseEvent('OnAuthentication',$this,null);
1032
	}
1033
 
1034
	/**
1035
	 * Raises OnAuthenticationComplete event.
1036
	 * This method is invoked right after the user request is authenticated.
1037
	 */
1038
	public function onAuthenticationComplete()
1039
	{
1040
		$this->raiseEvent('OnAuthenticationComplete',$this,null);
1041
	}
1042
 
1043
	/**
1044
	 * Raises OnAuthorization event.
1045
	 * This method is invoked when the user request needs to be authorized.
1046
	 */
1047
	public function onAuthorization()
1048
	{
1049
		$this->raiseEvent('OnAuthorization',$this,null);
1050
	}
1051
 
1052
	/**
1053
	 * Raises OnAuthorizationComplete event.
1054
	 * This method is invoked right after the user request is authorized.
1055
	 */
1056
	public function onAuthorizationComplete()
1057
	{
1058
		$this->raiseEvent('OnAuthorizationComplete',$this,null);
1059
	}
1060
 
1061
	/**
1062
	 * Raises OnLoadState event.
1063
	 * This method is invoked when the application needs to load state (probably stored in session).
1064
	 */
1065
	public function onLoadState()
1066
	{
1067
		$this->loadGlobals();
1068
		$this->raiseEvent('OnLoadState',$this,null);
1069
	}
1070
 
1071
	/**
1072
	 * Raises OnLoadStateComplete event.
1073
	 * This method is invoked right after the application state has been loaded.
1074
	 */
1075
	public function onLoadStateComplete()
1076
	{
1077
		$this->raiseEvent('OnLoadStateComplete',$this,null);
1078
	}
1079
 
1080
	/**
1081
	 * Raises OnPreRunService event.
1082
	 * This method is invoked right before the service is to be run.
1083
	 */
1084
	public function onPreRunService()
1085
	{
1086
		$this->raiseEvent('OnPreRunService',$this,null);
1087
	}
1088
 
1089
	/**
1090
	 * Runs the requested service.
1091
	 */
1092
	public function runService()
1093
	{
1094
		if($this->_service)
1095
			$this->_service->run();
1096
	}
1097
 
1098
	/**
1099
	 * Raises OnSaveState event.
1100
	 * This method is invoked when the application needs to save state (probably stored in session).
1101
	 */
1102
	public function onSaveState()
1103
	{
1104
		$this->raiseEvent('OnSaveState',$this,null);
1105
		$this->saveGlobals();
1106
	}
1107
 
1108
	/**
1109
	 * Raises OnSaveStateComplete event.
1110
	 * This method is invoked right after the application state has been saved.
1111
	 */
1112
	public function onSaveStateComplete()
1113
	{
1114
		$this->raiseEvent('OnSaveStateComplete',$this,null);
1115
	}
1116
 
1117
	/**
1118
	 * Raises OnPreFlushOutput event.
1119
	 * This method is invoked right before the application flushes output to client.
1120
	 */
1121
	public function onPreFlushOutput()
1122
	{
1123
		$this->raiseEvent('OnPreFlushOutput',$this,null);
1124
	}
1125
 
1126
	/**
1127
	 * Flushes output to client side.
1128
	 */
1129
	public function flushOutput()
1130
	{
1131
		$this->getResponse()->flush();
1132
	}
1133
 
1134
	/**
1135
	 * Raises OnEndRequest event.
1136
	 * This method is invoked when the application completes the processing of the request.
1137
	 */
1138
	public function onEndRequest()
1139
	{
1140
		$this->saveGlobals();  // save global state
1141
		$this->raiseEvent('OnEndRequest',$this,null);
1142
	}
1143
}
1144
 
1145
/**
1146
 * TApplicationMode class.
1147
 * TApplicationMode defines the possible mode that an application can be set at by
1148
 * setting {@link TApplication::setMode Mode}.
1149
 * In particular, the following modes are defined
1150
 * - Off: the application is not running. Any request to the application will obtain an error.
1151
 * - Debug: the application is running in debug mode.
1152
 * - Normal: the application is running in normal production mode.
1153
 * - Performance: the application is running in performance mode.
1154
 * @author Qiang Xue <qiang.xue@gmail.com>
1155
 * @version $Id: TApplication.php 2578 2008-12-01 22:50:15Z carlgmathisen $
1156
 * @package System
1157
 * @since 3.0.4
1158
 */
1159
class TApplicationMode extends TEnumerable
1160
{
1161
	const Off='Off';
1162
	const Debug='Debug';
1163
	const Normal='Normal';
1164
	const Performance='Performance';
1165
}
1166
 
1167
 
1168
/**
1169
 * TApplicationConfiguration class.
1170
 *
1171
 * This class is used internally by TApplication to parse and represent application configuration.
1172
 *
1173
 * @author Qiang Xue <qiang.xue@gmail.com>
1174
 * @version $Id: TApplication.php 2578 2008-12-01 22:50:15Z carlgmathisen $
1175
 * @package System
1176
 * @since 3.0
1177
 */
1178
class TApplicationConfiguration extends TComponent
1179
{
1180
	/**
1181
	 * @var array list of application initial property values, indexed by property names
1182
	 */
1183
	private $_properties=array();
1184
	/**
1185
	 * @var array list of namespaces to be used
1186
	 */
1187
	private $_usings=array();
1188
	/**
1189
	 * @var array list of path aliases, indexed by alias names
1190
	 */
1191
	private $_aliases=array();
1192
	/**
1193
	 * @var array list of module configurations
1194
	 */
1195
	private $_modules=array();
1196
	/**
1197
	 * @var array list of service configurations
1198
	 */
1199
	private $_services=array();
1200
	/**
1201
	 * @var array list of parameters
1202
	 */
1203
	private $_parameters=array();
1204
	/**
1205
	 * @var array list of included configurations
1206
	 */
1207
	private $_includes=array();
1208
	/**
1209
	 * @var boolean whether this configuration contains actual stuff
1210
	 */
1211
	private $_empty=true;
1212
 
1213
	/**
1214
	 * Parses the application configuration file.
1215
	 * @param string configuration file name
1216
	 * @throws TConfigurationException if there is any parsing error
1217
	 */
1218
	public function loadFromFile($fname)
1219
	{
1220
		$dom=new TXmlDocument;
1221
		$dom->loadFromFile($fname);
1222
		$this->loadFromXml($dom,dirname($fname));
1223
	}
1224
 
1225
	/**
1226
	 * @return boolean whether this configuration contains actual stuff
1227
	 */
1228
	public function getIsEmpty()
1229
	{
1230
		return $this->_empty;
1231
	}
1232
 
1233
	/**
1234
	 * Parses the application configuration given in terms of a TXmlElement.
1235
	 * @param TXmlElement the XML element
1236
	 * @param string the context path (for specifying relative paths)
1237
	 */
1238
	public function loadFromXml($dom,$configPath)
1239
	{
1240
		// application properties
1241
		foreach($dom->getAttributes() as $name=>$value)
1242
		{
1243
			$this->_properties[$name]=$value;
1244
			$this->_empty=false;
1245
		}
1246
 
1247
		foreach($dom->getElements() as $element)
1248
		{
1249
			switch($element->getTagName())
1250
			{
1251
				case 'paths':
1252
					$this->loadPathsXml($element,$configPath);
1253
					break;
1254
				case 'modules':
1255
					$this->loadModulesXml($element,$configPath);
1256
					break;
1257
				case 'services':
1258
					$this->loadServicesXml($element,$configPath);
1259
					break;
1260
				case 'parameters':
1261
					$this->loadParametersXml($element,$configPath);
1262
					break;
1263
				case 'include':
1264
					$this->loadExternalXml($element,$configPath);
1265
					break;
1266
				default:
1267
					//throw new TConfigurationException('appconfig_tag_invalid',$element->getTagName());
1268
					break;
1269
			}
1270
		}
1271
	}
1272
 
1273
	/**
1274
	 * Loads the paths XML node.
1275
	 * @param TXmlElement the paths XML node
1276
	 * @param string the context path (for specifying relative paths)
1277
	 */
1278
	protected function loadPathsXml($pathsNode,$configPath)
1279
	{
1280
		foreach($pathsNode->getElements() as $element)
1281
		{
1282
			switch($element->getTagName())
1283
			{
1284
				case 'alias':
1285
				{
1286
					if(($id=$element->getAttribute('id'))!==null && ($path=$element->getAttribute('path'))!==null)
1287
					{
1288
						$path=str_replace('\\','/',$path);
1289
						if(preg_match('/^\\/|.:\\/|.:\\\\/',$path))	// if absolute path
1290
							$p=realpath($path);
1291
						else
1292
							$p=realpath($configPath.DIRECTORY_SEPARATOR.$path);
1293
						if($p===false || !is_dir($p))
1294
							throw new TConfigurationException('appconfig_aliaspath_invalid',$id,$path);
1295
						if(isset($this->_aliases[$id]))
1296
							throw new TConfigurationException('appconfig_alias_redefined',$id);
1297
						$this->_aliases[$id]=$p;
1298
					}
1299
					else
1300
						throw new TConfigurationException('appconfig_alias_invalid');
1301
					$this->_empty=false;
1302
					break;
1303
				}
1304
				case 'using':
1305
				{
1306
					if(($namespace=$element->getAttribute('namespace'))!==null)
1307
						$this->_usings[]=$namespace;
1308
					else
1309
						throw new TConfigurationException('appconfig_using_invalid');
1310
					$this->_empty=false;
1311
					break;
1312
				}
1313
				default:
1314
					throw new TConfigurationException('appconfig_paths_invalid',$tagName);
1315
			}
1316
		}
1317
	}
1318
 
1319
	/**
1320
	 * Loads the modules XML node.
1321
	 * @param TXmlElement the modules XML node
1322
	 * @param string the context path (for specifying relative paths)
1323
	 */
1324
	protected function loadModulesXml($modulesNode,$configPath)
1325
	{
1326
		foreach($modulesNode->getElements() as $element)
1327
		{
1328
			if($element->getTagName()==='module')
1329
			{
1330
				$properties=$element->getAttributes();
1331
				$id=$properties->itemAt('id');
1332
				$type=$properties->remove('class');
1333
				if($type===null)
1334
					throw new TConfigurationException('appconfig_moduletype_required',$id);
1335
				$element->setParent(null);
1336
				if($id===null)
1337
					$this->_modules[]=array($type,$properties->toArray(),$element);
1338
				else
1339
					$this->_modules[$id]=array($type,$properties->toArray(),$element);
1340
				$this->_empty=false;
1341
			}
1342
			else
1343
				throw new TConfigurationException('appconfig_modules_invalid',$element->getTagName());
1344
		}
1345
	}
1346
 
1347
	/**
1348
	 * Loads the services XML node.
1349
	 * @param TXmlElement the services XML node
1350
	 * @param string the context path (for specifying relative paths)
1351
	 */
1352
	protected function loadServicesXml($servicesNode,$configPath)
1353
	{
1354
		foreach($servicesNode->getElements() as $element)
1355
		{
1356
			if($element->getTagName()==='service')
1357
			{
1358
				$properties=$element->getAttributes();
1359
				if(($id=$properties->itemAt('id'))===null)
1360
					throw new TConfigurationException('appconfig_serviceid_required');
1361
				if(($type=$properties->remove('class'))===null)
1362
					throw new TConfigurationException('appconfig_servicetype_required',$id);
1363
				$element->setParent(null);
1364
				$this->_services[$id]=array($type,$properties->toArray(),$element);
1365
				$this->_empty=false;
1366
			}
1367
			else
1368
				throw new TConfigurationException('appconfig_services_invalid',$element->getTagName());
1369
		}
1370
	}
1371
 
1372
	/**
1373
	 * Loads the parameters XML node.
1374
	 * @param TXmlElement the parameters XML node
1375
	 * @param string the context path (for specifying relative paths)
1376
	 */
1377
	protected function loadParametersXml($parametersNode,$configPath)
1378
	{
1379
		foreach($parametersNode->getElements() as $element)
1380
		{
1381
			if($element->getTagName()==='parameter')
1382
			{
1383
				$properties=$element->getAttributes();
1384
				if(($id=$properties->remove('id'))===null)
1385
					throw new TConfigurationException('appconfig_parameterid_required');
1386
				if(($type=$properties->remove('class'))===null)
1387
				{
1388
					if(($value=$properties->remove('value'))===null)
1389
						$this->_parameters[$id]=$element;
1390
					else
1391
						$this->_parameters[$id]=$value;
1392
				}
1393
				else
1394
					$this->_parameters[$id]=array($type,$properties->toArray());
1395
				$this->_empty=false;
1396
			}
1397
			else
1398
				throw new TConfigurationException('appconfig_parameters_invalid',$element->getTagName());
1399
		}
1400
	}
1401
 
1402
	/**
1403
	 * Loads the external XML configurations.
1404
	 * @param TXmlElement the application DOM element
1405
	 * @param string the context path (for specifying relative paths)
1406
	 */
1407
	protected function loadExternalXml($includeNode,$configPath)
1408
	{
1409
		if(($when=$includeNode->getAttribute('when'))===null)
1410
			$when=true;
1411
		if(($filePath=$includeNode->getAttribute('file'))===null)
1412
			throw new TConfigurationException('appconfig_includefile_required');
1413
		if(isset($this->_includes[$filePath]))
1414
			$this->_includes[$filePath]='('.$this->_includes[$filePath].') || ('.$when.')';
1415
		else
1416
			$this->_includes[$filePath]=$when;
1417
		$this->_empty=false;
1418
	}
1419
 
1420
	/**
1421
	 * Returns list of page initial property values.
1422
	 * Each array element represents a single property with the key
1423
	 * being the property name and the value the initial property value.
1424
	 * @return array list of page initial property values
1425
	 */
1426
	public function getProperties()
1427
	{
1428
		return $this->_properties;
1429
	}
1430
 
1431
	/**
1432
	 * Returns list of path alias definitions.
1433
	 * The definitions are aggregated (top-down) from configuration files along the path
1434
	 * to the specified page. Each array element represents a single alias definition,
1435
	 * with the key being the alias name and the value the absolute path.
1436
	 * @return array list of path alias definitions
1437
	 */
1438
	public function getAliases()
1439
	{
1440
		return $this->_aliases;
1441
	}
1442
 
1443
	/**
1444
	 * Returns list of namespaces to be used.
1445
	 * The namespaces are aggregated (top-down) from configuration files along the path
1446
	 * to the specified page. Each array element represents a single namespace usage,
1447
	 * with the value being the namespace to be used.
1448
	 * @return array list of namespaces to be used
1449
	 */
1450
	public function getUsings()
1451
	{
1452
		return $this->_usings;
1453
	}
1454
 
1455
	/**
1456
	 * Returns list of module configurations.
1457
	 * The module configurations are aggregated (top-down) from configuration files
1458
	 * along the path to the specified page. Each array element represents
1459
	 * a single module configuration, with the key being the module ID and
1460
	 * the value the module configuration. Each module configuration is
1461
	 * stored in terms of an array with the following content
1462
	 * ([0]=>module type, [1]=>module properties, [2]=>complete module configuration)
1463
	 * The module properties are an array of property values indexed by property names.
1464
	 * The complete module configuration is a TXmlElement object representing
1465
	 * the raw module configuration which may contain contents enclosed within
1466
	 * module tags.
1467
	 * @return array list of module configurations to be used
1468
	 */
1469
	public function getModules()
1470
	{
1471
		return $this->_modules;
1472
	}
1473
 
1474
	/**
1475
	 * @return array list of service configurations
1476
	 */
1477
	public function getServices()
1478
	{
1479
		return $this->_services;
1480
	}
1481
 
1482
	/**
1483
	 * Returns list of parameter definitions.
1484
	 * The parameter definitions are aggregated (top-down) from configuration files
1485
	 * along the path to the specified page. Each array element represents
1486
	 * a single parameter definition, with the key being the parameter ID and
1487
	 * the value the parameter definition. A parameter definition can be either
1488
	 * a string representing a string-typed parameter, or an array.
1489
	 * The latter defines a component-typed parameter whose format is as follows,
1490
	 * ([0]=>component type, [1]=>component properties)
1491
	 * The component properties are an array of property values indexed by property names.
1492
	 * @return array list of parameter definitions to be used
1493
	 */
1494
	public function getParameters()
1495
	{
1496
		return $this->_parameters;
1497
	}
1498
 
1499
	/**
1500
	 * @return array list of external configuration files. Each element is like $filePath=>$condition
1501
	 */
1502
	public function getExternalConfigurations()
1503
	{
1504
		return $this->_includes;
1505
	}
1506
}
1507
 
1508
/**
1509
 * TApplicationStatePersister class.
1510
 * TApplicationStatePersister provides a file-based persistent storage
1511
 * for application state. Application state, when serialized, is stored
1512
 * in a file named 'global.cache' under the 'runtime' directory of the application.
1513
 * Cache will be exploited if it is enabled.
1514
 *
1515
 * @author Qiang Xue <qiang.xue@gmail.com>
1516
 * @version $Id: TApplication.php 2578 2008-12-01 22:50:15Z carlgmathisen $
1517
 * @package System
1518
 * @since 3.0
1519
 */
1520
class TApplicationStatePersister extends TModule implements IStatePersister
1521
{
1522
	/**
1523
	 * Name of the value stored in cache
1524
	 */
1525
	const CACHE_NAME='prado:appstate';
1526
 
1527
	/**
1528
	 * Initializes module.
1529
	 * @param TXmlElement module configuration (may be null)
1530
	 */
1531
	public function init($config)
1532
	{
1533
		$this->getApplication()->setApplicationStatePersister($this);
1534
	}
1535
 
1536
	/**
1537
	 * @return string the file path storing the application state
1538
	 */
1539
	protected function getStateFilePath()
1540
	{
1541
		return $this->getApplication()->getRuntimePath().'/global.cache';
1542
	}
1543
 
1544
	/**
1545
	 * Loads application state from persistent storage.
1546
	 * @return mixed application state
1547
	 */
1548
	public function load()
1549
	{
1550
		if(($cache=$this->getApplication()->getCache())!==null && ($value=$cache->get(self::CACHE_NAME))!==false)
1551
			return unserialize($value);
1552
		else
1553
		{
1554
			if(($content=@file_get_contents($this->getStateFilePath()))!==false)
1555
				return unserialize($content);
1556
			else
1557
				return null;
1558
		}
1559
	}
1560
 
1561
	/**
1562
	 * Saves application state in persistent storage.
1563
	 * @param mixed application state
1564
	 */
1565
	public function save($state)
1566
	{
1567
		$content=serialize($state);
1568
		$saveFile=true;
1569
		if(($cache=$this->getApplication()->getCache())!==null)
1570
		{
1571
			if($cache->get(self::CACHE_NAME)===$content)
1572
				$saveFile=false;
1573
			else
1574
				$cache->set(self::CACHE_NAME,$content);
1575
		}
1576
		if($saveFile)
1577
		{
1578
			$fileName=$this->getStateFilePath();
1579
			file_put_contents($fileName,$content,LOCK_EX);
1580
		}
1581
	}
1582
 
1583
}