Completed
Push — master ( 43ed01...e90eeb )
by Fabio
07:20
created

TApplication   F

Complexity

Total Complexity 146

Size/Duplication

Total Lines 1148
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 18

Test Coverage

Coverage 31.4%

Importance

Changes 0
Metric Value
dl 0
loc 1148
ccs 119
cts 379
cp 0.314
rs 1.008
c 0
b 0
f 0
wmc 146
lcom 1
cbo 18

74 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 2
B resolvePaths() 0 34 10
A 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 clearGlobalState() 0 5 1
A loadGlobals() 0 4 1
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 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
B 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
A setSession() 0 4 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
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
8
 * @package Prado
9
 */
10
11
namespace Prado;
12
13
use Prado\Exceptions\TErrorHandler;
14
use Prado\Exceptions\THttpException;
15
use Prado\Exceptions\TConfigurationException;
16
use Prado\I18N\TGlobalization;
17
use Prado\Security\TSecurityManager;
18
use Prado\Web\TAssetManager;
19
use Prado\Web\THttpRequest;
20
use Prado\Web\THttpResponse;
21
use Prado\Web\THttpSession;
22
use Prado\Util\TLogger;
23
24
/**
25
 * TApplication class.
26
 *
27
 * TApplication coordinates modules and services, and serves as a configuration
28
 * context for all Prado components.
29
 *
30
 * TApplication uses a configuration file to specify the settings of
31
 * the application, the modules, the services, the parameters, and so on.
32
 *
33
 * TApplication adopts a modular structure. A TApplication instance is a composition
34
 * of multiple modules. A module is an instance of class implementing
35
 * {@link IModule} interface. Each module accomplishes certain functionalities
36
 * that are shared by all Prado components in an application.
37
 * There are default modules and user-defined modules. The latter offers extreme
38
 * flexibility of extending TApplication in a plug-and-play fashion.
39
 * Modules cooperate with each other to serve a user request by following
40
 * a sequence of lifecycles predefined in TApplication.
41
 *
42
 * TApplication has four modes that can be changed by setting {@link setMode Mode}
43
 * property (in the application configuration file).
44
 * - <b>Off</b> mode will prevent the application from serving user requests.
45
 * - <b>Debug</b> mode is mainly used during application development. It ensures
46
 *   the cache is always up-to-date if caching is enabled. It also allows
47
 *   exceptions are displayed with rich context information if they occur.
48
 * - <b>Normal</b> mode is mainly used during production stage. Exception information
49
 *   will only be recorded in system error logs. The cache is ensured to be
50
 *   up-to-date if it is enabled.
51
 * - <b>Performance</b> mode is similar to <b>Normal</b> mode except that it
52
 *   does not ensure the cache is up-to-date.
53
 *
54
 * TApplication dispatches each user request to a particular service which
55
 * finishes the actual work for the request with the aid from the application
56
 * modules.
57
 *
58
 * TApplication maintains a lifecycle with the following stages:
59
 * - [construct] : construction of the application instance
60
 * - [initApplication] : load application configuration and instantiate modules and the requested service
61
 * - onBeginRequest : this event happens right after application initialization
62
 * - onAuthentication : this event happens when authentication is needed for the current request
63
 * - onAuthenticationComplete : this event happens right after the authentication is done for the current request
64
 * - onAuthorization : this event happens when authorization is needed for the current request
65
 * - onAuthorizationComplete : this event happens right after the authorization is done for the current request
66
 * - onLoadState : this event happens when application state needs to be loaded
67
 * - onLoadStateComplete : this event happens right after the application state is loaded
68
 * - onPreRunService : this event happens right before the requested service is to run
69
 * - runService : the requested service runs
70
 * - onSaveState : this event happens when application needs to save its state
71
 * - onSaveStateComplete : this event happens right after the application saves its state
72
 * - onPreFlushOutput : this event happens right before the application flushes output to client side.
73
 * - flushOutput : the application flushes output to client side.
74
 * - onEndRequest : this is the last stage a request is being completed
75
 * - [destruct] : destruction of the application instance
76
 * Modules and services can attach their methods to one or several of the above
77
 * events and do appropriate processing when the events are raised. By this way,
78
 * the application is able to coordinate the activities of modules and services
79
 * in the above order. To terminate an application before the whole lifecycle
80
 * completes, call {@link completeRequest}.
81
 *
82
 * Examples:
83
 * - Create and run a Prado application:
84
 * <code>
85
 * $application=new TApplication($configFile);
86
 * $application->run();
87
 * </code>
88
 *
89
 * @author Qiang Xue <[email protected]>
90
 * @package Prado
91
 * @since 3.0
92
 */
