Passed
Push — master ( d5adc1...45b033 )
by Fabio
06:12
created

TApplication   F

Complexity

Total Complexity 153

Size/Duplication

Total Lines 1192
Duplicated Lines 0 %

Test Coverage

Coverage 31.4%

Importance

Changes 0
Metric Value
eloc 340
c 0
b 0
f 0
dl 0
loc 1192
rs 2
ccs 119
cts 379
cp 0.314
wmc 153

76 Methods

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

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
 * - onInitComplete : this event happens right after after module and service initialization. This event is particularly useful for CLI/Shell applications
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
	public const PAGE_SERVICE_ID = 'page';
100
	/**
101
	 * Application configuration file name
102
	 */
103
	public const CONFIG_FILE_XML = 'application.xml';
104
	/**
105
	 * File extension for external config files
106
	 */
107
	public const CONFIG_FILE_EXT_XML = '.xml';
108
	/**
109
	 * Configuration file type, application.xml and config.xml
110
	 */
111
	public const CONFIG_TYPE_XML = 'xml';
112
	/**
113
	 * Application configuration file name
114
	 */
115
	public const CONFIG_FILE_PHP = 'application.php';
116
	/**
117
	 * File extension for external config files
118
	 */
119
	public const CONFIG_FILE_EXT_PHP = '.php';
120
	/**
121
	 * Configuration file type, application.php and config.php
122
	 */
123
	public const CONFIG_TYPE_PHP = 'php';
124
	/**
125
	 * Runtime directory name
126
	 */
127
	public const RUNTIME_PATH = 'runtime';
128
	/**
129
	 * Config cache file
130
	 */
131
	public const CONFIGCACHE_FILE = 'config.cache';
132
	/**
133
	 * Global data file
134
	 */
135
	public 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 \Prado\Caching\ICache cache module, could be null
242
	 */
243
	private $_cache;
244
	/**
245
	 * @var IStatePersister application state persister
246
	 */
247
	private $_statePersister;
248
	/**
249
	 * @var \Prado\Security\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 \Prado\Security\TAuthorizationRuleCollection collection of authorization rules
266
	 */
267
	private $_authRules;
268
	/**
269
	 * @var string|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 string $configType configuration type. Defaults to CONFIG_TYPE_XML.
295
	 * @throws TConfigurationException if configuration file cannot be read or the runtime path is invalid.
296 55
	 */
297
	public function __construct($basePath = 'protected', $cacheConfig = true, $configType = self::CONFIG_TYPE_XML)
298
	{
299 55
		// register application as a singleton
300 55
		Prado::setApplication($this);
301 55
		$this->setConfigurationType($configType);
302
		$this->resolvePaths($basePath);
303 55
304 26
		if ($cacheConfig) {
305
			$this->_cacheFile = $this->_runtimePath . DIRECTORY_SEPARATOR . self::CONFIGCACHE_FILE;
306
		}
307
308 55
		// generates unique ID by hashing the runtime path
309 55
		$this->_uniqueID = md5($this->_runtimePath);
310 55
		$this->_parameters = new \Prado\Collections\TMap;
311
		$this->_services = [$this->getPageServiceID() => ['TPageService', [], null]];
312 55
313 55
		Prado::setPathOfAlias('Application', $this->_basePath);
314
		parent::__construct();
315
	}
316
317
	/**
318
	 * Resolves application-relevant paths.
319
	 * This method is invoked by the application constructor
320
	 * to determine the application configuration file,
321
	 * application root path and the runtime path.
322
	 * @param string $basePath the application root path or the application configuration file
323
	 * @see setBasePath
324
	 * @see setRuntimePath
325 55
	 * @see setConfigurationFile
326
	 */
327
	protected function resolvePaths($basePath)
