Test Failed
Branch master (206474)
by Fabio
18:24
created

TApplication   F

Complexity

Total Complexity 146

Size/Duplication

Total Lines 1148
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 18

Importance

Changes 0
Metric Value
dl 0
loc 1148
rs 0.6314
c 0
b 0
f 0
wmc 146
lcom 1
cbo 18

74 Methods

Rating   Name   Duplication   Size   Complexity  
A clearGlobalState() 0 5 1
A loadGlobals() 0 4 1
A __construct() 0 18 2
D resolvePaths() 0 34 10
B run() 0 24 5
A completeRequest() 0 4 1
A getRequestCompleted() 0 4 1
A getGlobalState() 0 4 2
A setGlobalState() 0 12 3
A saveGlobals() 0 7 2
A getID() 0 4 1
A setID() 0 4 1
A getPageServiceID() 0 4 1
A setPageServiceID() 0 4 1
A getUniqueID() 0 4 1
A getMode() 0 4 1
A setMode() 0 4 1
A getBasePath() 0 4 1
A setBasePath() 0 4 1
A getConfigurationFile() 0 4 1
A setConfigurationFile() 0 4 1
A getConfigurationType() 0 4 1
A setConfigurationType() 0 4 1
A getConfigurationFileExt() 0 13 3
A getConfigurationFileName() 0 14 3
A getRuntimePath() 0 4 1
A setRuntimePath() 0 9 2
A getService() 0 4 1
A setService() 0 4 1
A setModule() 0 8 2
A getModule() 0 14 3
A getModules() 0 4 1
A getParameters() 0 4 1
A getRequest() 0 8 2
A setRequest() 0 4 1
A getResponse() 0 8 2
A setResponse() 0 4 1
A getSession() 0 8 2
A setSession() 0 4 1
A getErrorHandler() 0 8 2
A setErrorHandler() 0 4 1
A getSecurityManager() 0 8 2
A setSecurityManager() 0 4 1
A getAssetManager() 0 8 2
A setAssetManager() 0 4 1
A getApplicationStatePersister() 0 8 2
A setApplicationStatePersister() 0 4 1
A getCache() 0 4 1
A setCache() 0 4 1
A getUser() 0 4 1
A setUser() 0 4 1
A getGlobalization() 0 8 3
A setGlobalization() 0 4 1
A getAuthorizationRules() 0 7 2
A getApplicationConfigurationClass() 0 4 1
B internalLoadModule() 0 23 6
F applyConfiguration() 0 74 20
B initApplication() 0 24 6
C startService() 0 33 7
A onError() 0 6 1
A onBeginRequest() 0 4 1
A onAuthentication() 0 4 1
A onAuthenticationComplete() 0 4 1
A onAuthorization() 0 4 1
A onAuthorizationComplete() 0 4 1
A onLoadState() 0 5 1
A onLoadStateComplete() 0 4 1
A onPreRunService() 0 4 1
A runService() 0 6 2
A onSaveState() 0 5 1
A onSaveStateComplete() 0 4 1
A onPreFlushOutput() 0 4 1
A flushOutput() 0 4 1
A onEndRequest() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like TApplication often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TApplication, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * TApplication class file
4
 *
5
 * @author Qiang Xue <[email protected]>
6
 * @link https://github.com/pradosoft/prado
7
 * @copyright Copyright &copy; 2005-2016 The PRADO Group
8
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
9
 * @package Prado
10
 */
11
12
namespace Prado;
13
14
use Prado\Exceptions\TErrorHandler;
15
use Prado\Exceptions\THttpException;
16
use Prado\Exceptions\TConfigurationException;
17
use Prado\I18N\TGlobalization;
18
use Prado\Security\TSecurityManager;
19
use Prado\Web\TAssetManager;
20
use Prado\Web\THttpRequest;
21
use Prado\Web\THttpResponse;
22
use Prado\Web\THttpSession;
23
use Prado\Util\TLogger;
24
25
/**
26
 * TApplication class.
27
 *
28
 * TApplication coordinates modules and services, and serves as a configuration
29
 * context for all Prado components.
30
 *
31
 * TApplication uses a configuration file to specify the settings of
32
 * the application, the modules, the services, the parameters, and so on.
33
 *
34
 * TApplication adopts a modular structure. A TApplication instance is a composition
35
 * of multiple modules. A module is an instance of class implementing
36
 * {@link IModule} interface. Each module accomplishes certain functionalities
37
 * that are shared by all Prado components in an application.
38
 * There are default modules and user-defined modules. The latter offers extreme
39
 * flexibility of extending TApplication in a plug-and-play fashion.
40
 * Modules cooperate with each other to serve a user request by following
41
 * a sequence of lifecycles predefined in TApplication.
42
 *
43
 * TApplication has four modes that can be changed by setting {@link setMode Mode}
44
 * property (in the application configuration file).
45
 * - <b>Off</b> mode will prevent the application from serving user requests.
46
 * - <b>Debug</b> mode is mainly used during application development. It ensures
47
 *   the cache is always up-to-date if caching is enabled. It also allows
48
 *   exceptions are displayed with rich context information if they occur.
49
 * - <b>Normal</b> mode is mainly used during production stage. Exception information
50
 *   will only be recorded in system error logs. The cache is ensured to be
51
 *   up-to-date if it is enabled.
52
 * - <b>Performance</b> mode is similar to <b>Normal</b> mode except that it
53
 *   does not ensure the cache is up-to-date.
54
 *
55
 * TApplication dispatches each user request to a particular service which
56
 * finishes the actual work for the request with the aid from the application
57
 * modules.
58
 *
59
 * TApplication maintains a lifecycle with the following stages:
60
 * - [construct] : construction of the application instance
61
 * - [initApplication] : load application configuration and instantiate modules and the requested service
62
 * - onBeginRequest : this event happens right after application initialization
63
 * - onAuthentication : this event happens when authentication is needed for the current request
64
 * - onAuthenticationComplete : this event happens right after the authentication is done for the current request
65
 * - onAuthorization : this event happens when authorization is needed for the current request
66
 * - onAuthorizationComplete : this event happens right after the authorization is done for the current request
67
 * - onLoadState : this event happens when application state needs to be loaded
68
 * - onLoadStateComplete : this event happens right after the application state is loaded
69
 * - onPreRunService : this event happens right before the requested service is to run
70
 * - runService : the requested service runs
71
 * - onSaveState : this event happens when application needs to save its state
72
 * - onSaveStateComplete : this event happens right after the application saves its state
73
 * - onPreFlushOutput : this event happens right before the application flushes output to client side.
74
 * - flushOutput : the application flushes output to client side.
75
 * - onEndRequest : this is the last stage a request is being completed
76
 * - [destruct] : destruction of the application instance
77
 * Modules and services can attach their methods to one or several of the above
78
 * events and do appropriate processing when the events are raised. By this way,
79
 * the application is able to coordinate the activities of modules and services
80
 * in the above order. To terminate an application before the whole lifecycle
81
 * completes, call {@link completeRequest}.
82
 *
83
 * Examples:
84
 * - Create and run a Prado application:
85
 * <code>
86
 * $application=new TApplication($configFile);
87
 * $application->run();
88
 * </code>
89
 *
90
 * @author Qiang Xue <[email protected]>
91
 * @package Prado
92
 * @since 3.0
93
 */
