Passed
Push — master ( 4e3c19...6e35e1 )
by Fabio
06:44 queued 01:59
created

TApplication::run()   B

Complexity

Conditions 6
Paths 15

Size

Total Lines 26
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

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

365
					/** @scrutinizer ignore-unhandled */ @chmod($runtimePath, Prado::getDefaultDirPermissions()); //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...
366
				}
367
				$this->setConfigurationFile($configFile);
368
			}
369
			$this->setBasePath($basePath);
370
			$this->setRuntimePath($runtimePath);
371
		} else {
372
			throw new TConfigurationException('application_runtimepath_invalid', $runtimePath);
373
		}
374
	}
375
376
	/**
377
	 * Executes the lifecycles of the application.
378
	 * This is the main entry function that leads to the running of the whole
379
	 * Prado application.
380
	 */
381
	public function run()
382
	{
383
		try {
384
			$this->initApplication();
385
			$n = count(self::$_steps);
386
			$this->_step = 0;
387
			$this->_requestCompleted = false;
388
			while ($this->_step < $n) {
389
				if ($this->_mode === TApplicationMode::Off) {
390
					throw new THttpException(503, 'application_unavailable');
391
				}
392
				if ($this->_requestCompleted) {
393
					break;
394
				}
395
				$method = self::$_steps[$this->_step];
396
				Prado::trace("Executing $method()", TApplication::class);
397
				$this->$method();
398
				$this->_step++;
399
			}
400
		} catch (TExitException $e) {
401
			$this->onEndRequest();
402
			exit($e->getExitCode());
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

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

702
			$module[0]->/** @scrutinizer ignore-call */ 
703
               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...
703
		}
704
705
		return $this->_modules[$id];
706
	}
707
708
	/**
709
	 * Returns a list of application modules indexed by module IDs.
710
	 * Modules that have not been loaded yet are returned as null objects.
711
	 * @return array<TModule> list of loaded application modules, indexed by module IDs
712
	 */
713 18
	public function getModules()
714
	{
715 18
		return $this->_modules;
716 1
	}
717 1
718
	/**
719 18
	 * Returns a list of application modules of a specific class.
720
	 * Lazy Loading Modules are not loaded, and are null but have an ID Key.
721
	 * When null modules are found, load them with {@link getModule}. eg.
722
	 * <code>
723
	 *	foreach (Prado::getApplication()->getModulesByType(\Prado\Caching\ICache::class) as $id => $module) {
724
	 *		$module = (!$module) ? $app->getModule($id) : $module;
725 42
	 *		...
726
	 *	}
727 42
	 * </code>
728 42
	 * @param string $type class name of the modules to look for.
729
	 * @param bool $strict should the module be the class or can the module be a subclass
730
	 * @return array keys are the ids of the module and values are module of a specific class
731
	 * @since 4.2.0
732
	 */
733 1
	public function getModulesByType($type, $strict = false)
734
	{
735 1
		$m = [];
736
		foreach ($this->_modules as $id => $module) {
737
			if ($module === null && isset($this->_lazyModules[$id])) {
738
				[$moduleClass, $initProperties, $configElement] = $this->_lazyModules[$id];
739 1
				if ($strict ? ($moduleClass === $type) : ($moduleClass instanceof $type)) {
740
					$m[$id] = null;
741
				}
742
			} elseif ($module !== null && ($strict ? ($module::class === $type) : $module->isa($type))) {
743
				$m[$id] = $module;
744
			}
745 8
		}
746
		return $m;
747 8
	}
748 8
749
	/**
750
	 * Returns the list of application parameters.
751
	 * Since the parameters are returned as a {@link \Prado\Collections\TMap} object, you may use
752
	 * the returned result to access, add or remove individual parameters.
753
	 * @return \Prado\Collections\TMap the list of application parameters
754
	 */
755
	public function getParameters()
756
	{
757
		return $this->_parameters;
758
	}
759
760
	/**
761
	 * @return THttpRequest the request module
762
	 */
763
	public function getRequest()
764
	{
765 10
		if (!$this->_request) {
766
			$this->_request = new \Prado\Web\THttpRequest();
767 10
			$this->_request->init(null);
768 10
		}
769
		return $this->_request;
770
	}