328 55
	{
329
		// determine configuration path and file
330
		if (empty($basePath) || ($basePath = realpath($basePath)) === false) {
331 55
			throw new TConfigurationException('application_basepath_invalid', $basePath);
332
		}
333 55
		if (is_dir($basePath) && is_file($basePath . DIRECTORY_SEPARATOR . $this->getConfigurationFileName())) {
334
			$configFile = $basePath . DIRECTORY_SEPARATOR . $this->getConfigurationFileName();
335
		} elseif (is_file($basePath)) {
336
			$configFile = $basePath;
337 55
			$basePath = dirname($configFile);
338
		} else {
339
			$configFile = null;
340
		}
341 55
342 55
		// determine runtime path
343 55
		$runtimePath = $basePath . DIRECTORY_SEPARATOR . self::RUNTIME_PATH;
344
		if (is_writable($runtimePath)) {
345
			if ($configFile !== null) {
346
				$runtimePath .= DIRECTORY_SEPARATOR . basename($configFile) . '-' . Prado::getVersion();
347
				if (!is_dir($runtimePath)) {
348
					if (@mkdir($runtimePath) === false) {
349
						throw new TConfigurationException('application_runtimepath_failed', $runtimePath);
350
					}
351
					@chmod($runtimePath, Prado::getDefaultPermissions()); //make it deletable
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

351
					/** @scrutinizer ignore-unhandled */ @chmod($runtimePath, Prado::getDefaultPermissions()); //make it deletable

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...
352
				}
353 55
				$this->setConfigurationFile($configFile);
354 55
			}
355
			$this->setBasePath($basePath);
356
			$this->setRuntimePath($runtimePath);
357
		} else {
358 55
			throw new TConfigurationException('application_runtimepath_invalid', $runtimePath);
359
		}
360
	}
361
362
	/**
363
	 * Executes the lifecycles of the application.
364
	 * This is the main entry function that leads to the running of the whole
365
	 * Prado application.
366
	 */
367
	public function run()
368
	{
369
		try {
370
			$this->initApplication();
371
			$n = count(self::$_steps);
372
			$this->_step = 0;
373
			$this->_requestCompleted = false;
374
			while ($this->_step < $n) {
375
				if ($this->_mode === TApplicationMode::Off) {
376
					throw new THttpException(503, 'application_unavailable');
377
				}
378
				if ($this->_requestCompleted) {
379
					break;
380
				}
381
				$method = self::$_steps[$this->_step];
382
				Prado::trace("Executing $method()", 'Prado\TApplication');
383
				$this->$method();
384
				$this->_step++;
385
			}
386
		} catch (\Exception $e) {
387
			$this->onError($e);
388
		}
389
		$this->onEndRequest();
390
	}
391
392
	/**
393
	 * Completes current request processing.
394
	 * This method can be used to exit the application lifecycles after finishing
395
	 * the current cycle.
396
	 */
397
	public function completeRequest()
398
	{
399
		$this->_requestCompleted = true;
400
	}
401
402
	/**
403
	 * @return bool whether the current request is processed.
404
	 */
405
	public function getRequestCompleted()
406
	{
407
		return $this->_requestCompleted;
408
	}
409
410
	/**
411
	 * Returns a global value.
412
	 *
413
	 * A global value is one that is persistent across users sessions and requests.
414
	 * @param string $key the name of the value to be returned
415
	 * @param mixed $defaultValue the default value. If $key is not found, $defaultValue will be returned
416 3
	 * @return mixed the global value corresponding to $key
417
	 */
418 3
	public function getGlobalState($key, $defaultValue = null)
419
	{
420
		return $this->_globals[$key] ?? $defaultValue;
421
	}
422
423
	/**
424
	 * Sets a global value.
425
	 *
426
	 * A global value is one that is persistent across users sessions and requests.
427
	 * Make sure that the value is serializable and unserializable.
428
	 * @param string $key the name of the value to be set
429
	 * @param mixed $value the global value to be set
430
	 * @param null|mixed $defaultValue the default value. If $key is not found, $defaultValue will be returned
431 3
	 * @param bool $forceSave wheter to force an immediate GlobalState save. defaults to false
432
	 */
433 3
	public function setGlobalState($key, $value, $defaultValue = null, $forceSave = false)
434 3
	{
435
		$this->_stateChanged = true;
436
		if ($value === $defaultValue) {
437 3
			unset($this->_globals[$key]);
438
		} else {
439 3
			$this->_globals[$key] = $value;
440 3
		}
441
		if ($forceSave) {
442 3
			$this->saveGlobals();
443
		}
444
	}
445
446
	/**
447
	 * Clears a global value.
448
	 *
449
	 * The value cleared will no longer be available in this request and the following requests.
450
	 * @param string $key the name of the value to be cleared
451
	 */
452
	public function clearGlobalState($key)
453
	{
454
		$this->_stateChanged = true;
455
		unset($this->_globals[$key]);
456
	}
457
458
	/**
459
	 * Loads global values from persistent storage.
460
	 * This method is invoked when {@link onLoadState OnLoadState} event is raised.
461
	 * After this method, values that are stored in previous requests become
462
	 * available to the current request via {@link getGlobalState}.
463
	 */
464
	protected function loadGlobals()
465
	{
466
		$this->_globals = $this->getApplicationStatePersister()->load();
467
	}