93
class TApplication extends \Prado\TComponent
94
{
95
	/**
96
	 * Page service ID
97
	 */
98
	const PAGE_SERVICE_ID = 'page';
99
	/**
100
	 * Application configuration file name
101
	 */
102
	const CONFIG_FILE_XML = 'application.xml';
103
	/**
104
	 * File extension for external config files
105
	 */
106
	const CONFIG_FILE_EXT_XML = '.xml';
107
	/**
108
	 * Configuration file type, application.xml and config.xml
109
	 */
110
	const CONFIG_TYPE_XML = 'xml';
111
	/**
112
	 * Application configuration file name
113
	 */
114
	const CONFIG_FILE_PHP = 'application.php';
115
	/**
116
	 * File extension for external config files
117
	 */
118
	const CONFIG_FILE_EXT_PHP = '.php';
119
	/**
120
	 * Configuration file type, application.php and config.php
121
	 */
122
	const CONFIG_TYPE_PHP = 'php';
123
	/**
124
	 * Runtime directory name
125
	 */
126
	const RUNTIME_PATH = 'runtime';
127
	/**
128
	 * Config cache file
129
	 */
130
	const CONFIGCACHE_FILE = 'config.cache';
131
	/**
132
	 * Global data file
133
	 */
134
	const GLOBAL_FILE = 'global.cache';
135
136
	/**
137
	 * @var array list of events that define application lifecycles
138
	 */
139
	private static $_steps = [
140
		'onBeginRequest',
141
		'onLoadState',
142
		'onLoadStateComplete',
143
		'onAuthentication',
144
		'onAuthenticationComplete',
145
		'onAuthorization',
146
		'onAuthorizationComplete',
147
		'onPreRunService',
148
		'runService',
149
		'onSaveState',
150
		'onSaveStateComplete',
151
		'onPreFlushOutput',
152
		'flushOutput'
153
	];
154
155
	/**
156
	 * @var string application ID
157
	 */
158
	private $_id;
159
	/**
160
	 * @var string unique application ID
161
	 */
162
	private $_uniqueID;
163
	/**
164
	 * @var bool whether the request is completed
165
	 */
166
	private $_requestCompleted = false;
167
	/**
168
	 * @var int application state
169
	 */
170
	private $_step;
171
	/**
172
	 * @var array available services and their configurations indexed by service IDs
173
	 */
174
	private $_services;
175
	/**
176
	 * @var IService current service instance
177
	 */
178
	private $_service;
179
	/**
180
	 * @var array list of loaded application modules
181
	 */
182
	private $_modules = [];
183
	/**
184
	 * @var array list of application modules yet to be loaded
185
	 */
186
	private $_lazyModules = [];
187
	/**
188
	 * @var \Prado\Collections\TMap list of application parameters
189
	 */
190
	private $_parameters;
191
	/**
192
	 * @var string configuration file
193
	 */
194
	private $_configFile;
195
	/**
196
	 * @var string configuration file extension
197
	 */
198
	private $_configFileExt;
199
	/**
200
	 * @var string configuration type
201
	 */
202
	private $_configType;
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 bool 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 = [];
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 $basePath 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 bool $cacheConfig whether to cache application configuration. Defaults to true.
293
	 * @param int $configType configuration type. Defaults to CONFIG_TYPE_XML.
294
	 * @throws TConfigurationException if configuration file cannot be read or the runtime path is invalid.
295
	 */
296 55
	public function __construct($basePath = 'protected', $cacheConfig = true, $configType = self::CONFIG_TYPE_XML)
297
	{
298
		// register application as a singleton
299 55
		Prado::setApplication($this);
300 55
		$this->setConfigurationType($configType);
301 55
		$this->resolvePaths($basePath);
302
303 55
		if ($cacheConfig) {
304 26
			$this->_cacheFile = $this->_runtimePath . DIRECTORY_SEPARATOR . self::CONFIGCACHE_FILE;
305
		}
306
307
		// generates unique ID by hashing the runtime path
308 55
		$this->_uniqueID = md5($this->_runtimePath);
309 55
		$this->_parameters = new \Prado\Collections\TMap;
310 55
		$this->_services = [$this->getPageServiceID() => ['TPageService', [], null]];
311
312 55
		Prado::setPathOfAlias('Application', $this->_basePath);
313 55
	}
314
315
	/**
316
	 * Resolves application-relevant paths.
317
	 * This method is invoked by the application constructor
318
	 * to determine the application configuration file,
319
	 * application root path and the runtime path.
320
	 * @param string $basePath the application root path or the application configuration file
321
	 * @see setBasePath
322
	 * @see setRuntimePath
323
	 * @see setConfigurationFile
324
	 */
325 55
	protected function resolvePaths($basePath)
326
	{
327
		// determine configuration path and file
328 55
		if (empty($basePath) || ($basePath = realpath($basePath)) === false) {
329
			throw new TConfigurationException('application_basepath_invalid', $basePath);
330
		}
331 55
		if (is_dir($basePath) && is_file($basePath . DIRECTORY_SEPARATOR . $this->getConfigurationFileName())) {
332
			$configFile = $basePath . DIRECTORY_SEPARATOR . $this->getConfigurationFileName();
333 55
		} elseif (is_file($basePath)) {
334
			$configFile = $basePath;
335
			$basePath = dirname($configFile);
336
		} else {
337 55
			$configFile = null;
338
		}
339
340
		// determine runtime path
341 55
		$runtimePath = $basePath . DIRECTORY_SEPARATOR . self::RUNTIME_PATH;
342 55
		if (is_writable($runtimePath)) {
343 55
			if ($configFile !== null) {
344
				$runtimePath .= DIRECTORY_SEPARATOR . basename($configFile) . '-' . Prado::getVersion();
345
				if (!is_dir($runtimePath)) {
346
					if (@mkdir($runtimePath) === false) {
347
						throw new TConfigurationException('application_runtimepath_failed', $runtimePath);
348
					}
349
					@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...
350
				}
351
				$this->setConfigurationFile($configFile);
352
			}
353 55
			$this->setBasePath($basePath);
354 55
			$this->setRuntimePath($runtimePath);
355
		} else {
356
			throw new TConfigurationException('application_runtimepath_invalid', $runtimePath);
357
		}
358 55
	}
359
360
	/**
361
	 * Executes the lifecycles of the application.
362
	 * This is the main entry function that leads to the running of the whole
363
	 * Prado application.
364
	 */
365
	public function run()
366
	{
367
		try {
368
			$this->initApplication();
369
			$n = count(self::$_steps);
370
			$this->_step = 0;
371
			$this->_requestCompleted = false;
372
			while ($this->_step < $n) {
373
				if ($this->_mode === TApplicationMode::Off) {
374
					throw new THttpException(503, 'application_unavailable');
375
				}
376
				if ($this->_requestCompleted) {
377
					break;
378
				}
379
				$method = self::$_steps[$this->_step];
380
				Prado::trace("Executing $method()", 'Prado\TApplication');
381
				$this->$method();
382
				$this->_step++;
383
			}
384
		} catch (\Exception $e) {
385
			$this->onError($e);
386
		}
387
		$this->onEndRequest();
388
	}
389
390
	/**
391
	 * Completes current request processing.
392
	 * This method can be used to exit the application lifecycles after finishing
393
	 * the current cycle.
394
	 */
395
	public function completeRequest()
396
	{
397
		$this->_requestCompleted = true;
398
	}
399
400
	/**
401
	 * @return bool whether the current request is processed.
402
	 */
403
	public function getRequestCompleted()
404
	{
405
		return $this->_requestCompleted;
406
	}
407
408
	/**
409
	 * Returns a global value.
410
	 *
411
	 * A global value is one that is persistent across users sessions and requests.
412
	 * @param string $key the name of the value to be returned
413
	 * @param mixed $defaultValue the default value. If $key is not found, $defaultValue will be returned
414
	 * @return mixed the global value corresponding to $key
415
	 */
416 3
	public function getGlobalState($key, $defaultValue = null)
417
	{
418 3
		return isset($this->_globals[$key]) ? $this->_globals[$key] : $defaultValue;
419
	}
420
421
	/**
422
	 * Sets a global value.
423
	 *
424
	 * A global value is one that is persistent across users sessions and requests.
425
	 * Make sure that the value is serializable and unserializable.
426
	 * @param string $key the name of the value to be set
427
	 * @param mixed $value the global value to be set
428
	 * @param null|mixed $defaultValue the default value. If $key is not found, $defaultValue will be returned
429
	 * @param bool $forceSave wheter to force an immediate GlobalState save. defaults to false
430
	 */
431 3
	public function setGlobalState($key, $value, $defaultValue = null, $forceSave = false)
432
	{
433 3
		$this->_stateChanged = true;
434 3
		if ($value === $defaultValue) {
435
			unset($this->_globals[$key]);
436
		} else {
437 3
			$this->_globals[$key] = $value;
438
		}
439 3
		if ($forceSave) {
440 3
			$this->saveGlobals();
441
		}
442 3
	}
443
444
	/**
445
	 * Clears a global value.
446
	 *
447
	 * The value cleared will no longer be available in this request and the following requests.
448
	 * @param string $key the name of the value to be cleared
449
	 */
450
	public function clearGlobalState($key)
451
	{
452
		$this->_stateChanged = true;
453
		unset($this->_globals[$key]);
454
	}
455
456
	/**
457
	 * Loads global values from persistent storage.
458
	 * This method is invoked when {@link onLoadState OnLoadState} event is raised.
459
	 * After this method, values that are stored in previous requests become
460
	 * available to the current request via {@link getGlobalState}.
461
	 */
462
	protected function loadGlobals()
463
	{
464
		$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...
465
	}
466
467
	/**
468
	 * Saves global values into persistent storage.
469
	 * This method is invoked when {@link onSaveState OnSaveState} event is raised.
470
	 */
471 3
	protected function saveGlobals()
472
	{
473 3
		if ($this->_stateChanged) {
474 3
			$this->_stateChanged = false;
475 3
			$this->getApplicationStatePersister()->save($this->_globals);
476
		}
477 3
	}
478
479
	/**
480
	 * @return string application ID
481
	 */
482 3
	public function getID()
483
	{
484 3
		return $this->_id;
485
	}
486
487
	/**
488
	 * @param string $value application ID
489
	 */
490
	public function setID($value)
491
	{
492
		$this->_id = $value;
493
	}
494
495
	/**
496
	 * @return string page service ID
497
	 */
498 55
	public function getPageServiceID()
499
	{
500 55
		return $this->_pageServiceID;
501
	}
502
503
	/**
504
	 * @param string $value page service ID
505
	 */
506
	public function setPageServiceID($value)
507
	{
508
		$this->_pageServiceID = $value;
509
	}
510
511
	/**
512
	 * @return string an ID that uniquely identifies this Prado application from the others
513
	 */
514 7
	public function getUniqueID()
515
	{
516 7
		return $this->_uniqueID;
517
	}
518
519
	/**
520
	 * @return TApplicationMode application mode. Defaults to TApplicationMode::Debug.
521
	 */
522 32
	public function getMode()
523
	{
524 32
		return $this->_mode;
525
	}
526
527
	/**
528
	 * @param TApplicationMode $value application mode
529
	 */
530
	public function setMode($value)
531
	{
532
		$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...
533
	}
534
535
	/**
536
	 * @return string the directory containing the application configuration file (absolute path)
537
	 */
538
	public function getBasePath()
539
	{
540
		return $this->_basePath;
541
	}
542
543
	/**
544
	 * @param string $value the directory containing the application configuration file
545
	 */
546 55
	public function setBasePath($value)
547
	{
548 55
		$this->_basePath = $value;
549 55
	}
550
551
	/**
552
	 * @return string the application configuration file (absolute path)
553
	 */
554
	public function getConfigurationFile()
555
	{
556
		return $this->_configFile;
557
	}
558
559
	/**
560
	 * @param string $value the application configuration file (absolute path)
561
	 */
562
	public function setConfigurationFile($value)
563
	{
564
		$this->_configFile = $value;
565
	}
566
567
	/**
568
	 * @return string the application configuration file (absolute path)
569
	 */
570 9
	public function getConfigurationType()
571
	{
572 9
		return $this->_configType;
573
	}
574
575
	/**
576
	 * @param string $value the application configuration type. 'xml' and 'php' are valid values
577
	 */
578 55
	public function setConfigurationType($value)
579
	{
580 55
		$this->_configType = $value;
581 55
	}
582
583
	/**
584
	 * @return string the application configuration type. default is 'xml'
585
	 */
586
	public function getConfigurationFileExt()
587
	{
588
		if ($this->_configFileExt === null) {
589
			switch ($this->_configType) {
590
				case TApplication::CONFIG_TYPE_PHP:
591
					$this->_configFileExt = TApplication::CONFIG_FILE_EXT_PHP;
592
					break;
593
				default:
594
					$this->_configFileExt = TApplication::CONFIG_FILE_EXT_XML;
595
			}
596
		}
597
		return $this->_configFileExt;
598
	}
599
600
	/**
601
	 * @return string the default configuration file name
602
	 */
603 55
	public function getConfigurationFileName()
604
	{
605 55
		static $fileName;
606 55
		if ($fileName == null) {
607 2
			switch ($this->_configType) {
608 2
				case TApplication::CONFIG_TYPE_PHP:
609
					$fileName = TApplication::CONFIG_FILE_PHP;
610
					break;
611
				default:
612 2
					$fileName = TApplication::CONFIG_FILE_XML;
613
			}
614
		}
615 55
		return $fileName;
616
	}
617
618
	/**
619
	 * @return string the directory storing cache data and application-level persistent data. (absolute path)
620
	 */
621 3
	public function getRuntimePath()
622
	{
623 3
		return $this->_runtimePath;
624
	}
625
626
	/**
627
	 * @param string $value the directory storing cache data and application-level persistent data. (absolute path)
628
	 */
629 55
	public function setRuntimePath($value)
630
	{
631 55
		$this->_runtimePath = $value;
632 55
		if ($this->_cacheFile) {
633
			$this->_cacheFile = $this->_runtimePath . DIRECTORY_SEPARATOR . self::CONFIGCACHE_FILE;
634
		}
635
		// generates unique ID by hashing the runtime path
636 55
		$this->_uniqueID = md5($this->_runtimePath);
637 55
	}
638
639
	/**
640
	 * @return IService the currently requested service
641
	 */
642
	public function getService()
643
	{
644
		return $this->_service;
645
	}
646
647
	/**
648
	 * @param IService $value the currently requested service
649
	 */
650
	public function setService($value)
651
	{
652
		$this->_service = $value;
653
	}
654
655
	/**
656
	 * Adds a module to application.
657
	 * Note, this method does not do module initialization.
658
	 * @param string $id ID of the module
659
	 * @param null|IModule $module module object or null if the module has not been loaded yet
660
	 */
661 14
	public function setModule($id, IModule $module = null)
662
	{
663 14
		if (isset($this->_modules[$id])) {
664
			throw new TConfigurationException('application_moduleid_duplicated', $id);
665
		} else {
666 14
			$this->_modules[$id] = $module;
667
		}
668 14
	}
669
670
	/**
671
	 * @param mixed $id
672
	 * @return IModule the module with the specified ID, null if not found
673
	 */
674 15
	public function getModule($id)
675
	{
676 15
		if (!array_key_exists($id, $this->_modules)) {
677 2
			return null;
678
		}
679
680
		// force loading of a lazy module
681 15
		if ($this->_modules[$id] === null) {
682
			$module = $this->internalLoadModule($id, true);
683
			$module[0]->init($module[1]);
684
		}
685
686 15
		return $this->_modules[$id];
687
	}
688
689
	/**
690
	 * Returns a list of application modules indexed by module IDs.
691
	 * Modules that have not been loaded yet are returned as null objects.
692
	 * @return array list of loaded application modules, indexed by module IDs
693
	 */
694
	public function getModules()
695
	{
696
		return $this->_modules;
697
	}
698
699
	/**
700
	 * Returns the list of application parameters.
701
	 * Since the parameters are returned as a {@link \Prado\Collections\TMap} object, you may use
702
	 * the returned result to access, add or remove individual parameters.
703
	 * @return \Prado\Collections\TMap the list of application parameters
704
	 */
705
	public function getParameters()
706
	{
707
		return $this->_parameters;
708
	}
709
710
	/**
711
	 * @return THttpRequest the request module
712
	 */
713 18
	public function getRequest()
714
	{
715 18
		if (!$this->_request) {
716 1
			$this->_request = new \Prado\Web\THttpRequest;
717 1
			$this->_request->init(null);
718
		}
719 18
		return $this->_request;
720
	}
721
722
	/**
723
	 * @param THttpRequest $request the request module
724
	 */
725 42
	public function setRequest(THttpRequest $request)
726
	{
727 42
		$this->_request = $request;
728 42
	}
729
730
	/**
731
	 * @return THttpResponse the response module
732
	 */
733 1
	public function getResponse()
734
	{
735 1
		if (!$this->_response) {
736
			$this->_response = new THttpResponse;
737
			$this->_response->init(null);
738
		}
739 1
		return $this->_response;
740
	}
741
742
	/**
743
	 * @param THttpRequest $response the request module
744
	 */
745 8
	public function setResponse(THttpResponse $response)
746
	{
747 8
		$this->_response = $response;
748 8
	}
749
750
	/**
751
	 * @return THttpSession the session module, null if session module is not installed
752
	 */
753
	public function getSession()
754
	{
755
		if (!$this->_session) {
756
			$this->_session = new THttpSession;
757
			$this->_session->init(null);
758
		}
759
		return $this->_session;
760
	}
761
762
	/**
763
	 * @param THttpSession $session the session module
764
	 */
765 10
	public function setSession(THttpSession $session)
766
	{
767 10
		$this->_session = $session;
768 10
	}
769
770
	/**
771
	 * @return TErrorHandler the error handler module
772
	 */
773
	public function getErrorHandler()
774
	{
775
		if (!$this->_errorHandler) {
776
			$this->_errorHandler = new TErrorHandler;
777
			$this->_errorHandler->init(null);
778
		}
779
		return $this->_errorHandler;
780
	}
781
782
	/**
783
	 * @param TErrorHandler $handler the error handler module
784
	 */
785
	public function setErrorHandler(TErrorHandler $handler)
786
	{
787
		$this->_errorHandler = $handler;
788
	}
789
790
	/**
791
	 * @return TSecurityManager the security manager module
792
	 */
793 2
	public function getSecurityManager()
794
	{
795 2
		if (!$this->_security) {
796 1
			$this->_security = new TSecurityManager;
797 1
			$this->_security->init(null);
798
		}
799 2
		return $this->_security;
800
	}
801
802
	/**
803
	 * @param TSecurityManager $sm the security manager module
804
	 */
805 9
	public function setSecurityManager(TSecurityManager $sm)
806
	{
807 9
		$this->_security = $sm;
808 9
	}
809
810
	/**
811
	 * @return TAssetManager asset manager
812
	 */
813 1
	public function getAssetManager()
814
	{
815 1
		if (!$this->_assetManager) {
816
			$this->_assetManager = new TAssetManager;
817
			$this->_assetManager->init(null);
818
		}
819 1
		return $this->_assetManager;
820
	}
821
822
	/**
823
	 * @param TAssetManager $value asset manager
824
	 */
825 6
	public function setAssetManager(TAssetManager $value)
826
	{
827 6
		$this->_assetManager = $value;
828 6
	}
829
830
	/**
831
	 * @return IStatePersister application state persister
832
	 */
833 3
	public function getApplicationStatePersister()
834
	{
835 3
		if (!$this->_statePersister) {
836 2
			$this->_statePersister = new TApplicationStatePersister;
837 2
			$this->_statePersister->init(null);
838
		}
839 3
		return $this->_statePersister;
840
	}
841
842
	/**
843
	 * @param IStatePersister $persister application state persister
844
	 */
845 2
	public function setApplicationStatePersister(IStatePersister $persister)
846
	{
847 2
		$this->_statePersister = $persister;
848 2
	}
849
850
	/**
851
	 * @return ICache the cache module, null if cache module is not installed
852
	 */
853 20
	public function getCache()
854
	{
855 20
		return $this->_cache;
856
	}
857
858
	/**
859
	 * @param \Prado\Caching\ICache $cache the cache module
860
	 */
861 17
	public function setCache(\Prado\Caching\ICache $cache)
862
	{
863 17
		$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...
864 17
	}
865
866
	/**
867
	 * @return IUser the application user
868
	 */
869
	public function getUser()
870
	{
871
		return $this->_user;
872
	}
873
874
	/**
875
	 * @param \Prado\Security\IUser $user the application user
876
	 */
877
	public function setUser(\Prado\Security\IUser $user)
878
	{
879
		$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...
880
	}
881
882
	/**
883
	 * @param bool $createIfNotExists whether to create globalization if it does not exist
884
	 * @return TGlobalization globalization module
885
	 */
886
	public function getGlobalization($createIfNotExists = true)
887
	{
888
		if ($this->_globalization === null && $createIfNotExists) {
889
			$this->_globalization = new TGlobalization;
890
			$this->_globalization->init(null);
891
		}
892
		return $this->_globalization;
893
	}
894
895
	/**
896
	 * @param \Prado\I18N\TGlobalization $glob globalization module
897
	 */
898
	public function setGlobalization(\Prado\I18N\TGlobalization $glob)
899
	{
900
		$this->_globalization = $glob;
901
	}
902
903
	/**
904
	 * @return TAuthorizationRuleCollection list of authorization rules for the current request
905
	 */
906
	public function getAuthorizationRules()
907
	{
908
		if ($this->_authRules === null) {
909
			$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...
910
		}
911
		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 911 which is incompatible with the return type documented by Prado\TApplication::getAuthorizationRules of type Prado\TAuthorizationRuleCollection.
Loading history...
912
	}
913
914
	protected function getApplicationConfigurationClass()
915
	{
916
		return '\Prado\TApplicationConfiguration';
917
	}
918
919
	protected function internalLoadModule($id, $force = false)
920
	{
921
		[$moduleClass, $initProperties, $configElement] = $this->_lazyModules[$id];
0 ignored issues
show
Bug introduced by
The variable $moduleClass does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $initProperties does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $configElement does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
922
		if (isset($initProperties['lazy']) && $initProperties['lazy'] && !$force) {
923
			Prado::trace("Postponed loading of lazy module $id ({$moduleClass})", '\Prado\TApplication');
924
			$this->setModule($id, null);
925
			return null;
926
		}
927
928
		Prado::trace("Loading module $id ({$moduleClass})", '\Prado\TApplication');
929
		$module = Prado::createComponent($moduleClass);
930
		foreach ($initProperties as $name => $value) {
931
			if ($name === 'lazy') {
932
				continue;
933
			}
934
			$module->setSubProperty($name, $value);
935
		}
936
		$this->setModule($id, $module);
937
		// keep the key to avoid reuse of the old module id
938
		$this->_lazyModules[$id] = null;
939
940
		return [$module, $configElement];
941
	}
942
	/**
943
	 * Applies an application configuration.
944
	 * @param TApplicationConfiguration $config the configuration
945
	 * @param bool $withinService whether the configuration is specified within a service.
946
	 */
947
	public function applyConfiguration($config, $withinService = false)
948
	{
949
		if ($config->getIsEmpty()) {
950
			return;
951
		}
952
953
		// set path aliases and using namespaces
954
		foreach ($config->getAliases() as $alias => $path) {
955
			Prado::setPathOfAlias($alias, $path);
956
		}
957
		foreach ($config->getUsings() as $using) {
958
			Prado::using($using);
959
		}
960
961
		// set application properties
962
		if (!$withinService) {
963
			foreach ($config->getProperties() as $name => $value) {
964
				$this->setSubProperty($name, $value);
965
			}
966
		}
967
968
		if (empty($this->_services)) {
969
			$this->_services = [$this->getPageServiceID() => ['Prado\Web\Services\TPageService', [], null]];
970
		}
971
972
		// load parameters
973
		foreach ($config->getParameters() as $id => $parameter) {
974
			if (is_array($parameter)) {
975
				$component = Prado::createComponent($parameter[0]);
976
				foreach ($parameter[1] as $name => $value) {
977
					$component->setSubProperty($name, $value);
978
				}
979
				$this->_parameters->add($id, $component);
980
			} else {
981
				$this->_parameters->add($id, $parameter);
982
			}
983
		}
984
985
		// load and init modules specified in app config
986
		$modules = [];
987
		foreach ($config->getModules() as $id => $moduleConfig) {
988
			if (!is_string($id)) {
989
				$id = '_module' . count($this->_lazyModules);
990
			}
991
			$this->_lazyModules[$id] = $moduleConfig;
992
			if ($module = $this->internalLoadModule($id)) {
993
				$modules[] = $module;
994
			}
995
		}
996
		foreach ($modules as $module) {
997
			$module[0]->init($module[1]);
998
		}
999
1000
		// load service
1001
		foreach ($config->getServices() as $serviceID => $serviceConfig) {
1002
			$this->_services[$serviceID] = $serviceConfig;
1003
		}
1004
1005
		// external configurations
1006
		foreach ($config->getExternalConfigurations() as $filePath => $condition) {
1007
			if ($condition !== true) {
1008
				$condition = $this->evaluateExpression($condition);
1009
			}
1010
			if ($condition) {
1011
				if (($path = Prado::getPathOfNamespace($filePath, $this->getConfigurationFileExt())) === null || !is_file($path)) {
1012
					throw new TConfigurationException('application_includefile_invalid', $filePath);
1013
				}
1014
				$cn = $this->getApplicationConfigurationClass();
1015
				$c = new $cn;
1016
				$c->loadFromFile($path);
1017
				$this->applyConfiguration($c, $withinService);
1018
			}
1019
		}
1020
	}
1021
1022
	/**
1023
	 * Loads configuration and initializes application.
1024
	 * Configuration file will be read and parsed (if a valid cached version exists,
1025
	 * it will be used instead). Then, modules are created and initialized;
1026
	 * Afterwards, the requested service is created and initialized.
1027
	 * @throws TConfigurationException if module is redefined of invalid type, or service not defined or of invalid type
1028
	 */
1029
	protected function initApplication()
1030
	{
1031
		Prado::trace('Initializing application', 'Prado\TApplication');
1032
1033
		if ($this->_configFile !== null) {
1034
			if ($this->_cacheFile === null || @filemtime($this->_cacheFile) < filemtime($this->_configFile)) {
1035
				$config = new TApplicationConfiguration;
1036
				$config->loadFromFile($this->_configFile);
1037
				if ($this->_cacheFile !== null) {
1038
					file_put_contents($this->_cacheFile, serialize($config), LOCK_EX);
1039
				}
1040
			} else {
1041
				$config = unserialize(file_get_contents($this->_cacheFile));
1042
			}
1043
1044
			$this->applyConfiguration($config, false);
1045
		}
1046
1047
		if (($serviceID = $this->getRequest()->resolveRequest(array_keys($this->_services))) === null) {
1048
			$serviceID = $this->getPageServiceID();
1049
		}
1050
1051
		$this->startService($serviceID);
1052
	}
1053
1054
	/**
1055
	 * Starts the specified service.
1056
	 * The service instance will be created. Its properties will be initialized
1057
	 * and the configurations will be applied, if any.
1058
	 * @param string $serviceID service ID
1059
	 */
1060
	public function startService($serviceID)
1061
	{
1062
		if (isset($this->_services[$serviceID])) {
1063
			[$serviceClass, $initProperties, $configElement] = $this->_services[$serviceID];
0 ignored issues
show
Bug introduced by
The variable $serviceClass does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $initProperties does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $configElement does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1064
			$service = Prado::createComponent($serviceClass);
1065
			if (!($service instanceof IService)) {
1066
				throw new THttpException(500, 'application_service_invalid', $serviceClass);
1067
			}
1068
			if (!$service->getEnabled()) {
1069
				throw new THttpException(500, 'application_service_unavailable', $serviceClass);
1070
			}
1071
			$service->setID($serviceID);
1072
			$this->setService($service);
1073
1074
			foreach ($initProperties as $name => $value) {
1075
				$service->setSubProperty($name, $value);
1076
			}
1077
1078
			if ($configElement !== null) {
1079
				$config = new TApplicationConfiguration;
1080
				if ($this->getConfigurationType() == self::CONFIG_TYPE_PHP) {
1081
					$config->loadFromPhp($configElement, $this->getBasePath());
1082
				} else {
1083
					$config->loadFromXml($configElement, $this->getBasePath());
1084
				}
1085
				$this->applyConfiguration($config, true);
1086
			}
1087
1088
			$service->init($configElement);
1089
		} else {
1090
			throw new THttpException(500, 'application_service_unknown', $serviceID);
1091
		}
1092
	}
1093
1094
	/**
1095
	 * Raises OnError event.
1096
	 * This method is invoked when an exception is raised during the lifecycles
1097
	 * of the application.
1098
	 * @param mixed $param event parameter
1099
	 */
1100
	public function onError($param)
1101
	{
1102
		Prado::log($param->getMessage(), TLogger::ERROR, 'Prado\TApplication');
1103
		$this->raiseEvent('OnError', $this, $param);
1104
		$this->getErrorHandler()->handleError($this, $param);
1105
	}
1106
1107
	/**
1108
	 * Raises OnBeginRequest event.
1109
	 * At the time when this method is invoked, application modules are loaded
1110
	 * and initialized, user request is resolved and the corresponding service
1111
	 * is loaded and initialized. The application is about to start processing
1112
	 * the user request.
1113
	 */
1114
	public function onBeginRequest()
1115
	{
1116
		$this->raiseEvent('OnBeginRequest', $this, null);
1117
	}
1118
1119
	/**
1120
	 * Raises OnAuthentication event.
1121
	 * This method is invoked when the user request needs to be authenticated.
1122
	 */
1123
	public function onAuthentication()
1124
	{
1125
		$this->raiseEvent('OnAuthentication', $this, null);
1126
	}
1127
1128
	/**
1129
	 * Raises OnAuthenticationComplete event.
1130
	 * This method is invoked right after the user request is authenticated.
1131
	 */
1132
	public function onAuthenticationComplete()
1133
	{
1134
		$this->raiseEvent('OnAuthenticationComplete', $this, null);
1135
	}
1136
1137
	/**
1138
	 * Raises OnAuthorization event.
1139
	 * This method is invoked when the user request needs to be authorized.
1140
	 */
1141
	public function onAuthorization()
1142
	{
1143
		$this->raiseEvent('OnAuthorization', $this, null);
1144
	}
1145
1146
	/**
1147
	 * Raises OnAuthorizationComplete event.
1148
	 * This method is invoked right after the user request is authorized.
1149
	 */
1150
	public function onAuthorizationComplete()
1151
	{
1152
		$this->raiseEvent('OnAuthorizationComplete', $this, null);
1153
	}
1154
1155
	/**
1156
	 * Raises OnLoadState event.
1157
	 * This method is invoked when the application needs to load state (probably stored in session).
1158
	 */
1159
	public function onLoadState()
1160
	{
1161
		$this->loadGlobals();
1162
		$this->raiseEvent('OnLoadState', $this, null);
1163
	}
1164
1165
	/**
1166
	 * Raises OnLoadStateComplete event.
1167
	 * This method is invoked right after the application state has been loaded.
1168
	 */
1169
	public function onLoadStateComplete()
1170
	{
1171
		$this->raiseEvent('OnLoadStateComplete', $this, null);
1172
	}
1173
1174
	/**
1175
	 * Raises OnPreRunService event.
1176
	 * This method is invoked right before the service is to be run.
1177
	 */
1178
	public function onPreRunService()
1179
	{
1180
		$this->raiseEvent('OnPreRunService', $this, null);
1181
	}
1182
1183
	/**
1184
	 * Runs the requested service.
1185
	 */
1186
	public function runService()
1187
	{
1188
		if ($this->_service) {
1189
			$this->_service->run();
1190
		}
1191
	}
1192
1193
	/**
1194
	 * Raises OnSaveState event.
1195
	 * This method is invoked when the application needs to save state (probably stored in session).
1196
	 */
1197
	public function onSaveState()
1198
	{
1199
		$this->raiseEvent('OnSaveState', $this, null);
1200
		$this->saveGlobals();
1201
	}
1202
1203
	/**
1204
	 * Raises OnSaveStateComplete event.
1205
	 * This method is invoked right after the application state has been saved.
1206
	 */
1207
	public function onSaveStateComplete()
1208
	{
1209
		$this->raiseEvent('OnSaveStateComplete', $this, null);
1210
	}
1211
1212
	/**
1213
	 * Raises OnPreFlushOutput event.
1214
	 * This method is invoked right before the application flushes output to client.
1215
	 */
1216
	public function onPreFlushOutput()
1217
	{
1218
		$this->raiseEvent('OnPreFlushOutput', $this, null);
1219
	}
1220
1221
	/**
1222
	 * Flushes output to client side.
1223
	 * @param bool $continueBuffering whether to continue buffering after flush if buffering was active
1224
	 */
1225
	public function flushOutput($continueBuffering = true)
1226
	{
1227
		$this->getResponse()->flush($continueBuffering);
1228
	}
1229
1230
	/**
1231
	 * Raises OnEndRequest event.
1232
	 * This method is invoked when the application completes the processing of the request.
1233
	 */
1234
	public function onEndRequest()
1235
	{
1236
		$this->flushOutput(false); // flush all remaining content in the buffer
1237
		$this->saveGlobals();  // save global state
1238
		$this->raiseEvent('OnEndRequest', $this, null);
1239
	}
1240
}
1241