771
772
	/**
773
	 * @param THttpRequest $request the request module
774
	 */
775
	public function setRequest(THttpRequest $request)
776
	{
777
		$this->_request = $request;
778
	}
779
780
	/**
781
	 * @return THttpResponse the response module
782
	 */
783
	public function getResponse()
784
	{
785
		if (!$this->_response) {
786
			$this->_response = new THttpResponse();
787
			$this->_response->init(null);
788
		}
789
		return $this->_response;
790
	}
791
792
	/**
793 2
	 * @param THttpResponse $response the request module
794
	 */
795 2
	public function setResponse(THttpResponse $response)
796 1
	{
797 1
		$this->_response = $response;
798
	}
799 2
800
	/**
801
	 * @return THttpSession the session module, null if session module is not installed
802
	 */
803
	public function getSession()
804
	{
805 9
		if (!$this->_session) {
806
			$this->_session = new THttpSession();
807 9
			$this->_session->init(null);
808 9
		}
809
		return $this->_session;
810
	}
811
812
	/**
813 1
	 * @param THttpSession $session the session module
814
	 */
815 1
	public function setSession(THttpSession $session)
816
	{
817
		$this->_session = $session;
818
	}
819 1
820
	/**
821
	 * @return TErrorHandler the error handler module
822
	 */
823
	public function getErrorHandler()
824
	{
825 6
		if (!$this->_errorHandler) {
826
			$this->_errorHandler = new TErrorHandler();
827 6
			$this->_errorHandler->init(null);
828 6
		}
829
		return $this->_errorHandler;
830
	}
831
832
	/**
833 3
	 * @param TErrorHandler $handler the error handler module
834
	 */
835 3
	public function setErrorHandler(TErrorHandler $handler)
836 2
	{
837 2
		$this->_errorHandler = $handler;
838
	}
839 3
840
	/**
841
	 * @return TSecurityManager the security manager module
842
	 */
843
	public function getSecurityManager()
844
	{
845 2
		if (!$this->_security) {
846
			$this->_security = new TSecurityManager();
847 2
			$this->_security->init(null);
848 2
		}
849
		return $this->_security;
850
	}
851
852
	/**
853 20
	 * @param TSecurityManager $sm the security manager module
854
	 */
855 20
	public function setSecurityManager(TSecurityManager $sm)
856
	{
857
		$this->_security = $sm;
858
	}
859
860
	/**
861 17
	 * @return TAssetManager asset manager
862
	 */
863 17
	public function getAssetManager()
864 17
	{
865
		if (!$this->_assetManager) {
866
			$this->_assetManager = new TAssetManager();
867
			$this->_assetManager->init(null);
868
		}
869
		return $this->_assetManager;
870
	}
871
872
	/**
873
	 * @param TAssetManager $value asset manager
874
	 */
875
	public function setAssetManager(TAssetManager $value)
876
	{
877
		$this->_assetManager = $value;
878
	}
879
880
	/**
881
	 * @return TTemplateManager template manager
882
	 */
883
	public function getTemplateManager()
884
	{
885
		if (!$this->_templateManager) {
886
			$this->_templateManager = new TTemplateManager();
887
			$this->_templateManager->init(null);
888
		}
889
		return $this->_templateManager;
890
	}
891
892
	/**
893
	 * @param TTemplateManager $value template manager
894
	 */
895
	public function setTemplateManager(TTemplateManager $value)
896
	{
897
		$this->_templateManager = $value;
898
	}
899
900
	/**
901
	 * @return TThemeManager theme manager
902
	 */
903
	public function getThemeManager()
904
	{
905
		if (!$this->_themeManager) {
906
			$this->_themeManager = new TThemeManager();
907
			$this->_themeManager->init(null);
908
		}
909
		return $this->_themeManager;
910
	}
911
912
	/**
913
	 * @param TThemeManager $value theme manager
914
	 */
915
	public function setThemeManager(TThemeManager $value)
916
	{
917
		$this->_themeManager = $value;
918
	}
919
920
	/**
921
	 * @return IStatePersister application state persister
922
	 */
923
	public function getApplicationStatePersister()