468
469
	/**
470
	 * Saves global values into persistent storage.
471 3
	 * This method is invoked when {@link onSaveState OnSaveState} event is raised.
472
	 */
473 3
	protected function saveGlobals()
474 3
	{
475 3
		if ($this->_stateChanged) {
476
			$this->_stateChanged = false;
477 3
			$this->getApplicationStatePersister()->save($this->_globals);
478
		}
479
	}
480
481
	/**
482 3
	 * @return string application ID
483
	 */
484 3
	public function getID()
485
	{
486
		return $this->_id;
487
	}
488
489
	/**
490
	 * @param string $value application ID
491
	 */
492
	public function setID($value)
493
	{
494
		$this->_id = $value;
495
	}
496
497
	/**
498 55
	 * @return string page service ID
499
	 */
500 55
	public function getPageServiceID()
501
	{
502
		return $this->_pageServiceID;
503
	}
504
505
	/**
506
	 * @param string $value page service ID
507
	 */
508
	public function setPageServiceID($value)
509
	{
510
		$this->_pageServiceID = $value;
511
	}
512
513
	/**
514 7
	 * @return string an ID that uniquely identifies this Prado application from the others
515
	 */
516 7
	public function getUniqueID()
517
	{
518
		return $this->_uniqueID;
519
	}
520
521
	/**
522 32
	 * @return string|TApplicationMode application mode. Defaults to TApplicationMode::Debug.
523
	 */
524 32
	public function getMode()
525
	{
526
		return $this->_mode;
527
	}
528
529
	/**
530
	 * @param TApplicationMode $value application mode
531
	 */
532
	public function setMode($value)
533
	{
534
		$this->_mode = TPropertyValue::ensureEnum($value, '\Prado\TApplicationMode');
535
	}
536
537
	/**
538
	 * @return string the directory containing the application configuration file (absolute path)
539
	 */
540
	public function getBasePath()
541
	{
542
		return $this->_basePath;
543
	}
544
545
	/**
546 55
	 * @param string $value the directory containing the application configuration file
547
	 */
548 55
	public function setBasePath($value)
549 55
	{
550
		$this->_basePath = $value;
551
	}
552
553
	/**
554
	 * @return string the application configuration file (absolute path)
555
	 */
556
	public function getConfigurationFile()
557
	{
558
		return $this->_configFile;
559
	}
560
561
	/**
562
	 * @param string $value the application configuration file (absolute path)
563
	 */
564
	public function setConfigurationFile($value)
565
	{
566
		$this->_configFile = $value;
567
	}
568
569
	/**
570 9
	 * @return string the application configuration file (absolute path)
571
	 */
572 9
	public function getConfigurationType()
573
	{
574
		return $this->_configType;
575
	}
576
577
	/**
578 55
	 * @param string $value the application configuration type. 'xml' and 'php' are valid values
579
	 */
580 55
	public function setConfigurationType($value)
581 55
	{
582
		$this->_configType = $value;
583
	}
584
585
	/**
586
	 * @return string the application configuration type. default is 'xml'
587
	 */
588
	public function getConfigurationFileExt()
589
	{
590
		if ($this->_configFileExt === null) {
591
			switch ($this->_configType) {
592
				case TApplication::CONFIG_TYPE_PHP:
593
					$this->_configFileExt = TApplication::CONFIG_FILE_EXT_PHP;
594
					break;
595
				default:
596
					$this->_configFileExt = TApplication::CONFIG_FILE_EXT_XML;
597
			}
598
		}
599
		return $this->_configFileExt;
600
	}
601
602
	/**
603 55
	 * @return string the default configuration file name
604
	 */
605 55
	public function getConfigurationFileName()
606 55
	{
607 2
		static $fileName;
608 2
		if ($fileName == null) {
609
			switch ($this->_configType) {
610
				case TApplication::CONFIG_TYPE_PHP:
611
					$fileName = TApplication::CONFIG_FILE_PHP;
612 2
					break;
613
				default:
614
					$fileName = TApplication::CONFIG_FILE_XML;
615 55
			}
616
		}
617
		return $fileName;
618
	}
619
620
	/**
621 3
	 * @return string the directory storing cache data and application-level persistent data. (absolute path)
622
	 */
623 3
	public function getRuntimePath()
624
	{
625
		return $this->_runtimePath;
626
	}
627
628
	/**
629 55
	 * @param string $value the directory storing cache data and application-level persistent data. (absolute path)
630
	 */
631 55
	public function setRuntimePath($value)