94
class TApplication extends \Prado\TComponent
95
{
96
	/**
97
	 * Page service ID
98
	 */
99
	const PAGE_SERVICE_ID = 'page';
100
	/**
101
	 * Application configuration file name
102
	 */
103
	const CONFIG_FILE_XML = 'application.xml';
104
	/**
105
	 * File extension for external config files
106
	 */
107
	const CONFIG_FILE_EXT_XML = '.xml';
108
	/**
109
	 * Configuration file type, application.xml and config.xml
110
	 */
111
	const CONFIG_TYPE_XML = 'xml';
112
	/**
113
	 * Application configuration file name
114
	 */
115
	const CONFIG_FILE_PHP = 'application.php';
116
	/**
117
	 * File extension for external config files
118
	 */
119
	const CONFIG_FILE_EXT_PHP = '.php';
120
	/**
121
	 * Configuration file type, application.php and config.php
122
	 */
123
	const CONFIG_TYPE_PHP = 'php';
124
	/**
125
	 * Runtime directory name
126
	 */
127
	const RUNTIME_PATH = 'runtime';
128
	/**
129
	 * Config cache file
130
	 */
131
	const CONFIGCACHE_FILE = 'config.cache';
132
	/**
133
	 * Global data file
134
	 */
135
	const GLOBAL_FILE = 'global.cache';
136
137
	/**
138
	 * @var array list of events that define application lifecycles
139
	 */
140
	private static $_steps = [
141
		'onBeginRequest',
142
		'onLoadState',
143
		'onLoadStateComplete',
144
		'onAuthentication',
145
		'onAuthenticationComplete',
146
		'onAuthorization',
147
		'onAuthorizationComplete',
148
		'onPreRunService',
149
		'runService',
150
		'onSaveState',
151
		'onSaveStateComplete',
152
		'onPreFlushOutput',
153
		'flushOutput'
154
	];
155
156
	/**
157
	 * @var string application ID
158
	 */
159
	private $_id;
160
	/**
161
	 * @var string unique application ID
162
	 */
163
	private $_uniqueID;
164
	/**
165
	 * @var bool whether the request is completed
166
	 */
167
	private $_requestCompleted = false;
168
	/**
169
	 * @var int application state
170
	 */
171
	private $_step;
172
	/**
173
	 * @var array available services and their configurations indexed by service IDs
174
	 */
175
	private $_services;
176
	/**
177
	 * @var IService current service instance
178
	 */
179
	private $_service;
180
	/**
181
	 * @var array list of loaded application modules
182
	 */
183
	private $_modules = [];
184
	/**
185
	 * @var array list of application modules yet to be loaded
186
	 */
187
	private $_lazyModules = [];
188
	/**
189
	 * @var \Prado\Collections\TMap list of application parameters
190
	 */
191
	private $_parameters;
192
	/**
193
	 * @var string configuration file
194
	 */
195
	private $_configFile;
196
	/**
197
	 * @var string configuration file extension
198
	 */
199
	private $_configFileExt;
200
	/**
201
	 * @var string configuration type
202
	 */
203
	private $_configType;
204
	/**
205
	 * @var string application base path
206
	 */
207
	private $_basePath;
208
	/**
209
	 * @var string directory storing application state
210
	 */
211
	private $_runtimePath;
212
	/**
213
	 * @var bool if any global state is changed during the current request
214
	 */
215
	private $_stateChanged = false;
216
	/**
217
	 * @var array global variables (persistent across sessions, requests)
218
	 */
219
	private $_globals = [];
220
	/**
221
	 * @var string cache file
222
	 */
223
	private $_cacheFile;
224
	/**
225
	 * @var TErrorHandler error handler module
226
	 */
227
	private $_errorHandler;
228
	/**
229
	 * @var THttpRequest request module
230
	 */
231
	private $_request;
232
	/**
233
	 * @var THttpResponse response module
234
	 */
235
	private $_response;
236
	/**
237
	 * @var THttpSession session module, could be null
238
	 */
239
	private $_session;
240
	/**
241
	 * @var ICache cache module, could be null
242
	 */
243
	private $_cache;
244
	/**
245
	 * @var IStatePersister application state persister
246
	 */
247
	private $_statePersister;
248
	/**
249
	 * @var IUser user instance, could be null
250
	 */
251
	private $_user;
252
	/**
253
	 * @var TGlobalization module, could be null
254
	 */
255
	private $_globalization;
256
	/**
257
	 * @var TSecurityManager security manager module
258
	 */
259
	private $_security;
260
	/**
261
	 * @var TAssetManager asset manager module
262
	 */
263
	private $_assetManager;
264
	/**
265
	 * @var TAuthorizationRuleCollection collection of authorization rules
266
	 */
267
	private $_authRules;
268
	/**
269
	 * @var TApplicationMode application mode
270
	 */
271
	private $_mode = TApplicationMode::Debug;
272
273
	/**
274
	 * @var string Customizable page service ID
275
	 */
276
	private $_pageServiceID = self::PAGE_SERVICE_ID;
277
278
	/**
279
	 * Constructor.
280
	 * Sets application base path and initializes the application singleton.
281
	 * Application base path refers to the root directory storing application
282
	 * data and code not directly accessible by Web users.
283
	 * By default, the base path is assumed to be the <b>protected</b>
284
	 * directory under the directory containing the current running script.
285
	 * @param string $basePath application base path or configuration file path.
286
	 *        If the parameter is a file, it is assumed to be the application
287
	 *        configuration file, and the directory containing the file is treated
288
	 *        as the application base path.
289
	 *        If it is a directory, it is assumed to be the application base path,
290
	 *        and within that directory, a file named <b>application.xml</b>
291
	 *        will be looked for. If found, the file is considered as the application
292
	 *        configuration file.
293
	 * @param bool $cacheConfig whether to cache application configuration. Defaults to true.
294
	 * @param int $configType configuration type. Defaults to CONFIG_TYPE_XML.
295
	 * @throws TConfigurationException if configuration file cannot be read or the runtime path is invalid.
296
	 */
297
	public function __construct($basePath = 'protected', $cacheConfig = true, $configType = self::CONFIG_TYPE_XML)
298
	{
299
		// register application as a singleton
300
		Prado::setApplication($this);
301
		$this->setConfigurationType($configType);
302
		$this->resolvePaths($basePath);
303
304
		if ($cacheConfig) {
305
			$this->_cacheFile = $this->_runtimePath . DIRECTORY_SEPARATOR . self::CONFIGCACHE_FILE;
306
		}
307
308
		// generates unique ID by hashing the runtime path
309
		$this->_uniqueID = md5($this->_runtimePath);
310
		$this->_parameters = new \Prado\Collections\TMap;
311
		$this->_services = [$this->getPageServiceID() => ['TPageService', [], null]];
312
313
		Prado::setPathOfAlias('Application', $this->_basePath);
314
	}
315
316
	/**
317
	 * Resolves application-relevant paths.
318
	 * This method is invoked by the application constructor
319
	 * to determine the application configuration file,
320
	 * application root path and the runtime path.
321
	 * @param string $basePath the application root path or the application configuration file
322
	 * @see setBasePath
323
	 * @see setRuntimePath
324
	 * @see setConfigurationFile
325
	 */
326
	protected function resolvePaths($basePath)
327
	{
328
		// determine configuration path and file
329
		if (empty($basePath) || ($basePath = realpath($basePath)) === false) {
330
			throw new TConfigurationException('application_basepath_invalid', $basePath);
331
		}
332
		if (is_dir($basePath) && is_file($basePath . DIRECTORY_SEPARATOR . $this->getConfigurationFileName())) {
333
			$configFile = $basePath . DIRECTORY_SEPARATOR . $this->getConfigurationFileName();
334
		} elseif (is_file($basePath)) {
335
			$configFile = $basePath;
336
			$basePath = dirname($configFile);
337
		} else {
338
			$configFile = null;
339
		}
340
341
		// determine runtime path
342
		$runtimePath = $basePath . DIRECTORY_SEPARATOR . self::RUNTIME_PATH;
343
		if (is_writable($runtimePath)) {
344
			if ($configFile !== null) {
345
				$runtimePath .= DIRECTORY_SEPARATOR . basename($configFile) . '-' . Prado::getVersion();
346
				if (!is_dir($runtimePath)) {
347
					if (@mkdir($runtimePath) === false) {
348
						throw new TConfigurationException('application_runtimepath_failed', $runtimePath);
349
					}
350
					@chmod($runtimePath, PRADO_CHMOD); //make it deletable
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
351
				}
352
				$this->setConfigurationFile($configFile);
353
			}
354
			$this->setBasePath($basePath);
355
			$this->setRuntimePath($runtimePath);
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
			$this->initApplication();
370
			$n = count(self::$_steps);
371
			$this->_step = 0;
372
			$this->_requestCompleted = false;
373
			while ($this->_step < $n) {
374
				if ($this->_mode === TApplicationMode::Off) {
375
					throw new THttpException(503, 'application_unavailable');
376
				}
377
				if ($this->_requestCompleted) {
378
					break;
379
				}
380
				$method = self::$_steps[$this->_step];
381
				Prado::trace("Executing $method()", 'Prado\TApplication');
382
				$this->$method();
383
				$this->_step++;
384
			}
385
		} catch (\Exception $e) {
386
			$this->onError($e);
387
		}
388
		$this->onEndRequest();
389
	}
390
391
	/**
392
	 * Completes current request processing.
393
	 * This method can be used to exit the application lifecycles after finishing
394
	 * the current cycle.
395
	 */
396
	public function completeRequest()
397
	{
398
		$this->_requestCompleted = true;
399
	}
400
401
	/**
402
	 * @return bool whether the current request is processed.
403
	 */
404
	public function getRequestCompleted()
405
	{
406
		return $this->_requestCompleted;
407
	}
408
409
	/**
410
	 * Returns a global value.
411
	 *
412
	 * A global value is one that is persistent across users sessions and requests.
413
	 * @param string $key the name of the value to be returned
414
	 * @param mixed $defaultValue the default value. If $key is not found, $defaultValue will be returned
415
	 * @return mixed the global value corresponding to $key
416
	 */
417
	public function getGlobalState($key, $defaultValue = null)
418
	{
419
		return isset($this->_globals[$key]) ? $this->_globals[$key] : $defaultValue;
420
	}
421
422
	/**
423
	 * Sets a global value.
424
	 *
425
	 * A global value is one that is persistent across users sessions and requests.
426
	 * Make sure that the value is serializable and unserializable.
427
	 * @param string $key the name of the value to be set
428
	 * @param mixed $value the global value to be set
429
	 * @param null|mixed $defaultValue the default value. If $key is not found, $defaultValue will be returned
430
	 * @param bool $forceSave wheter to force an immediate GlobalState save. defaults to false
431
	 */
432
	public function setGlobalState($key, $value, $defaultValue = null, $forceSave = false)
433
	{
434
		$this->_stateChanged = true;
435
		if ($value === $defaultValue) {
436
			unset($this->_globals[$key]);
437
		} else {
438
			$this->_globals[$key] = $value;
439
		}
440
		if ($forceSave) {
441
			$this->saveGlobals();
442
		}
443
	}
444
445
	/**
446
	 * Clears a global value.
447
	 *
448
	 * The value cleared will no longer be available in this request and the following requests.
449
	 * @param string $key the name of the value to be cleared
450
	 */
451
	public function clearGlobalState($key)
452
	{
453
		$this->_stateChanged = true;
454
		unset($this->_globals[$key]);
455
	}
456
457
	/**
458
	 * Loads global values from persistent storage.
459
	 * This method is invoked when {@link onLoadState OnLoadState} event is raised.
460
	 * After this method, values that are stored in previous requests become
461
	 * available to the current request via {@link getGlobalState}.
462
	 */
463
	protected function loadGlobals()
464
	{
465
		$this->_globals = $this->getApplicationStatePersister()->load();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getApplicationStatePersister()->load() of type * is incompatible with the declared type array of property $_globals.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
466
	}
467
468
	/**
469
	 * Saves global values into persistent storage.
470
	 * This method is invoked when {@link onSaveState OnSaveState} event is raised.
471
	 */
472
	protected function saveGlobals()
473
	{
474
		if ($this->_stateChanged) {
475
			$this->_stateChanged = false;
476
			$this->getApplicationStatePersister()->save($this->_globals);
477
		}
478
	}
479
480
	/**
481
	 * @return string application ID
482
	 */
483
	public function getID()
484
	{
485
		return $this->_id;
486
	}
487
488
	/**
489
	 * @param string $value application ID
490
	 */
491
	public function setID($value)
492
	{
493
		$this->_id = $value;
494
	}
495
496
	/**
497
	 * @return string page service ID
498
	 */
499
	public function getPageServiceID()
500
	{
501
		return $this->_pageServiceID;
502
	}
503
504
	/**
505
	 * @param string $value page service ID
506
	 */
507
	public function setPageServiceID($value)
508
	{
509
		$this->_pageServiceID = $value;
510
	}
511
512
	/**
513
	 * @return string an ID that uniquely identifies this Prado application from the others
514
	 */
515
	public function getUniqueID()
516
	{
517
		return $this->_uniqueID;
518
	}
519
520
	/**
521
	 * @return TApplicationMode application mode. Defaults to TApplicationMode::Debug.
522
	 */
523
	public function getMode()
524
	{
525
		return $this->_mode;
526
	}
527
528
	/**
529
	 * @param TApplicationMode $value application mode
530
	 */
531
	public function setMode($value)
532
	{
533
		$this->_mode = TPropertyValue::ensureEnum($value, '\Prado\TApplicationMode');
0 ignored issues
show
Documentation Bug introduced by
It seems like \Prado\TPropertyValue::e...ado\\TApplicationMode') of type string is incompatible with the declared type object<Prado\TApplicationMode> of property $_mode.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
534
	}
535
536
	/**
537
	 * @return string the directory containing the application configuration file (absolute path)
538
	 */
539
	public function getBasePath()
540
	{
541
		return $this->_basePath;
542
	}
543
544
	/**
545
	 * @param string $value the directory containing the application configuration file
546
	 */
547
	public function setBasePath($value)
548
	{
549
		$this->_basePath = $value;
550
	}
551
552
	/**
553
	 * @return string the application configuration file (absolute path)
554
	 */
555
	public function getConfigurationFile()
556
	{
557
		return $this->_configFile;
558
	}
559
560
	/**
561
	 * @param string $value the application configuration file (absolute path)
562
	 */
563
	public function setConfigurationFile($value)
564
	{
565
		$this->_configFile = $value;
566
	}
567
568
	/**
569
	 * @return string the application configuration file (absolute path)
570
	 */
571
	public function getConfigurationType()
572
	{
573
		return $this->_configType;
574
	}
575
576
	/**
577
	 * @param string $value the application configuration type. 'xml' and 'php' are valid values
578
	 */
579
	public function setConfigurationType($value)
580
	{
581
		$this->_configType = $value;
582
	}
583
584
	/**
585
	 * @return string the application configuration type. default is 'xml'
586
	 */
587
	public function getConfigurationFileExt()
588
	{
589
		if ($this->_configFileExt === null) {
590
			switch ($this->_configType) {
591
				case TApplication::CONFIG_TYPE_PHP:
592
					$this->_configFileExt = TApplication::CONFIG_FILE_EXT_PHP;
593
					break;
594
				default:
595
					$this->_configFileExt = TApplication::CONFIG_FILE_EXT_XML;
596
			}
597
		}
598
		return $this->_configFileExt;
599
	}
600
601
	/**
602
	 * @return string the default configuration file name
603
	 */
604
	public function getConfigurationFileName()
605
	{
606
		static $fileName;
607
		if ($fileName == null) {
608
			switch ($this->_configType) {
609
				case TApplication::CONFIG_TYPE_PHP:
610
					$fileName = TApplication::CONFIG_FILE_PHP;
611
					break;
612
				default:
613
					$fileName = TApplication::CONFIG_FILE_XML;
614
			}
615
		}
616
		return $fileName;
617
	}
618
619
	/**
620
	 * @return string the directory storing cache data and application-level persistent data. (absolute path)
621
	 */
622
	public function getRuntimePath()
623
	{
624
		return $this->_runtimePath;
625
	}
626
627
	/**
628
	 * @param string $value the directory storing cache data and application-level persistent data. (absolute path)
629
	 */
630
	public function setRuntimePath($value)
631
	{
632
		$this->_runtimePath = $value;
633
		if ($this->_cacheFile) {
634
			$this->_cacheFile = $this->_runtimePath . DIRECTORY_SEPARATOR . self::CONFIGCACHE_FILE;
635
		}
636
		// generates unique ID by hashing the runtime path
637
		$this->_uniqueID = md5($this->_runtimePath);
638
	}
639
640
	/**
641
	 * @return IService the currently requested service
642
	 */
643
	public function getService()
644
	{
645
		return $this->_service;
646
	}
647
648
	/**
649
	 * @param IService $value the currently requested service
650
	 */
651
	public function setService($value)
652
	{
653
		$this->_service = $value;
654
	}
655
656
	/**
657
	 * Adds a module to application.
658
	 * Note, this method does not do module initialization.
659
	 * @param string $id ID of the module
660
	 * @param null|IModule $module module object or null if the module has not been loaded yet
661
	 */
662
	public function setModule($id, IModule $module = null)
663
	{
664
		if (isset($this->_modules[$id])) {
665
			throw new TConfigurationException('application_moduleid_duplicated', $id);
666
		} else {
667
			$this->_modules[$id] = $module;
668
		}
669
	}
670
671
	/**
672
	 * @param mixed $id
673
	 * @return IModule the module with the specified ID, null if not found
674
	 */
675
	public function getModule($id)
676
	{
677
		if (!array_key_exists($id, $this->_modules)) {
678
			return null;
679
		}
680
681
		// force loading of a lazy module
682
		if ($this->_modules[$id] === null) {
683
			$module = $this->internalLoadModule($id, true);
684
			$module[0]->init($module[1]);
685
		}
686
687
		return $this->_modules[$id];
688
	}
689
690
	/**
691
	 * Returns a list of application modules indexed by module IDs.
692
	 * Modules that have not been loaded yet are returned as null objects.
693
	 * @return array list of loaded application modules, indexed by module IDs
694
	 */
695
	public function getModules()
696
	{
697
		return $this->_modules;
698
	}
699
700
	/**
701
	 * Returns the list of application parameters.
702
	 * Since the parameters are returned as a {@link \Prado\Collections\TMap} object, you may use
703
	 * the returned result to access, add or remove individual parameters.
704
	 * @return \Prado\Collections\TMap the list of application parameters
705
	 */
706
	public function getParameters()
707
	{
708
		return $this->_parameters;
709
	}
710
711
	/**
712
	 * @return THttpRequest the request module
713
	 */
714
	public function getRequest()
715
	{
716
		if (!$this->_request) {
717
			$this->_request = new \Prado\Web\THttpRequest;
718
			$this->_request->init(null);
719
		}
720
		return $this->_request;
721
	}
722
723
	/**
724
	 * @param THttpRequest $request the request module
725
	 */
726
	public function setRequest(THttpRequest $request)
727
	{
728
		$this->_request = $request;
729
	}
730
731
	/**
732
	 * @return THttpResponse the response module
733
	 */
734
	public function getResponse()
735
	{
736
		if (!$this->_response) {
737
			$this->_response = new THttpResponse;
738
			$this->_response->init(null);
739
		}
740
		return $this->_response;
741
	}
742
743
	/**
744
	 * @param THttpRequest $response the request module
745
	 */
746
	public function setResponse(THttpResponse $response)
747
	{
748
		$this->_response = $response;
749
	}
750
751
	/**
752
	 * @return THttpSession the session module, null if session module is not installed
753
	 */
754
	public function getSession()
755
	{
756
		if (!$this->_session) {
757
			$this->_session = new THttpSession;
758
			$this->_session->init(null);
759
		}
760
		return $this->_session;
761
	}
762
763
	/**
764
	 * @param THttpSession $session the session module
765
	 */
766
	public function setSession(THttpSession $session)
767
	{
768
		$this->_session = $session;
769
	}
770
771
	/**
772
	 * @return TErrorHandler the error handler module
773
	 */
774
	public function getErrorHandler()
775
	{
776
		if (!$this->_errorHandler) {
777
			$this->_errorHandler = new TErrorHandler;
778
			$this->_errorHandler->init(null);
779
		}
780
		return $this->_errorHandler;
781
	}
782
783
	/**
784
	 * @param TErrorHandler $handler the error handler module
785
	 */
786
	public function setErrorHandler(TErrorHandler $handler)
787
	{
788
		$this->_errorHandler = $handler;
789
	}
790
791
	/**
792
	 * @return TSecurityManager the security manager module
793
	 */
794
	public function getSecurityManager()
795
	{
796
		if (!$this->_security) {
797
			$this->_security = new TSecurityManager;
798
			$this->_security->init(null);
799
		}
800
		return $this->_security;
801
	}
802
803
	/**
804
	 * @param TSecurityManager $sm the security manager module
805
	 */
806
	public function setSecurityManager(TSecurityManager $sm)
807
	{
808
		$this->_security = $sm;
809
	}
810
811
	/**
812
	 * @return TAssetManager asset manager
813
	 */
814
	public function getAssetManager()
815
	{
816
		if (!$this->_assetManager) {
817
			$this->_assetManager = new TAssetManager;
818
			$this->_assetManager->init(null);
819
		}
820
		return $this->_assetManager;
821
	}
822
823
	/**
824
	 * @param TAssetManager $value asset manager
825
	 */
826
	public function setAssetManager(TAssetManager $value)
827
	{
828
		$this->_assetManager = $value;
829
	}
830
831
	/**
832
	 * @return IStatePersister application state persister
833
	 */
834
	public function getApplicationStatePersister()
835
	{
836
		if (!$this->_statePersister) {
837
			$this->_statePersister = new TApplicationStatePersister;
838
			$this->_statePersister->init(null);
839
		}
840
		return $this->_statePersister;
841
	}
842
843
	/**
844
	 * @param IStatePersister $persister application state persister
845
	 */
846
	public function setApplicationStatePersister(IStatePersister $persister)
847
	{
848
		$this->_statePersister = $persister;
849
	}
850
851
	/**
852
	 * @return ICache the cache module, null if cache module is not installed
853
	 */
854
	public function getCache()
855
	{
856
		return $this->_cache;
857
	}
858
859
	/**
860
	 * @param \Prado\Caching\ICache $cache the cache module
861
	 */
862
	public function setCache(\Prado\Caching\ICache $cache)
863
	{
864
		$this->_cache = $cache;
0 ignored issues
show
Documentation Bug introduced by
It seems like $cache of type object<Prado\Caching\ICache> is incompatible with the declared type object<Prado\ICache> of property $_cache.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
865
	}
866
867
	/**
868
	 * @return IUser the application user
869
	 */
870
	public function getUser()
871
	{
872
		return $this->_user;
873
	}
874
875
	/**
876
	 * @param \Prado\Security\IUser $user the application user
877
	 */
878
	public function setUser(\Prado\Security\IUser $user)
879
	{
880
		$this->_user = $user;
0 ignored issues
show
Documentation Bug introduced by
It seems like $user of type object<Prado\Security\IUser> is incompatible with the declared type object<Prado\IUser> of property $_user.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
881
	}
882
883
	/**
884
	 * @param bool $createIfNotExists whether to create globalization if it does not exist
885
	 * @return TGlobalization globalization module
886
	 */
887
	public function getGlobalization($createIfNotExists = true)
888
	{
889
		if ($this->_globalization === null && $createIfNotExists) {
890
			$this->_globalization = new TGlobalization;
891
			$this->_globalization->init(null);
892
		}
893
		return $this->_globalization;
894
	}
895
896
	/**
897
	 * @param \Prado\I18N\TGlobalization $glob globalization module
898
	 */
899
	public function setGlobalization(\Prado\I18N\TGlobalization $glob)
900
	{
901
		$this->_globalization = $glob;
902
	}
903
904
	/**
905
	 * @return TAuthorizationRuleCollection list of authorization rules for the current request
906
	 */
907
	public function getAuthorizationRules()
908
	{
909
		if ($this->_authRules === null) {
910
			$this->_authRules = new \Prado\Security\TAuthorizationRuleCollection;
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Prado\Security\TAuthorizationRuleCollection() of type object<Prado\Security\TA...rizationRuleCollection> is incompatible with the declared type object<Prado\TAuthorizationRuleCollection> of property $_authRules.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
911
		}
912
		return $this->_authRules;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->_authRules; of type Prado\Security\TAuthoriz...orizationRuleCollection adds the type Prado\Security\TAuthorizationRuleCollection to the return on line 912 which is incompatible with the return type documented by Prado\TApplication::getAuthorizationRules of type Prado\TAuthorizationRuleCollection.
Loading history...
913
	}
914
915
	protected function getApplicationConfigurationClass()
916
	{
917
		return '\Prado\TApplicationConfiguration';
918
	}
919
920
	protected function internalLoadModule($id, $force = false)
921
	{
922
		list($moduleClass, $initProperties, $configElement) = $this->_lazyModules[$id];
923
		if (isset($initProperties['lazy']) && $initProperties['lazy'] && !$force) {
924
			Prado::trace("Postponed loading of lazy module $id ({$moduleClass})", '\Prado\TApplication');
925
			$this->setModule($id, null);
926
			return null;
927
		}
928
929
		Prado::trace("Loading module $id ({$moduleClass})", '\Prado\TApplication');
930
		$module = Prado::createComponent($moduleClass);
931
		foreach ($initProperties as $name => $value) {
932
			if ($name === 'lazy') {
933
				continue;
934
			}
935
			$module->setSubProperty($name, $value);
936
		}
937
		$this->setModule($id, $module);
938
		// keep the key to avoid reuse of the old module id
939
		$this->_lazyModules[$id] = null;
940
941
		return [$module, $configElement];
942
	}
943
	/**
944
	 * Applies an application configuration.
945
	 * @param TApplicationConfiguration $config the configuration
946
	 * @param bool $withinService whether the configuration is specified within a service.
947
	 */
948
	public function applyConfiguration($config, $withinService = false)
949
	{
950
		if ($config->getIsEmpty()) {
951
			return;
952
		}
953
954
		// set path aliases and using namespaces
955
		foreach ($config->getAliases() as $alias => $path) {
956
			Prado::setPathOfAlias($alias, $path);
957
		}
958
		foreach ($config->getUsings() as $using) {
959
			Prado::using($using);
960
		}
961
962
		// set application properties
963
		if (!$withinService) {
964
			foreach ($config->getProperties() as $name => $value) {
965
				$this->setSubProperty($name, $value);
966
			}
967
		}
968
969
		if (empty($this->_services)) {
970
			$this->_services = [$this->getPageServiceID() => ['Prado\Web\Services\TPageService', [], null]];
971
		}
972
973
		// load parameters
974
		foreach ($config->getParameters() as $id => $parameter) {
975
			if (is_array($parameter)) {
976
				$component = Prado::createComponent($parameter[0]);
977
				foreach ($parameter[1] as $name => $value) {
978
					$component->setSubProperty($name, $value);
979
				}
980
				$this->_parameters->add($id, $component);
981
			} else {
982
				$this->_parameters->add($id, $parameter);
983
			}
984
		}
985
986
		// load and init modules specified in app config
987
		$modules = [];
988
		foreach ($config->getModules() as $id => $moduleConfig) {
989
			if (!is_string($id)) {
990
				$id = '_module' . count($this->_lazyModules);
991
			}
992
			$this->_lazyModules[$id] = $moduleConfig;
993
			if ($module = $this->internalLoadModule($id)) {
994
				$modules[] = $module;
995
			}
996
		}
997
		foreach ($modules as $module) {
998
			$module[0]->init($module[1]);
999
		}
1000
1001
		// load service
1002
		foreach ($config->getServices() as $serviceID => $serviceConfig) {
1003
			$this->_services[$serviceID] = $serviceConfig;
1004
		}
1005
1006
		// external configurations
1007
		foreach ($config->getExternalConfigurations() as $filePath => $condition) {
1008
			if ($condition !== true) {
1009
				$condition = $this->evaluateExpression($condition);
1010
			}
1011
			if ($condition) {
1012
				if (($path = Prado::getPathOfNamespace($filePath, $this->getConfigurationFileExt())) === null || !is_file($path)) {
1013
					throw new TConfigurationException('application_includefile_invalid', $filePath);
1014
				}
1015
				$cn = $this->getApplicationConfigurationClass();
1016
				$c = new $cn;
1017
				$c->loadFromFile($path);
1018
				$this->applyConfiguration($c, $withinService);
1019
			}
1020
		}
1021
	}
1022
1023
	/**
1024
	 * Loads configuration and initializes application.
1025
	 * Configuration file will be read and parsed (if a valid cached version exists,
1026
	 * it will be used instead). Then, modules are created and initialized;
1027
	 * Afterwards, the requested service is created and initialized.
1028
	 * @throws TConfigurationException if module is redefined of invalid type, or service not defined or of invalid type
1029
	 */
1030
	protected function initApplication()
1031
	{
1032
		Prado::trace('Initializing application', 'Prado\TApplication');
1033
1034
		if ($this->_configFile !== null) {
1035
			if ($this->_cacheFile === null || @filemtime($this->_cacheFile) < filemtime($this->_configFile)) {
1036
				$config = new TApplicationConfiguration;
1037
				$config->loadFromFile($this->_configFile);
1038
				if ($this->_cacheFile !== null) {
1039
					file_put_contents($this->_cacheFile, serialize($config), LOCK_EX);
1040
				}
1041
			} else {
1042
				$config = unserialize(file_get_contents($this->_cacheFile));
1043
			}
1044
1045
			$this->applyConfiguration($config, false);
1046
		}
1047
1048
		if (($serviceID = $this->getRequest()->resolveRequest(array_keys($this->_services))) === null) {
1049
			$serviceID = $this->getPageServiceID();
1050
		}
1051
1052
		$this->startService($serviceID);
1053
	}
1054
1055
	/**
1056
	 * Starts the specified service.
1057
	 * The service instance will be created. Its properties will be initialized
1058
	 * and the configurations will be applied, if any.
1059
	 * @param string $serviceID service ID
1060
	 */
1061
	public function startService($serviceID)
1062
	{
1063
		if (isset($this->_services[$serviceID])) {
1064
			list($serviceClass, $initProperties, $configElement) = $this->_services[$serviceID];
1065
			$service = Prado::createComponent($serviceClass);
1066
			if (!($service instanceof IService)) {
1067
				throw new THttpException(500, 'application_service_invalid', $serviceClass);
1068
			}
1069
			if (!$service->getEnabled()) {
1070
				throw new THttpException(500, 'application_service_unavailable', $serviceClass);
1071
			}
1072
			$service->setID($serviceID);
1073
			$this->setService($service);
1074
1075
			foreach ($initProperties as $name => $value) {
1076
				$service->setSubProperty($name, $value);
1077
			}
1078
1079
			if ($configElement !== null) {
1080
				$config = new TApplicationConfiguration;
1081
				if ($this->getConfigurationType() == self::CONFIG_TYPE_PHP) {
1082
					$config->loadFromPhp($configElement, $this->getBasePath());
1083
				} else {
1084
					$config->loadFromXml($configElement, $this->getBasePath());
1085
				}
1086
				$this->applyConfiguration($config, true);
1087
			}
1088
1089
			$service->init($configElement);
1090
		} else {
1091
			throw new THttpException(500, 'application_service_unknown', $serviceID);
1092
		}
1093
	}
1094
1095
	/**
1096
	 * Raises OnError event.
1097
	 * This method is invoked when an exception is raised during the lifecycles
1098
	 * of the application.
1099
	 * @param mixed $param event parameter
1100
	 */
1101
	public function onError($param)
1102
	{
1103
		Prado::log($param->getMessage(), TLogger::ERROR, 'Prado\TApplication');
1104
		$this->raiseEvent('OnError', $this, $param);
1105
		$this->getErrorHandler()->handleError($this, $param);
1106
	}
1107
1108
	/**
1109
	 * Raises OnBeginRequest event.
1110
	 * At the time when this method is invoked, application modules are loaded
1111
	 * and initialized, user request is resolved and the corresponding service
1112
	 * is loaded and initialized. The application is about to start processing
1113
	 * the user request.
1114
	 */
1115
	public function onBeginRequest()
1116
	{
1117
		$this->raiseEvent('OnBeginRequest', $this, null);
1118
	}
1119
1120
	/**
1121
	 * Raises OnAuthentication event.
1122
	 * This method is invoked when the user request needs to be authenticated.
1123
	 */
1124
	public function onAuthentication()
1125
	{
1126
		$this->raiseEvent('OnAuthentication', $this, null);
1127
	}
1128
1129
	/**
1130
	 * Raises OnAuthenticationComplete event.
1131
	 * This method is invoked right after the user request is authenticated.
1132
	 */
1133
	public function onAuthenticationComplete()
1134
	{
1135
		$this->raiseEvent('OnAuthenticationComplete', $this, null);
1136
	}
1137
1138
	/**
1139
	 * Raises OnAuthorization event.
1140
	 * This method is invoked when the user request needs to be authorized.
1141
	 */
1142
	public function onAuthorization()
1143
	{
1144
		$this->raiseEvent('OnAuthorization', $this, null);
1145
	}
1146
1147
	/**
1148
	 * Raises OnAuthorizationComplete event.
1149
	 * This method is invoked right after the user request is authorized.
1150
	 */
1151
	public function onAuthorizationComplete()
1152
	{
1153
		$this->raiseEvent('OnAuthorizationComplete', $this, null);
1154
	}
1155
1156
	/**
1157
	 * Raises OnLoadState event.
1158
	 * This method is invoked when the application needs to load state (probably stored in session).
1159
	 */
1160
	public function onLoadState()
1161
	{
1162
		$this->loadGlobals();
1163
		$this->raiseEvent('OnLoadState', $this, null);
1164
	}
1165
1166
	/**
1167
	 * Raises OnLoadStateComplete event.
1168
	 * This method is invoked right after the application state has been loaded.
1169
	 */
1170
	public function onLoadStateComplete()
1171
	{
1172
		$this->raiseEvent('OnLoadStateComplete', $this, null);
1173
	}
1174
1175
	/**
1176
	 * Raises OnPreRunService event.
1177
	 * This method is invoked right before the service is to be run.
1178
	 */
1179
	public function onPreRunService()
1180
	{
1181
		$this->raiseEvent('OnPreRunService', $this, null);
1182
	}
1183
1184
	/**
1185
	 * Runs the requested service.
1186
	 */
1187
	public function runService()
1188
	{
1189
		if ($this->_service) {
1190
			$this->_service->run();
1191
		}
1192
	}
1193
1194
	/**
1195
	 * Raises OnSaveState event.
1196
	 * This method is invoked when the application needs to save state (probably stored in session).
1197
	 */
1198
	public function onSaveState()
1199
	{
1200
		$this->raiseEvent('OnSaveState', $this, null);
1201
		$this->saveGlobals();
1202
	}
1203
1204
	/**
1205
	 * Raises OnSaveStateComplete event.
1206
	 * This method is invoked right after the application state has been saved.
1207
	 */
1208
	public function onSaveStateComplete()
1209
	{
1210
		$this->raiseEvent('OnSaveStateComplete', $this, null);
1211
	}
1212
1213
	/**
1214
	 * Raises OnPreFlushOutput event.
1215
	 * This method is invoked right before the application flushes output to client.
1216
	 */
1217
	public function onPreFlushOutput()
1218
	{
1219
		$this->raiseEvent('OnPreFlushOutput', $this, null);
1220
	}
1221
1222
	/**
1223
	 * Flushes output to client side.
1224
	 * @param bool $continueBuffering whether to continue buffering after flush if buffering was active
1225
	 */
1226
	public function flushOutput($continueBuffering = true)
1227
	{
1228
		$this->getResponse()->flush($continueBuffering);
1229
	}
1230
1231
	/**
1232
	 * Raises OnEndRequest event.
1233
	 * This method is invoked when the application completes the processing of the request.
1234
	 */
1235
	public function onEndRequest()
1236
	{
1237
		$this->flushOutput(false); // flush all remaining content in the buffer
1238
		$this->saveGlobals();  // save global state
1239
		$this->raiseEvent('OnEndRequest', $this, null);
1240
	}
1241
}
1242