924
	{
925
		if (!$this->_statePersister) {
926
			$this->_statePersister = new TApplicationStatePersister();
927
			$this->_statePersister->init(null);
928
		}
929
		return $this->_statePersister;
930
	}
931
932
	/**
933
	 * @param IStatePersister $persister application state persister
934
	 */
935
	public function setApplicationStatePersister(IStatePersister $persister)
936
	{
937
		$this->_statePersister = $persister;
938
	}
939
940
	/**
941
	 * @return null|\Prado\Caching\ICache the cache module, null if cache module is not installed
942
	 */
943
	public function getCache()
944
	{
945
		return $this->_cache;
946
	}
947
948
	/**
949
	 * @param \Prado\Caching\ICache $cache the cache module
950
	 */
951
	public function setCache(\Prado\Caching\ICache $cache)
952
	{
953
		$this->_cache = $cache;
954
	}
955
956
	/**
957
	 * @return \Prado\Security\IUser the application user
958
	 */
959
	public function getUser()
960
	{
961
		return $this->_user;
962
	}
963
964
	/**
965
	 * This sets the application user and raises the onSetUser event.
966
	 * @param \Prado\Security\IUser $user the application user
967
	 */
968
	public function setUser(\Prado\Security\IUser $user)
969
	{
970
		$this->_user = $user;
971
		$this->onSetUser($user);
972
	}
973
974
	/**
975
	 * Raises onSetUser event.
976
	 * Allows modules/components to run handlers when the Application User is set.
977
	 * e.g. A user module could set the $_SERVER['HTTP_ACCEPT_LANGUAGE'] and
978
	 * $_SERVER['HTTP_ACCEPT_CHARSET'] in a cli environment to the user's last
979
	 * web Language and Charset so Emails (and other templates) get language
980
	 * customized.
981
	 * @param \Prado\Security\IUser $user
982
	 * @since 4.2.2
983
	 */
984
	public function onSetUser(\Prado\Security\IUser $user)
985
	{
986
		$this->raiseEvent('onSetUser', $this, $user);
0 ignored issues
show
Bug introduced by
$user of type Prado\Security\IUser is incompatible with the type Prado\TEventParameter expected by parameter $param of Prado\TComponent::raiseEvent(). ( Ignorable by Annotation )

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

986
		$this->raiseEvent('onSetUser', $this, /** @scrutinizer ignore-type */ $user);
Loading history...
987
	}
988
989
	/**
990
	 * @param bool $createIfNotExists whether to create globalization if it does not exist
991
	 * @return null|TGlobalization globalization module
992
	 */
993
	public function getGlobalization($createIfNotExists = true)
994
	{
995
		if ($this->_globalization === null && $createIfNotExists) {
996
			$this->_globalization = new TGlobalization();
997
			$this->_globalization->init(null);
998
		}
999
		return $this->_globalization;
1000
	}
1001
1002
	/**
1003
	 * @param \Prado\I18N\TGlobalization $glob globalization module
1004
	 */
1005
	public function setGlobalization(\Prado\I18N\TGlobalization $glob)
1006
	{
1007
		$this->_globalization = $glob;
1008
	}
1009
1010
	/**
1011
	 * @return \Prado\Security\TAuthorizationRuleCollection list of authorization rules for the current request
1012
	 */
1013
	public function getAuthorizationRules()
1014
	{
1015
		if ($this->_authRules === null) {
1016
			$this->_authRules = new \Prado\Security\TAuthorizationRuleCollection();
1017
		}
1018
		return $this->_authRules;
1019
	}
1020
1021
	protected function getApplicationConfigurationClass()
1022
	{
1023
		return TApplicationConfiguration::class;
1024
	}
1025
1026
	protected function internalLoadModule($id, $force = false)