632 55
	{
633
		$this->_runtimePath = $value;
634
		if ($this->_cacheFile) {
635
			$this->_cacheFile = $this->_runtimePath . DIRECTORY_SEPARATOR . self::CONFIGCACHE_FILE;
636 55
		}
637 55
		// generates unique ID by hashing the runtime path
638
		$this->_uniqueID = md5($this->_runtimePath);
639
	}
640
641
	/**
642
	 * @return IService the currently requested service
643
	 */
644
	public function getService()
645
	{
646
		return $this->_service;
647
	}
648
649
	/**
650
	 * @param IService $value the currently requested service
651
	 */
652
	public function setService($value)
653
	{
654
		$this->_service = $value;
655
	}
656
657
	/**
658
	 * Adds a module to application.
659
	 * Note, this method does not do module initialization.
660
	 * @param string $id ID of the module
661 14
	 * @param null|IModule $module module object or null if the module has not been loaded yet
662
	 */
663 14
	public function setModule($id, IModule $module = null)
664
	{
665
		if (isset($this->_modules[$id])) {
666 14
			throw new TConfigurationException('application_moduleid_duplicated', $id);
667
		} else {
668 14
			$this->_modules[$id] = $module;
669
		}
670
	}
671
672
	/**
673
	 * @param mixed $id
674 15
	 * @return IModule the module with the specified ID, null if not found
675
	 */
676 15
	public function getModule($id)
677 2
	{
678
		if (!array_key_exists($id, $this->_modules)) {
679
			return null;
680
		}
681 15
682
		// force loading of a lazy module
683
		if ($this->_modules[$id] === null) {
684
			$module = $this->internalLoadModule($id, true);
685
			$module[0]->init($module[1]);
0 ignored issues
show
Bug introduced by
The method init() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

685
			$module[0]->/** @scrutinizer ignore-call */ 
686
               init($module[1]);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
686 15
		}
687
688
		return $this->_modules[$id];
689
	}
690
691
	/**
692
	 * Returns a list of application modules indexed by module IDs.
693
	 * Modules that have not been loaded yet are returned as null objects.
694
	 * @return array list of loaded application modules, indexed by module IDs
695
	 */
696
	public function getModules()
697
	{
698
		return $this->_modules;
699
	}
700
701
	/**
702
	 * Returns a list of application modules of a specific class.
703
	 * Lazy Loading Modules are not loaded, and are null but have an ID Key.
704
	 * When null modules are found, load them with {@link getModule}.
705
	 * @param string $type class name of the modules to look for.
706
	 * @param bool $strict should the module be the class or can the module be a subclass
707
	 * @return array keys are the ids of the module and values are module of a specific class
708
	 * @since 4.2.0
709
	 */
710
	public function getModulesByType($type, $strict = false)
711
	{
712
		$m = [];
713 18
		foreach ($this->_modules as $id => $module) {
714
			if ($module === null) {
715 18
				[$moduleClass, $initProperties, $configElement] = $this->_lazyModules[$id];
716 1
				if ($strict ? ($moduleClass === $type) : ($moduleClass instanceof $type)) {
717 1
					$m[$id] = null;
718
				}
719 18
			} elseif ($strict ? (get_class($module) === $type) : $module->isa($type)) {
720
				$m[$id] = $module;
721
			}
722
		}
723
		return $m;
724
	}
725 42
726
	/**
727 42
	 * Returns the list of application parameters.
728 42
	 * Since the parameters are returned as a {@link \Prado\Collections\TMap} object, you may use
729
	 * the returned result to access, add or remove individual parameters.
730
	 * @return \Prado\Collections\TMap the list of application parameters
731
	 */
732
	public function getParameters()
733 1
	{
734
		return $this->_parameters;
735 1
	}
736
737
	/**
738
	 * @return THttpRequest the request module
739 1
	 */
740
	public function getRequest()
741
	{
742
		if (!$this->_request) {
743
			$this->_request = new \Prado\Web\THttpRequest;
744
			$this->_request->init(null);
745 8
		}
746
		return $this->_request;
747 8
	}
748 8
749
	/**
750
	 * @param THttpRequest $request the request module
751
	 */
752
	public function setRequest(THttpRequest $request)
753
	{
754
		$this->_request = $request;
755
	}
756
757
	/**
758
	 * @return THttpResponse the response module
759
	 */
760
	public function getResponse()
761
	{
762
		if (!$this->_response) {
763
			$this->_response = new THttpResponse;
764
			$this->_response->init(null);
765 10
		}
766
		return $this->_response;
767 10
	}
768 10
769
	/**
770
	 * @param THttpResponse $response the request module
771
	 */
772
	public function setResponse(THttpResponse $response)
773
	{
774
		$this->_response = $response;
775
	}
776
777
	/**
778
	 * @return THttpSession the session module, null if session module is not installed
779
	 */
780
	public function getSession()
781
	{
782
		if (!$this->_session) {
783
			$this->_session = new THttpSession;
784
			$this->_session->init(null);
785
		}
786
		return $this->_session;
787
	}
788
789
	/**
790
	 * @param THttpSession $session the session module
791
	 */
792
	public function setSession(THttpSession $session)
793 2
	{
794
		$this->_session = $session;
795 2
	}
796 1
797 1
	/**
798
	 * @return TErrorHandler the error handler module
799 2
	 */
800
	public function getErrorHandler()
801
	{
802
		if (!$this->_errorHandler) {
803
			$this->_errorHandler = new TErrorHandler;
804
			$this->_errorHandler->init(null);
805 9
		}
806
		return $this->_errorHandler;
807 9
	}
808 9
809
	/**
810
	 * @param TErrorHandler $handler the error handler module
811
	 */
812
	public function setErrorHandler(TErrorHandler $handler)
813 1
	{
814
		$this->_errorHandler = $handler;
815 1
	}
816
817
	/**
818
	 * @return TSecurityManager the security manager module
819 1
	 */
820
	public function getSecurityManager()
821
	{
822
		if (!$this->_security) {
823
			$this->_security = new TSecurityManager;
824
			$this->_security->init(null);
825 6
		}
826
		return $this->_security;
827 6
	}
828 6
829
	/**
830
	 * @param TSecurityManager $sm the security manager module
831
	 */
832
	public function setSecurityManager(TSecurityManager $sm)
833 3
	{
834
		$this->_security = $sm;
835 3
	}
836 2
837 2
	/**
838
	 * @return TAssetManager asset manager
839 3
	 */
840
	public function getAssetManager()
841
	{
842
		if (!$this->_assetManager) {
843
			$this->_assetManager = new TAssetManager;
844
			$this->_assetManager->init(null);
845 2
		}
846
		return $this->_assetManager;
847 2
	}
848 2
849
	/**
850
	 * @param TAssetManager $value asset manager
851
	 */
852
	public function setAssetManager(TAssetManager $value)
853 20
	{
854
		$this->_assetManager = $value;
855 20
	}
856
857
	/**
858
	 * @return IStatePersister application state persister
859
	 */
860
	public function getApplicationStatePersister()
861 17
	{
862
		if (!$this->_statePersister) {
863 17
			$this->_statePersister = new TApplicationStatePersister;
864 17
			$this->_statePersister->init(null);
865
		}
866
		return $this->_statePersister;
867
	}
868
869
	/**
870
	 * @param IStatePersister $persister application state persister
871
	 */
872
	public function setApplicationStatePersister(IStatePersister $persister)
873
	{
874
		$this->_statePersister = $persister;
875
	}
876
877
	/**
878
	 * @return null|\Prado\Caching\ICache the cache module, null if cache module is not installed
879
	 */
880
	public function getCache()
881
	{
882
		return $this->_cache;
883
	}
884
885
	/**
886
	 * @param \Prado\Caching\ICache $cache the cache module
887
	 */
888
	public function setCache(\Prado\Caching\ICache $cache)
889
	{
890
		$this->_cache = $cache;
891
	}
892
893
	/**
894
	 * @return \Prado\Security\IUser the application user
895
	 */
896
	public function getUser()
897
	{
898
		return $this->_user;
899
	}
900
901
	/**
902
	 * @param \Prado\Security\IUser $user the application user
903
	 */
904
	public function setUser(\Prado\Security\IUser $user)
905
	{
906
		$this->_user = $user;
907
	}
908
909
	/**
910
	 * @param bool $createIfNotExists whether to create globalization if it does not exist
911
	 * @return null|TGlobalization globalization module
912
	 */
913
	public function getGlobalization($createIfNotExists = true)
914
	{
915
		if ($this->_globalization === null && $createIfNotExists) {
916
			$this->_globalization = new TGlobalization;
917
			$this->_globalization->init(null);
918
		}
919
		return $this->_globalization;
920
	}
921
922
	/**
923
	 * @param \Prado\I18N\TGlobalization $glob globalization module
924
	 */
925
	public function setGlobalization(\Prado\I18N\TGlobalization $glob)
926
	{
927
		$this->_globalization = $glob;
928
	}
929
930
	/**
931
	 * @return \Prado\Security\TAuthorizationRuleCollection list of authorization rules for the current request
932
	 */
933
	public function getAuthorizationRules()