1027
	{
1028
		[$moduleClass, $initProperties, $configElement] = $this->_lazyModules[$id];
1029
		if (isset($initProperties['lazy']) && $initProperties['lazy'] && !$force) {
1030
			Prado::trace("Postponed loading of lazy module $id ({$moduleClass})", TApplication::class);
1031
			$this->setModule($id, null);
1032
			return null;
1033
		}
1034
1035
		Prado::trace("Loading module $id ({$moduleClass})", TApplication::class);
1036
		$module = Prado::createComponent($moduleClass);
1037
		foreach ($initProperties as $name => $value) {
1038
			if ($name === 'lazy') {
1039
				continue;
1040
			}
1041
			$module->setSubProperty($name, $value);
1042
		}
1043
		$this->setModule($id, $module);
1044
		// keep the key to avoid reuse of the old module id
1045
		$this->_lazyModules[$id] = null;
1046
		$module->dyPreInit($configElement);
0 ignored issues
show
Bug introduced by
The method dyPreInit() does not exist on Prado\IModule. It seems like you code against a sub-type of said class. However, the method does not exist in Prado\Util\IDbModule or Prado\Util\IPluginModule. Are you sure you never get one of those? ( Ignorable by Annotation )

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

1046
		$module->/** @scrutinizer ignore-call */ 
1047
           dyPreInit($configElement);
Loading history...
1047
1048
		return [$module, $configElement];
1049
	}
1050
	/**
1051
	 * Applies an application configuration.
1052
	 * @param TApplicationConfiguration $config the configuration
1053
	 * @param bool $withinService whether the configuration is specified within a service.
1054
	 */
1055
	public function applyConfiguration($config, $withinService = false)
1056
	{
1057
		if ($config->getIsEmpty()) {
1058
			return;
1059
		}
1060
1061
		// set path aliases and using namespaces
1062
		foreach ($config->getAliases() as $alias => $path) {
1063
			Prado::setPathOfAlias($alias, $path);
1064
		}
1065
		foreach ($config->getUsings() as $using) {
1066
			Prado::using($using);
1067
		}
1068
1069
		// set application properties
1070
		if (!$withinService) {
1071
			foreach ($config->getProperties() as $name => $value) {
1072
				$this->setSubProperty($name, $value);
1073
			}
1074
		}
1075
1076
		if (empty($this->_services)) {
1077
			$this->_services = [$this->getPageServiceID() => [\Prado\Web\Services\TPageService::class, [], null]];
1078
		}
1079
1080
		// load parameters
1081
		foreach ($config->getParameters() as $id => $parameter) {
1082
			if (is_array($parameter)) {
1083
				$component = Prado::createComponent($parameter[0]);
1084
				foreach ($parameter[1] as $name => $value) {
1085
					$component->setSubProperty($name, $value);
1086
				}
1087
				$component->dyInit($parameter[2]);
1088
				$this->_parameters->add($id, $component);
1089
			} else {
1090
				$this->_parameters->add($id, $parameter);
1091
			}
1092
		}
1093
1094
		// load and init modules specified in app config
1095
		$modules = [];
1096
		foreach ($config->getModules() as $id => $moduleConfig) {
1097
			if (!is_string($id)) {
1098
				$id = '_module' . count($this->_lazyModules);
1099
			}
1100
			$this->_lazyModules[$id] = $moduleConfig;
1101
			if ($module = $this->internalLoadModule($id)) {
1102
				$modules[] = $module;
1103
			}
1104
		}
1105
		foreach ($modules as $module) {
1106
			$module[0]->init($module[1]);
1107
		}
1108
1109
		// load service
1110
		foreach ($config->getServices() as $serviceID => $serviceConfig) {
1111
			$this->_services[$serviceID] = $serviceConfig;
1112
		}
1113
1114
		// external configurations
1115
		foreach ($config->getExternalConfigurations() as $filePath => $condition) {
1116
			if ($condition !== true) {
1117
				$condition = $this->evaluateExpression($condition);
1118
			}
1119
			if ($condition) {
1120
				if (($path = Prado::getPathOfNamespace($filePath, $this->getConfigurationFileExt())) === null || !is_file($path)) {
1121
					throw new TConfigurationException('application_includefile_invalid', $filePath);
1122
				}
1123
				$cn = $this->getApplicationConfigurationClass();
1124
				$c = new $cn();
1125
				$c->loadFromFile($path);
1126
				$this->applyConfiguration($c, $withinService);
1127
			}
1128
		}
1129
	}
1130
1131
	/**
1132
	 * Loads configuration and initializes application.
1133
	 * Configuration file will be read and parsed (if a valid cached version exists,
1134
	 * it will be used instead). Then, modules are created and initialized;
1135
	 * Afterwards, the requested service is created and initialized.
1136
	 * Lastly, the onInitComplete event is raised.
1137
	 * @throws TConfigurationException if module is redefined of invalid type, or service not defined or of invalid type
1138
	 */