934
	{
935
		if ($this->_authRules === null) {
936
			$this->_authRules = new \Prado\Security\TAuthorizationRuleCollection;
937
		}
938
		return $this->_authRules;
939
	}
940
941
	protected function getApplicationConfigurationClass()
942
	{
943
		return '\Prado\TApplicationConfiguration';
944
	}
945
946
	protected function internalLoadModule($id, $force = false)
947
	{
948
		[$moduleClass, $initProperties, $configElement] = $this->_lazyModules[$id];
949
		if (isset($initProperties['lazy']) && $initProperties['lazy'] && !$force) {
950
			Prado::trace("Postponed loading of lazy module $id ({$moduleClass})", '\Prado\TApplication');
951
			$this->setModule($id, null);
952
			return null;
953
		}
954
955
		Prado::trace("Loading module $id ({$moduleClass})", '\Prado\TApplication');
956
		$module = Prado::createComponent($moduleClass);
957
		foreach ($initProperties as $name => $value) {
958
			if ($name === 'lazy') {
959
				continue;
960
			}
961
			$module->setSubProperty($name, $value);
962
		}
963
		$this->setModule($id, $module);
964
		// keep the key to avoid reuse of the old module id
965
		$this->_lazyModules[$id] = null;
966
		$module->dyPreInit($configElement);
0 ignored issues
show
Bug introduced by
The method dyPreInit() does not exist on Prado\IModule. Since it exists in all sub-types, consider adding an abstract or default implementation to Prado\IModule. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

966
		$module->/** @scrutinizer ignore-call */ 
967
           dyPreInit($configElement);
Loading history...
967
968
		return [$module, $configElement];
969
	}
970
	/**
971
	 * Applies an application configuration.
972
	 * @param TApplicationConfiguration $config the configuration
973
	 * @param bool $withinService whether the configuration is specified within a service.
974
	 */
975
	public function applyConfiguration($config, $withinService = false)
976
	{
977
		if ($config->getIsEmpty()) {
978
			return;
979
		}
980
981
		// set path aliases and using namespaces
982
		foreach ($config->getAliases() as $alias => $path) {
983
			Prado::setPathOfAlias($alias, $path);
984
		}
985
		foreach ($config->getUsings() as $using) {
986
			Prado::using($using);
987
		}
988
989
		// set application properties
990
		if (!$withinService) {
991
			foreach ($config->getProperties() as $name => $value) {
992
				$this->setSubProperty($name, $value);
993
			}
994
		}
995
996
		if (empty($this->_services)) {
997
			$this->_services = [$this->getPageServiceID() => ['Prado\Web\Services\TPageService', [], null]];
998
		}
999
1000
		// load parameters
1001
		foreach ($config->getParameters() as $id => $parameter) {
1002
			if (is_array($parameter)) {
1003
				$component = Prado::createComponent($parameter[0]);
1004
				foreach ($parameter[1] as $name => $value) {
1005
					$component->setSubProperty($name, $value);
1006
				}
1007
				$component->dyInit(null);
1008
				$this->_parameters->add($id, $component);
1009
			} else {
1010
				$this->_parameters->add($id, $parameter);
1011
			}
1012
		}
1013
1014
		// load and init modules specified in app config
1015
		$modules = [];
1016
		foreach ($config->getModules() as $id => $moduleConfig) {
1017
			if (!is_string($id)) {
1018
				$id = '_module' . count($this->_lazyModules);
1019
			}
1020
			$this->_lazyModules[$id] = $moduleConfig;
1021
			if ($module = $this->internalLoadModule($id)) {
1022
				$modules[] = $module;
1023
			}
1024
		}
1025
		foreach ($modules as $module) {
1026
			$module[0]->init($module[1]);
1027
		}
1028
1029
		// load service
1030
		foreach ($config->getServices() as $serviceID => $serviceConfig) {
1031
			$this->_services[$serviceID] = $serviceConfig;
1032
		}
1033
1034
		// external configurations
1035
		foreach ($config->getExternalConfigurations() as $filePath => $condition) {
1036
			if ($condition !== true) {
1037
				$condition = $this->evaluateExpression($condition);
1038
			}
1039
			if ($condition) {
1040
				if (($path = Prado::getPathOfNamespace($filePath, $this->getConfigurationFileExt())) === null || !is_file($path)) {
1041
					throw new TConfigurationException('application_includefile_invalid', $filePath);
1042
				}
1043
				$cn = $this->getApplicationConfigurationClass();
1044
				$c = new $cn;
1045
				$c->loadFromFile($path);
1046
				$this->applyConfiguration($c, $withinService);
1047
			}
1048
		}
1049
	}
1050
1051
	/**
1052
	 * Loads configuration and initializes application.
1053
	 * Configuration file will be read and parsed (if a valid cached version exists,
1054
	 * it will be used instead). Then, modules are created and initialized;
1055
	 * Afterwards, the requested service is created and initialized.
1056
	 * Lastly, the onInitComplete event is raised.
1057
	 * @throws TConfigurationException if module is redefined of invalid type, or service not defined or of invalid type
1058
	 */
1059
	protected function initApplication()
1060
	{
1061
		Prado::trace('Initializing application', 'Prado\TApplication');
1062
1063
		if ($this->_configFile !== null) {
1064
			if ($this->_cacheFile === null || @filemtime($this->_cacheFile) < filemtime($this->_configFile)) {
1065
				$config = new TApplicationConfiguration;
1066
				$config->loadFromFile($this->_configFile);
1067
				if ($this->_cacheFile !== null) {
1068
					file_put_contents($this->_cacheFile, serialize($config), LOCK_EX);
1069
				}
1070
			} else {
1071
				$config = unserialize(file_get_contents($this->_cacheFile));
1072
			}
1073
1074
			$this->applyConfiguration($config, false);
1075
		}
1076
1077
		if (($serviceID = $this->getRequest()->resolveRequest(array_keys($this->_services))) === null) {
0 ignored issues
show
introduced by
The condition $serviceID = $this->getR...s->_services)) === null is always false.
Loading history...
1078
			$serviceID = $this->getPageServiceID();
1079
		}
1080
1081
		$this->startService($serviceID);
1082
1083
		$this->onInitComplete();
1084
	}
1085
1086
	/**
1087
	 * Starts the specified service.
1088
	 * The service instance will be created. Its properties will be initialized
1089
	 * and the configurations will be applied, if any.
1090
	 * @param string $serviceID service ID
1091
	 */
1092
	public function startService($serviceID)
1093
	{
1094
		if (isset($this->_services[$serviceID])) {
1095
			[$serviceClass, $initProperties, $configElement] = $this->_services[$serviceID];
1096
			$service = Prado::createComponent($serviceClass);
1097
			if (!($service instanceof IService)) {
1098
				throw new THttpException(500, 'application_service_invalid', $serviceClass);
1099
			}
1100
			if (!$service->getEnabled()) {
1101
				throw new THttpException(500, 'application_service_unavailable', $serviceClass);
1102
			}
1103
			$service->setID($serviceID);
1104
			$this->setService($service);
1105
1106
			foreach ($initProperties as $name => $value) {
1107
				$service->setSubProperty($name, $value);
0 ignored issues
show
Bug introduced by
The method setSubProperty() does not exist on Prado\IService. Since it exists in all sub-types, consider adding an abstract or default implementation to Prado\IService. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1107
				$service->/** @scrutinizer ignore-call */ 
1108
              setSubProperty($name, $value);
Loading history...
1108
			}
1109
1110
			if ($configElement !== null) {
1111
				$config = new TApplicationConfiguration;
1112
				if ($this->getConfigurationType() == self::CONFIG_TYPE_PHP) {
1113
					$config->loadFromPhp($configElement, $this->getBasePath());
1114
				} else {
1115
					$config->loadFromXml($configElement, $this->getBasePath());
1116
				}
1117
				$this->applyConfiguration($config, true);
1118
			}
1119
1120
			$service->init($configElement);
1121
		} else {
1122
			throw new THttpException(500, 'application_service_unknown', $serviceID);
1123
		}
1124
	}
1125
1126
	/**
1127
	 * Raises OnError event.
1128
	 * This method is invoked when an exception is raised during the lifecycles
1129
	 * of the application.
1130
	 * @param mixed $param event parameter
1131
	 */
1132
	public function onError($param)
1133
	{
1134
		Prado::log($param->getMessage(), TLogger::ERROR, 'Prado\TApplication');
1135
		$this->raiseEvent('OnError', $this, $param);
1136
		$this->getErrorHandler()->handleError($this, $param);
1137
	}
1138
1139
	/**
1140
	 * Raises onInitComplete event.
1141
	 * At the time when this method is invoked, application modules are loaded,
1142
	 * user request is resolved and the corresponding service is loaded and
1143
	 * initialized. The application is about to start processing the user
1144
	 * request.  This call is important for CLI/Shell applications that do not have
1145
	 * a web service lifecycle stack.  This is the first and last event for finalization
1146
	 * of any loaded modules in CLI/Shell mode.
1147
	 * @since 4.2.0
1148
	 */
1149
	public function onInitComplete()