1139
	protected function initApplication()
1140
	{
1141
		Prado::trace('Initializing application', TApplication::class);
1142
1143
		if ($this->_configFile !== null) {
1144
			if ($this->_cacheFile === null || @filemtime($this->_cacheFile) < filemtime($this->_configFile)) {
1145
				$config = new TApplicationConfiguration();
1146
				$config->loadFromFile($this->_configFile);
1147
				if ($this->_cacheFile !== null) {
1148
					file_put_contents($this->_cacheFile, serialize($config), LOCK_EX);
1149
				}
1150
			} else {
1151
				$config = unserialize(file_get_contents($this->_cacheFile));
1152
			}
1153
1154
			$this->applyConfiguration($config, false);
1155
		}
1156
1157
		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...
1158
			$serviceID = $this->getPageServiceID();
1159
		}
1160
1161
		$this->startService($serviceID);
1162
1163
		$this->onInitComplete();
1164
	}
1165
1166
	/**
1167
	 * Starts the specified service.
1168
	 * The service instance will be created. Its properties will be initialized
1169
	 * and the configurations will be applied, if any.
1170
	 * @param string $serviceID service ID
1171
	 */
1172
	public function startService($serviceID)
1173
	{
1174
		if (isset($this->_services[$serviceID])) {
1175
			[$serviceClass, $initProperties, $configElement] = $this->_services[$serviceID];
1176
			$service = Prado::createComponent($serviceClass);
1177
			if (!($service instanceof TService)) {
1178
				throw new THttpException(500, 'application_service_invalid', $serviceClass);
1179
			}
1180
			if (!$service->getEnabled()) {
1181
				throw new THttpException(500, 'application_service_unavailable', $serviceClass);
1182
			}
1183
			$service->setID($serviceID);
1184
			$this->setService($service);
1185
1186
			foreach ($initProperties as $name => $value) {
1187
				$service->setSubProperty($name, $value);
1188
			}
1189
1190
			if ($configElement !== null) {
1191
				$config = new TApplicationConfiguration();
1192
				if ($this->getConfigurationType() == self::CONFIG_TYPE_PHP) {
1193
					$config->loadFromPhp($configElement, $this->getBasePath());
1194
				} else {
1195
					$config->loadFromXml($configElement, $this->getBasePath());
1196
				}
1197
				$this->applyConfiguration($config, true);
1198
			}
1199
1200
			$service->init($configElement);
1201
		} else {
1202
			throw new THttpException(500, 'application_service_unknown', $serviceID);
1203
		}
1204
	}
1205
1206
	/**
1207
	 * Raises OnError event.
1208
	 * This method is invoked when an exception is raised during the lifecycles
1209
	 * of the application.
1210
	 * @param mixed $param event parameter
1211
	 */
1212
	public function onError($param)
1213
	{
1214
		Prado::log($param->getMessage(), TLogger::ERROR, TApplication::class);
1215
		$this->raiseEvent('OnError', $this, $param);
1216
		$this->getErrorHandler()->handleError($this, $param);
1217
	}
1218
1219
	/**
1220
	 * Raises onInitComplete event.
1221
	 * At the time when this method is invoked, application modules are loaded,
1222
	 * user request is resolved and the corresponding service is loaded and
1223
	 * initialized. The application is about to start processing the user
1224
	 * request.  This call is important for CLI/Shell applications that do not have
1225
	 * a web service lifecycle stack.  This is the first and last event for finalization
1226
	 * of any loaded modules in CLI/Shell mode.
1227
	 * @since 4.2.0
1228
	 */
1229
	public function onInitComplete()
1230
	{
1231
		$this->raiseEvent('onInitComplete', $this, null);
1232
	}
1233
1234
	/**
1235
	 * Raises OnBeginRequest event.
1236
	 * At the time when this method is invoked, application modules are loaded
1237
	 * and initialized, user request is resolved and the corresponding service
1238
	 * is loaded and initialized. The application is about to start processing
1239
	 * the user request.
1240
	 */