1150
	{
1151
		$this->raiseEvent('onInitComplete', $this, null);
1152
	}
1153
1154
	/**
1155
	 * Raises OnBeginRequest event.
1156
	 * At the time when this method is invoked, application modules are loaded
1157
	 * and initialized, user request is resolved and the corresponding service
1158
	 * is loaded and initialized. The application is about to start processing
1159
	 * the user request.
1160
	 */
1161
	public function onBeginRequest()
1162
	{
1163
		$this->raiseEvent('OnBeginRequest', $this, null);
1164
	}
1165
1166
	/**
1167
	 * Raises OnAuthentication event.
1168
	 * This method is invoked when the user request needs to be authenticated.
1169
	 */
1170
	public function onAuthentication()
1171
	{
1172
		$this->raiseEvent('OnAuthentication', $this, null);
1173
	}
1174
1175
	/**
1176
	 * Raises OnAuthenticationComplete event.
1177
	 * This method is invoked right after the user request is authenticated.
1178
	 */
1179
	public function onAuthenticationComplete()
1180
	{
1181
		$this->raiseEvent('OnAuthenticationComplete', $this, null);
1182
	}
1183
1184
	/**
1185
	 * Raises OnAuthorization event.
1186
	 * This method is invoked when the user request needs to be authorized.
1187
	 */
1188
	public function onAuthorization()
1189
	{
1190
		$this->raiseEvent('OnAuthorization', $this, null);
1191
	}
1192
1193
	/**
1194
	 * Raises OnAuthorizationComplete event.
1195
	 * This method is invoked right after the user request is authorized.
1196
	 */
1197
	public function onAuthorizationComplete()
1198
	{
1199
		$this->raiseEvent('OnAuthorizationComplete', $this, null);
1200
	}
1201
1202
	/**
1203
	 * Raises OnLoadState event.
1204
	 * This method is invoked when the application needs to load state (probably stored in session).
1205
	 */
1206
	public function onLoadState()
1207
	{
1208
		$this->loadGlobals();
1209
		$this->raiseEvent('OnLoadState', $this, null);
1210
	}
1211
1212
	/**
1213
	 * Raises OnLoadStateComplete event.
1214
	 * This method is invoked right after the application state has been loaded.
1215
	 */
1216
	public function onLoadStateComplete()
1217
	{
1218
		$this->raiseEvent('OnLoadStateComplete', $this, null);
1219
	}
1220
1221
	/**
1222
	 * Raises OnPreRunService event.
1223
	 * This method is invoked right before the service is to be run.
1224
	 */
1225
	public function onPreRunService()
1226
	{
1227
		$this->raiseEvent('OnPreRunService', $this, null);
1228
	}
1229
1230
	/**
1231
	 * Runs the requested service.
1232
	 */
1233
	public function runService()
1234
	{
1235
		if ($this->_service) {
1236
			$this->_service->run();
1237
		}
1238
	}
1239
1240
	/**
1241
	 * Raises OnSaveState event.
1242
	 * This method is invoked when the application needs to save state (probably stored in session).
1243
	 */
1244
	public function onSaveState()
1245
	{
1246
		$this->raiseEvent('OnSaveState', $this, null);
1247
		$this->saveGlobals();
1248
	}
1249
1250
	/**
1251
	 * Raises OnSaveStateComplete event.
1252
	 * This method is invoked right after the application state has been saved.
1253
	 */
1254
	public function onSaveStateComplete()
1255
	{
1256
		$this->raiseEvent('OnSaveStateComplete', $this, null);
1257
	}
1258
1259
	/**
1260
	 * Raises OnPreFlushOutput event.
1261
	 * This method is invoked right before the application flushes output to client.
1262
	 */
1263
	public function onPreFlushOutput()
1264
	{
1265
		$this->raiseEvent('OnPreFlushOutput', $this, null);
1266
	}
1267
1268
	/**
1269
	 * Flushes output to client side.
1270
	 * @param bool $continueBuffering whether to continue buffering after flush if buffering was active
1271
	 */
1272
	public function flushOutput($continueBuffering = true)
1273
	{
1274
		$this->getResponse()->flush($continueBuffering);
1275
	}
1276
1277
	/**
1278
	 * Raises OnEndRequest event.
1279
	 * This method is invoked when the application completes the processing of the request.
1280
	 */
1281
	public function onEndRequest()
1282
	{
1283
		$this->flushOutput(false); // flush all remaining content in the buffer
1284
		$this->raiseEvent('OnEndRequest', $this, null);
1285
		$this->saveGlobals();  // save global state
1286
	}
1287
}
1288