1241
	public function onBeginRequest()
1242
	{
1243
		$this->raiseEvent('OnBeginRequest', $this, null);
1244
	}
1245
1246
	/**
1247
	 * Raises OnAuthentication event.
1248
	 * This method is invoked when the user request needs to be authenticated.
1249
	 */
1250
	public function onAuthentication()
1251
	{
1252
		$this->raiseEvent('OnAuthentication', $this, null);
1253
	}
1254
1255
	/**
1256
	 * Raises OnAuthenticationComplete event.
1257
	 * This method is invoked right after the user request is authenticated.
1258
	 */
1259
	public function onAuthenticationComplete()
1260
	{
1261
		$this->raiseEvent('OnAuthenticationComplete', $this, null);
1262
	}
1263
1264
	/**
1265
	 * Raises OnAuthorization event.
1266
	 * This method is invoked when the user request needs to be authorized.
1267
	 */
1268
	public function onAuthorization()
1269
	{
1270
		$this->raiseEvent('OnAuthorization', $this, null);
1271
	}
1272
1273
	/**
1274
	 * Raises OnAuthorizationComplete event.
1275
	 * This method is invoked right after the user request is authorized.
1276
	 */
1277
	public function onAuthorizationComplete()
1278
	{
1279
		$this->raiseEvent('OnAuthorizationComplete', $this, null);
1280
	}
1281
1282
	/**
1283
	 * Raises OnLoadState event.
1284
	 * This method is invoked when the application needs to load state (probably stored in session).
1285
	 */
1286
	public function onLoadState()
1287
	{
1288
		$this->loadGlobals();
1289
		$this->raiseEvent('OnLoadState', $this, null);
1290
	}
1291
1292
	/**
1293
	 * Raises OnLoadStateComplete event.
1294
	 * This method is invoked right after the application state has been loaded.
1295
	 */
1296
	public function onLoadStateComplete()
1297
	{
1298
		$this->raiseEvent('OnLoadStateComplete', $this, null);
1299
	}
1300
1301
	/**
1302
	 * Raises OnPreRunService event.
1303
	 * This method is invoked right before the service is to be run.
1304
	 */
1305
	public function onPreRunService()
1306
	{
1307
		$this->raiseEvent('OnPreRunService', $this, null);
1308
	}
1309
1310
	/**
1311
	 * Runs the requested service.
1312
	 */
1313
	public function runService()
1314
	{
1315
		if ($this->_service) {
1316
			$this->_service->run();
1317
		}
1318
	}
1319
1320
	/**
1321
	 * Raises OnSaveState event.
1322
	 * This method is invoked when the application needs to save state (probably stored in session).
1323
	 */
1324
	public function onSaveState()
1325
	{
1326
		$this->raiseEvent('OnSaveState', $this, null);
1327
		$this->saveGlobals();
1328
	}
1329
1330
	/**
1331
	 * Raises OnSaveStateComplete event.
1332
	 * This method is invoked right after the application state has been saved.
1333
	 */
1334
	public function onSaveStateComplete()
1335
	{
1336
		$this->raiseEvent('OnSaveStateComplete', $this, null);
1337
	}
1338
1339
	/**
1340
	 * Raises OnPreFlushOutput event.
1341
	 * This method is invoked right before the application flushes output to client.
1342
	 */
1343
	public function onPreFlushOutput()
1344
	{
1345
		$this->raiseEvent('OnPreFlushOutput', $this, null);
1346
	}
1347
1348
	/**
1349
	 * Flushes output to client side.
1350
	 * @param bool $continueBuffering whether to continue buffering after flush if buffering was active
1351
	 */
1352
	public function flushOutput($continueBuffering = true)
1353
	{
1354
		$this->getResponse()->flush($continueBuffering);
1355
	}
1356
1357
	/**
1358
	 * Raises OnEndRequest event.
1359
	 * This method is invoked when the application completes the processing of the request.
1360
	 */
1361
	public function onEndRequest()
1362
	{
1363
		$this->flushOutput(false); // flush all remaining content in the buffer
1364
		$this->raiseEvent('OnEndRequest', $this, null);
1365
		$this->saveGlobals();  // save global state
1366
	}
1367
}
1368