Passed
Push — master ( 60550c...9acbb8 )
by Fabio
06:08
created

Prado::error()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 3
dl 0
loc 6
ccs 0
cts 0
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Prado class file.
4
 *
5
 * This is the file that establishes the PRADO component model
6
 * and error handling mechanism.
7
 *
8
 * @author Qiang Xue <[email protected]>
9
 * @link https://github.com/pradosoft/prado
10
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
11
 */
12
13
namespace Prado;
14
15
use Prado\Exceptions\TInvalidDataValueException;
16
use Prado\Exceptions\TInvalidOperationException;
17
use Prado\Exceptions\TPhpErrorException;
18
use Prado\Exceptions\TPhpFatalErrorException;
19
use Prado\Util\TLogger;
20
use Prado\Util\TVarDumper;
21
use Prado\I18N\Translation;
22
23
// Defines the PRADO framework installation path.
24
if (!defined('PRADO_DIR')) {
25
	define('PRADO_DIR', __DIR__);
26
}
27
28
// Defines the default permission for writable directories
29
// @todo, the test on PRADO_CHMOD must be remove in the next major release
30
if (!defined('PRADO_DIR_CHMOD')) {
31
	define('PRADO_DIR_CHMOD', !defined('PRADO_CHMOD') ? 0o755 : PRADO_CHMOD);
0 ignored issues
show
Bug introduced by
The constant Prado\0o755 was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
32
}
33
34
// Defines the default permission for writable files
35
// @todo, the test on PRADO_CHMOD must be removed in the next major release
36
if (!defined('PRADO_FILE_CHMOD')) {
37
	define('PRADO_FILE_CHMOD', !defined('PRADO_CHMOD') ? 0o644 : PRADO_CHMOD);
0 ignored issues
show
Bug introduced by
The constant Prado\0o644 was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
38
}
39
40
// Defines the default permission for writable directories and files
41
// @todo, adding this define must be removed in the next major release
42
if (!defined('PRADO_CHMOD')) {
43
	define('PRADO_CHMOD', 0o777);
0 ignored issues
show
Bug introduced by
The constant Prado\0o777 was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
44
}
45
46
// Defines the Composer's vendor/ path.
47
if (!defined('PRADO_VENDORDIR')) {
48
	$reflector = new \ReflectionClass('\Composer\Autoload\ClassLoader');
49
	define('PRADO_VENDORDIR', dirname((string) $reflector->getFileName(), 2));
50
	unset($reflector);
51
}
52
53
/**
54
 * Prado class.
55
 *
56
 * Prado implements a few fundamental static methods.
57
 *
58
 * To use the static methods, Use Prado as the class name rather than Prado.
59
 * Prado is meant to serve as the base class of Prado. The latter might be
60
 * rewritten for customization.
61
 *
62
 * @author Qiang Xue <[email protected]>
63
 * @since 3.0
64
 */
65
class Prado
66
{
67
	/**
68
	 * File extension for Prado class files.
69
	 */
70
	public const CLASS_FILE_EXT = '.php';
71
	/**
72
	 * @var array<string, string> list of path aliases
73
	 */
74
	private static $_aliases = [
75
		'Prado' => PRADO_DIR,
76
		'Vendor' => PRADO_VENDORDIR,
77
		];
78
	/**
79
	 * @var array<string, string> list of namespaces currently in use
80
	 */
81
	private static $_usings = [
82
		'Prado' => PRADO_DIR,
83
		];
84
	/**
85
	 * @var array<string, string> list of namespaces currently in use
86
	 */
87
	public static $classMap = [];
88
	/**
89
	 * @var null|TApplication the application instance
90
	 */
91
	private static $_application;
92
	/**
93
	 * @var null|TLogger logger instance
94
	 */
95
	private static $_logger;
96
	/**
97
	 * @var array<string, bool> list of class exists checks
98
	 */
99
	protected static $classExists = [];
100
	/**
101
	 * @return string the version of Prado framework
102
	 */
103
	public static function getVersion(): string
104
	{
105
		return '4.2.2';
106
	}
107
108
	/**
109
	 * Initializes the Prado static class
110
	 */
111
	public static function init(): void
112
	{
113
		static::initAutoloader();
114
		static::initErrorHandlers();
115
	}
116
117
	/**
118
	 * Loads the static classmap and registers the autoload function.
119
	 */
120
	public static function initAutoloader(): void
121
	{
122
		self::$classMap = require(__DIR__ . '/classes.php');
123
124
		spl_autoload_register([static::class, 'autoload']);
125
	}
126
127
	/**
128
	 * Initializes error handlers.
129
	 * This method set error and exception handlers to be functions
130
	 * defined in this class.
131
	 */
132
	public static function initErrorHandlers(): void
133
	{
134
		/**
135
		 * Sets error handler to be Prado::phpErrorHandler
136
		 */
137
		set_error_handler([static::class, 'phpErrorHandler']);
138
		/**
139
		 * Sets shutdown function to be Prado::phpFatalErrorHandler
140
		 */
141
		register_shutdown_function([static::class, 'phpFatalErrorHandler']);
142
		/**
143
		 * Sets exception handler to be Prado::exceptionHandler
144
		 */
145
		set_exception_handler([static::class, 'exceptionHandler']);
146
		/**
147
		 * Disable php's builtin error reporting to avoid duplicated reports
148
		 */
149
		ini_set('display_errors', '0');
150
	}
151
152
	/**
153
	 * Class autoload loader.
154
	 * This method is provided to be registered within an spl_autoload_register() method.
155
	 * @param string $className class name
156
	 */
157
	public static function autoload($className): void
158
	{
159
		static::using($className);
160
	}
161
162
	/**
163
	 * @param int $logoType the type of "powered logo". Valid values include 0 and 1.
164
	 * @return string a string that can be displayed on your Web page showing powered-by-PRADO information
165
	 */
166
	public static function poweredByPrado($logoType = 0): string
167
	{
168
		$logoName = $logoType == 1 ? 'powered2' : 'powered';
169
		if (self::$_application !== null) {
170
			$am = self::$_application->getAssetManager();
171
			$url = $am->publishFilePath((string) self::getPathOfNamespace('Prado\\' . $logoName, '.gif'));
172
		} else {
173
			$url = 'http://pradosoft.github.io/docs/' . $logoName . '.gif';
174
		}
175
		return '<a title="Powered by PRADO" href="https://github.com/pradosoft/prado" target="_blank"><img src="' . $url . '" style="border-width:0px;" alt="Powered by PRADO" /></a>';
176
	}
177
178
	/**
179
	 * PHP error handler.
180
	 * This method should be registered as PHP error handler using
181
	 * {@link set_error_handler}. The method throws an exception that
182
	 * contains the error information.
183
	 * @param int $errno the level of the error raised
184
	 * @param string $errstr the error message
185
	 * @param string $errfile the filename that the error was raised in
186
	 * @param int $errline the line number the error was raised at
187
	 */
188
	public static function phpErrorHandler($errno, $errstr, $errfile, $errline): bool
189
	{
190
		if (error_reporting() & $errno) {
191
			throw new TPhpErrorException($errno, $errstr, $errfile, $errline);
192
		}
193
		return true;
194
	}
195
196
	/**
197
	 * PHP shutdown function used to catch fatal errors.
198
	 * This method should be registered as PHP error handler using
199
	 * {@link register_shutdown_function}. The method throws an exception that
200
	 * contains the error information.
201
	 */
202
	public static function phpFatalErrorHandler(): void
203
	{
204
		$error = error_get_last();
205
		if ($error &&
206
			TPhpErrorException::isFatalError($error) &&
207
			error_reporting() & $error['type']) {
208
			self::exceptionHandler(new TPhpFatalErrorException($error['type'], $error['message'], $error['file'], $error['line']));
209
		}
210
	}
211
212
	/**
213
	 * Default exception handler.
214
	 * This method should be registered as default exception handler using
215
	 * {@link set_exception_handler}. The method tries to use the errorhandler
216
	 * module of the Prado application to handle the exception.
217
	 * If the application or the module does not exist, it simply echoes the
218
	 * exception.
219
	 * @param \Throwable $exception exception that is not caught
220
	 */
221
	public static function exceptionHandler($exception): void
222
	{
223
		if (self::$_application !== null && ($errorHandler = self::$_application->getErrorHandler()) !== null) {
224
			$errorHandler->handleError(null, $exception);
225
		} else {
226
			echo $exception;
227
		}
228
		exit(1);
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...
229
	}
230
231
	/**
232
	 * Stores the application instance in the class static member.
233
	 * This method helps implement a singleton pattern for TApplication.
234
	 * Repeated invocation of this method or the application constructor
235
	 * will cause the throw of an exception.
236
	 * This method should only be used by framework developers.
237
	 * @param TApplication $application the application instance
238
	 * @throws TInvalidOperationException if this method is invoked twice or more.
239
	 */
240
	public static function setApplication($application): void
241
	{
242
		if (self::$_application !== null && !defined('PRADO_TEST_RUN')) {
243
			throw new TInvalidOperationException('prado_application_singleton_required');
244
		}
245
		self::$_application = $application;
246
	}
247
248
	/**
249
	 * @return null|TApplication the application singleton, null if the singleton has not be created yet.
250
	 */
251
	public static function getApplication(): ?TApplication
252
	{
253
		return self::$_application;
254
	}
255
256
	/**
257
	 * @return string the path of the framework
258
	 */
259
	public static function getFrameworkPath(): string
260
	{
261
		return PRADO_DIR;
262
	}
263
264
	/**
265
	 * @deprecated deprecated since version 4.2.2, replaced by @getDefaultDirPermissions and @getDefaultFilePermissions
266
	 * @return int chmod permissions, defaults to 0777
267
	 */
268
	public static function getDefaultPermissions(): int
269
	{
270
		return PRADO_CHMOD;
271
	}
272
273
	/**
274
	 * @return int chmod dir permissions, defaults to 0755
275
	 */
276
	public static function getDefaultDirPermissions(): int
277
	{
278
		return PRADO_DIR_CHMOD;
279
	}
280
281
	/**
282
	 * @return int chmod file permissions, defaults to 0644
283
	 */
284
	public static function getDefaultFilePermissions(): int
285
	{
286
		return PRADO_FILE_CHMOD;
287
	}
288
289
	/**
290
	 * Convert old Prado namespaces to PHP namespaces
291
	 * @param string $type old class name in Prado3 namespace format
292
	 * @return string Equivalent class name in PHP namespace format
293
	 */
294
	protected static function prado3NamespaceToPhpNamespace($type): string
295
	{
296
		if (substr($type, 0, 6) === 'System') {
297
			$type = 'Prado' . substr($type, 6);
298
		}
299
300
		if (false === strpos($type, '\\')) {
301
			return str_replace('.', '\\', $type);
302
		} else {
303
			return $type;
304
		}
305
	}
306
307
	/**
308
	 * Creates a component with the specified type.
309
	 * A component type can be either the component class name
310
	 * or a namespace referring to the path of the component class file.
311
	 * For example, 'TButton', '\Prado\Web\UI\WebControls\TButton' are both
312
	 * valid component type.
313
	 * This method can also pass parameters to component constructors.
314
	 * All parameters passed to this method except the first one (the component type)
315
	 * will be supplied as component constructor parameters.
316
	 * @template T
317
	 * @param class-string<T> $requestedType component type
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
318
	 * @param array<mixed> $params
319
	 * @throws TInvalidDataValueException if the component type is unknown
320
	 * @return T component instance of the specified type
321
	 */
322
	public static function createComponent($requestedType, ...$params)
323
	{
324
		$properties = null;
325
		if (is_array($requestedType)) {
326
			$properties = $requestedType;
327
			$requestedType = $properties['class'];
328
			unset($properties['class']);
329
		}
330
		$type = static::prado3NamespaceToPhpNamespace($requestedType);
331
		if (!isset(self::$classExists[$type])) {
332
			self::$classExists[$type] = class_exists($type, false);
333
		}
334
335
		if (!isset(self::$_usings[$type]) && !self::$classExists[$type]) {
336
			static::using($type);
337
			self::$classExists[$type] = class_exists($type, false);
338
		}
339
340
		/*
341
		 * Old apps compatibility support: if the component name has been specified using the
342
		 * old namespace syntax (eg. Application.Common.MyDataModule), assume that the calling
343
		 * code expects the class not to be php5.3-namespaced (eg: MyDataModule instead of
344
		 * \Application\Common\MyDataModule)
345
		 * Skip this if the class is inside the Prado\* namespace, since all Prado classes are now namespaced
346
		 */
347
		if (($pos = strrpos($type, '\\')) !== false && ($requestedType != $type) && strpos($type, 'Prado\\') !== 0) {
348
			$type = substr($type, $pos + 1);
349
		}
350
351
		if (count($params) > 0) {
352
			$object = new $type(...$params);
353
		} else {
354
			$object = new $type();
355
		}
356
		if ($properties) {
357
			foreach ($properties as $property => $value) {
358
				$object->setSubProperty($property, $value);
359
			}
360
		}
361
		return $object;
362
	}
363
364
	/**
365
	 * Uses a namespace.
366
	 * A namespace ending with an asterisk '*' refers to a directory, otherwise it represents a PHP file.
367
	 * If the namespace corresponds to a directory, the directory will be appended
368
	 * to the include path. If the namespace corresponds to a file, it will be included (include_once).
369
	 * @param string $namespace namespace to be used
370
	 * @throws TInvalidDataValueException if the namespace is invalid
371
	 */
372
	public static function using($namespace): void
373
	{
374
		$namespace = static::prado3NamespaceToPhpNamespace($namespace);
375
376
		if (isset(self::$_usings[$namespace]) ||
377
			class_exists($namespace, false) ||
378
			interface_exists($namespace, false) ||
379
			trait_exists($namespace, false)) {
380
			return;
381
		}
382
383
		if (array_key_exists($namespace, self::$classMap)) {
384
			// fast autoload a Prado3 class name
385
			$phpNamespace = self::$classMap[$namespace];
386
			if (class_exists($phpNamespace, true) ||
387
				interface_exists($phpNamespace, true) ||
388
				trait_exists($phpNamespace, true)) {
389
				if (!class_exists($namespace) &&
390
					!interface_exists($namespace) &&
391
					!trait_exists($namespace)) {
392
					class_alias($phpNamespace, $namespace);
393
				}
394
				return;
395
			}
396
		} elseif (($pos = strrpos($namespace, '\\')) === false) {
397
			// trying to autoload an old class name
398
			foreach (self::$_usings as $k => $v) {
399
				$path = $v . DIRECTORY_SEPARATOR . $namespace . self::CLASS_FILE_EXT;
400
				if (file_exists($path)) {
401
					$phpNamespace = '\\' . $k . '\\' . $namespace;
402
					if (class_exists($phpNamespace, true) ||
403
						interface_exists($phpNamespace, true) ||
404
						trait_exists($phpNamespace, true)) {
405
						if (!class_exists($namespace) &&
406
							!interface_exists($namespace) &&
407
							!trait_exists($namespace)) {
408
							class_alias($phpNamespace, $namespace);
409
						}
410
						return;
411
					}
412
				}
413
			}
414
		} elseif (($path = self::getPathOfNamespace($namespace, self::CLASS_FILE_EXT)) !== null) {
415
			$className = substr($namespace, $pos + 1);
416
			if ($className === '*') {  // a directory
417
				self::$_usings[substr($namespace, 0, $pos)] = $path;
418
			} else {  // a file
419
				if (class_exists($className, false) ||
420
					interface_exists($className, false) ||
421
					trait_exists($className, false)) {
422
					return;
423
				}
424
425
				if (file_exists($path)) {
426
					include_once($path);
427
					if (!class_exists($className, false) &&
428
						!interface_exists($className, false) &&
429
						!trait_exists($className, false)) {
430
						class_alias($namespace, $className);
431
					}
432
				}
433
			}
434
		}
435
	}
436
437
	/**
438
	 * This conforms PHP's Magic Methods to PHP's Visibility standards for public,
439
	 * protected, and private properties, methods, and constants. This method checks
440
	 * if the object calling your method can access your object's property, method,
441
	 * or constant based upon the defined visibility.  External objects can only access
442
	 * public properties, methods, and constants.  When calling the self, private
443
	 * properties, methods, and constants are allowed to be accessed by the same class.
444
	 * @param object|string $object_or_class The object to check for the method within
445
	 *   and for visibility to the calling object.
446
	 * @param string $method
447
	 * @return bool Does the method exist and is publicly callable.
448
	 * @since 4.2.3
449
	 */
450
	public static function method_visible($object_or_class, string $method): bool
451
	{
452
		if (method_exists($object_or_class, $method)) {
453
			$trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 3);
454
			$reflection = new \ReflectionMethod($object_or_class, $method);
455
			if (empty($trace[2]) || empty($trace[1]['object']) || empty($trace[2]['object']) || $trace[1]['object'] !== $trace[2]['object']) {
456
				return $reflection->isPublic();
457
			} elseif ($reflection->isPrivate()) {
458
				return $trace[2]['class'] === $reflection->class;
459
			}
460
			return true;
461
		}
462
		return false;
463
	}
464
465
	/**
466
	 * This method return the object that is calling your method.
467
	 * @return ?object The parent object calling your code block.
468
	 * @since 4.2.3
469
	 */
470
	public static function callingObject(): ?object
471
	{
472
		$trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 3);
473
		if (!empty($trace[2]) && !empty($trace[2]['object'])) {
474
			return $trace[2]['object'];
475
		}
476
		return null;
477
	}
478
479
	/**
480
	 * This checks if object calling your object method is the same object.  In effect,
481
	 * this signifies if self, parents, and children have visibility to "protected"
482
	 * properties, methods, and constants.
483
	 * @return bool Does the method exist and is publicly callable.
484
	 * @since 4.2.3
485
	 */
486
	public static function isCallingSelf(): bool
487
	{
488
		$trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 3);
489
		return isset($trace[2]) && isset($trace[1]['object']) && isset($trace[2]['object']) && $trace[1]['object'] === $trace[2]['object'];
490
	}
491
492
	/**
493
	 * This checks if object calling your object method is the same object and same class.
494
	 * In effect, this allows only the self to have visibility to "private" properties,
495
	 * methods, and constants.
496
	 * @return bool Does the method exist and is publicly callable.
497
	 * @since 4.2.3
498
	 */
499
	public static function isCallingSelfClass(): bool
500
	{
501
		$trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 3);
502
		return isset($trace[2]) && isset($trace[1]['object']) && isset($trace[2]['object']) && $trace[1]['object'] === $trace[2]['object'] && $trace[1]['class'] === $trace[2]['class'];
503
	}
504
505
	/**
506
	 * Translates a namespace into a file path.
507
	 * The first segment of the namespace is considered as a path alias
508
	 * which is replaced with the actual path. The rest segments are
509
	 * subdirectory names appended to the aliased path.
510
	 * If the namespace ends with an asterisk '*', it represents a directory;
511
	 * Otherwise it represents a file whose extension name is specified by the second parameter (defaults to empty).
512
	 * Note, this method does not ensure the existence of the resulting file path.
513
	 * @param string $namespace namespace
514
	 * @param string $ext extension to be appended if the namespace refers to a file
515
	 * @return null|string file path corresponding to the namespace, null if namespace is invalid
516
	 */
517
	public static function getPathOfNamespace($namespace, $ext = ''): ?string
518
	{
519
		$namespace = static::prado3NamespaceToPhpNamespace($namespace);
520
521
		if (self::CLASS_FILE_EXT === $ext || empty($ext)) {
522
			if (isset(self::$_usings[$namespace])) {
523
				return self::$_usings[$namespace];
524
			}
525
526
			if (isset(self::$_aliases[$namespace])) {
527
				return self::$_aliases[$namespace];
528
			}
529
		}
530
531
		$segs = explode('\\', $namespace);
532
		$alias = array_shift($segs);
533
534
		if (null !== ($file = array_pop($segs)) && null !== ($root = self::getPathOfAlias($alias))) {
535
			return rtrim($root . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $segs), '/\\') . (($file === '*') ? '' : DIRECTORY_SEPARATOR . $file . $ext);
536
		}
537
538
		return null;
539
	}
540
541
	/**
542
	 * @param string $alias alias to the path
543
	 * @return null|string the path corresponding to the alias, null if alias not defined.
544
	 */
545
	public static function getPathOfAlias($alias): ?string
546
	{
547
		return self::$_aliases[$alias] ?? null;
548
	}
549
550
	/**
551
	 * @return array<string, string> list of path aliases
552
	 */
553
	protected static function getPathAliases(): array
554
	{
555
		return self::$_aliases;
556
	}
557
558
	/**
559
	 * @param string $alias alias to the path
560
	 * @param string $path the path corresponding to the alias
561
	 * @throws TInvalidOperationException $alias if the alias is already defined
562
	 * @throws TInvalidDataValueException $path if the path is not a valid file path
563
	 */
564
	public static function setPathOfAlias($alias, $path): void
565
	{
566
		if (isset(self::$_aliases[$alias]) && !defined('PRADO_TEST_RUN')) {
567
			throw new TInvalidOperationException('prado_alias_redefined', $alias);
568
		} elseif (($rp = realpath($path)) !== false && is_dir($rp)) {
569
			if (strpos($alias, '.') === false) {
570
				self::$_aliases[$alias] = $rp;
571
			} else {
572
				throw new TInvalidDataValueException('prado_aliasname_invalid', $alias);
573
			}
574
		} else {
575
			throw new TInvalidDataValueException('prado_alias_invalid', $alias, $path);
576
		}
577
	}
578
579
	/**
580
	 * Fatal error handler.
581
	 * This method displays an error message together with the current call stack.
582
	 * The application will exit after calling this method.
583
	 * @param string $msg error message
584
	 */
585
	public static function fatalError($msg): void
586
	{
587
		echo '<h1>Fatal Error</h1>';
588
		echo '<p>' . $msg . '</p>';
589
		if (!function_exists('debug_backtrace')) {
590
			return;
591
		}
592
		echo '<h2>Debug Backtrace</h2>';
593
		echo '<pre>';
594
		$index = -1;
595
		foreach (debug_backtrace() as $t) {
596
			$index++;
597
			if ($index == 0) {  // hide the backtrace of this function
598
				continue;
599
			}
600
			echo '#' . $index . ' ';
601
			if (isset($t['file'])) {
602
				echo basename($t['file']) . ':' . $t['line'];
603
			} else {
604
				echo '<PHP inner-code>';
605
			}
606
			echo ' -- ';
607
			if (isset($t['class'])) {
608
				echo $t['class'] . $t['type'];
609
			}
610
			echo $t['function'] . '(';
611
			if (isset($t['args']) && count($t['args']) > 0) {
612
				$count = 0;
613
				foreach ($t['args'] as $item) {
614
					if (is_string($item)) {
615
						$str = htmlentities(str_replace("\r\n", "", $item), ENT_QUOTES);
616
						if (strlen($item) > 70) {
617
							echo "'" . substr($str, 0, 70) . "...'";
618
						} else {
619
							echo "'" . $str . "'";
620
						}
621
					} elseif (is_int($item) || is_float($item)) {
622
						echo $item;
623
					} elseif (is_object($item)) {
624
						echo $item::class;
625
					} elseif (is_array($item)) {
626
						echo 'array(' . count($item) . ')';
627
					} elseif (is_bool($item)) {
628
						echo $item ? 'true' : 'false';
629
					} elseif ($item === null) {
630
						echo 'NULL';
631
					} elseif (is_resource($item)) {
632
						echo get_resource_type($item);
633
					}
634
					$count++;
635
					if (count($t['args']) > $count) {
636
						echo ', ';
637
					}
638
				}
639
			}
640
			echo ")\n";
641
		}
642
		echo '</pre>';
643
		exit(1);
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...
644
	}
645
646
	/**
647
	 * Returns a list of user preferred languages.
648
	 * The languages are returned as an array. Each array element
649
	 * represents a single language preference. The languages are ordered
650
	 * according to user preferences. The first language is the most preferred.
651
	 * @return array<string> list of user preferred languages.
652
	 */
653
	public static function getUserLanguages(): array
654
	{
655
		static $languages = null;
656
		if ($languages === null) {
657
			if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
658
				$languages[0] = 'en';
659
			} else {
660
				$languages = [];
661
				foreach (explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']) as $language) {
662
					$array = explode(';q=', trim($language));
663
					$languages[trim($array[0])] = isset($array[1]) ? (float) $array[1] : 1.0;
664
				}
665
				arsort($languages);
666
				$languages = array_keys($languages);
667
				if (count($languages) == 0) {
668
					$languages[0] = 'en';
669
				}
670
			}
671
		}
672
		return $languages;
673
	}
674
675
	/**
676
	 * Returns the most preferred language by the client user.
677
	 * @return string the most preferred language by the client user, defaults to English.
678
	 */
679
	public static function getPreferredLanguage(): string
680
	{
681
		static $language = null;
682
		if ($language === null) {
683
			$langs = Prado::getUserLanguages();
684
			$lang = explode('-', $langs[0]);
685
			if (empty($lang[0]) || !ctype_alpha($lang[0])) {
686
				$language = 'en';
687
			} else {
688
				$language = $lang[0];
689
			}
690
		}
691
		return $language;
692
	}
693
694
	/**
695
	 * Writes a log message.
696
	 * This method wraps {@link log()} by checking the application mode.
697
	 * When the application is in Debug mode, debug backtrace information is appended
698
	 * to the message and the message is logged at DEBUG level.
699
	 * When the application is in Performance mode, this method does nothing.
700
	 * Otherwise, the message is logged at INFO level.
701
	 * @param string $msg message to be logged
702
	 * @param ?string $category category of the message, null fills is with the calling class
703
	 * @param null|\Prado\Web\UI\TControl|string $ctl control of the message
704
	 * @see log, getLogger
705
	 */
706
	public static function trace($msg, $category = null, $ctl = null): void
707
	{
708
		if (self::$_application && self::$_application->getMode() === TApplicationMode::Performance) {
709
			return;
710
		}
711
		if ($category === null) {
712
			$category = self::callingObject()::class;
713
		}
714
		if (!self::$_application || self::$_application->getMode() === TApplicationMode::Debug) {
715
			$trace = debug_backtrace();
716
			if (isset($trace[0]['file']) && isset($trace[0]['line'])) {
717
				$msg .= " (line {$trace[0]['line']}, {$trace[0]['file']})";
718
			}
719
			$level = TLogger::DEBUG;
720
		} else {
721
			$level = TLogger::INFO;
722
		}
723
		self::log($msg, $level, $category, $ctl);
724
	}
725
726
	/**
727
	 * Writes a debug log message.
728
	 * @param string $msg message to be logged
729
	 * @param ?string $category category of the message, default null for the calling class.
730
	 * @param null|\Prado\Web\UI\TControl|string $ctl control of the message, default null
731
	 */
732
	public static function debug($msg, $category = null, $ctl = null): void
733
	{
734
		if (self::$_application && self::$_application->getMode() !== TApplicationMode::Debug) {
735
			return;
736
		}
737
		if ($category === null) {
738
			$category = self::callingObject()::class;
739
		}
740
		$trace = debug_backtrace();
741
		if (isset($trace[0]['file']) && isset($trace[0]['line'])) {
742
			$msg .= " (line {$trace[0]['line']}, {$trace[0]['file']})";
743
		}
744
		self::log($msg, TLogger::DEBUG, $category, $ctl);
745
	}
746
747
	/**
748
	 * Writes an info log message.
749
	 * @param string $msg message to be logged
750
	 * @param ?string $category category of the message, default null for the calling class.
751
	 * @param null|\Prado\Web\UI\TControl|string $ctl control of the message, default null
752
	 */
753
	public static function info($msg, $category = null, $ctl = null): void
754
	{
755
		if ($category === null) {
756
			$category = self::callingObject()::class;
757
		}
758
		self::log($msg, TLogger::INFO, $category, $ctl);
759
	}
760
761
	/**
762
	 * Writes a notice log message.
763
	 * @param string $msg message to be logged
764
	 * @param ?string $category category of the message, default null for the calling class.
765
	 * @param null|\Prado\Web\UI\TControl|string $ctl control of the message, default null
766
	 */
767
	public static function notice($msg, $category = null, $ctl = null): void
768
	{
769
		if ($category === null) {
770
			$category = self::callingObject()::class;
771
		}
772
		self::log($msg, TLogger::NOTICE, $category, $ctl);
773
	}
774
775
	/**
776
	 * Writes a warning log message.
777
	 * @param string $msg message to be logged
778
	 * @param ?string $category category of the message, default null for the calling class.
779
	 * @param null|\Prado\Web\UI\TControl|string $ctl control of the message, default null
780
	 */
781
	public static function warning($msg, $category = null, $ctl = null): void
782
	{
783
		if ($category === null) {
784
			$category = self::callingObject()::class;
785
		}
786
		self::log($msg, TLogger::WARNING, $category, $ctl);
787
	}
788
789
	/**
790
	 * Writes an error log message.
791
	 * @param string $msg message to be logged
792
	 * @param ?string $category category of the message, default null for the calling class.
793
	 * @param null|\Prado\Web\UI\TControl|string $ctl control of the message, default null
794
	 */
795
	public static function error($msg, $category = null, $ctl = null): void
796
	{
797
		if ($category === null) {
798
			$category = self::callingObject()::class;
799
		}
800
		self::log($msg, TLogger::ERROR, $category, $ctl);
801
	}
802
803
	/**
804
	 * Writes an alert log message.
805
	 * @param string $msg message to be logged
806
	 * @param ?string $category category of the message, default null for the calling class.
807
	 * @param null|\Prado\Web\UI\TControl|string $ctl control of the message, default null
808
	 */
809
	public static function alert($msg, $category = null, $ctl = null): void
810
	{
811
		if ($category === null) {
812
			$category = self::callingObject()::class;
813
		}
814
		self::log($msg, TLogger::ALERT, $category, $ctl);
815
	}
816
817
	/**
818
	 * Writes a fatal log message.
819
	 * @param string $msg message to be logged
820
	 * @param ?string $category category of the message, default null for the calling class.
821
	 * @param null|\Prado\Web\UI\TControl|string $ctl control of the message, default null
822
	 */
823
	public static function fatal($msg, $category = null, $ctl = null): void
824
	{
825
		if ($category === null) {
826
			$category = self::callingObject()::class;
827
		}
828
		self::log($msg, TLogger::FATAL, $category, $ctl);
829
	}
830
831
	/**
832
	 * Logs a message.
833
	 * Messages logged by this method may be retrieved via {@link TLogger::getLogs}
834
	 * and may be recorded in different media, such as file, email, database, using
835
	 * {@link TLogRouter}.
836
	 * @param string $msg message to be logged
837
	 * @param int $level level of the message. Valid values include
838
	 * TLogger::DEBUG, TLogger::INFO, TLogger::NOTICE, TLogger::WARNING,
839
	 * TLogger::ERROR, TLogger::ALERT, TLogger::FATAL.
840
	 * @param string $category category of the message
841
	 * @param null|\Prado\Web\UI\TControl|string $ctl control of the message
842
	 */
843
	public static function log($msg, $level = TLogger::INFO, $category = 'Uncategorized', $ctl = null): void
844
	{
845
		if (self::$_logger === null) {
846
			self::$_logger = new TLogger();
847
		}
848
		if ($category === null) {
0 ignored issues
show
introduced by
The condition $category === null is always false.
Loading history...
849
			$category = self::callingObject()::class;
850
		}
851
		self::$_logger->log($msg, $level, $category, $ctl);
852
	}
853
854
	/**
855
	 * @return TLogger message logger
856
	 */
857
	public static function getLogger(): TLogger
858
	{
859
		if (self::$_logger === null) {
860
			self::$_logger = new TLogger();
861
		}
862
		return self::$_logger;
863
	}
864
865
	/**
866
	 * Converts a variable into a string representation.
867
	 * This method achieves the similar functionality as var_dump and print_r
868
	 * but is more robust when handling complex objects such as PRADO controls.
869
	 * @param mixed $var variable to be dumped
870
	 * @param int $depth maximum depth that the dumper should go into the variable. Defaults to 10.
871
	 * @param bool $highlight whether to syntax highlight the output. Defaults to false.
872
	 * @return string the string representation of the variable
873
	 */
874
	public static function varDump($var, $depth = 10, $highlight = false): string
875
	{
876
		return TVarDumper::dump($var, $depth, $highlight);
877
	}
878
879
	/**
880
	 * Localize a text to the locale/culture specified in the globalization handler.
881
	 * @param string $text text to be localized.
882
	 * @param array<string, string> $parameters a set of parameters to substitute.
883
	 * @param string $catalogue a different catalogue to find the localize text.
884
	 * @param string $charset the input AND output charset.
885
	 * @return string localized text.
886
	 * @see TTranslate::formatter()
887
	 * @see TTranslate::init()
888
	 */
889
	public static function localize($text, $parameters = [], $catalogue = null, $charset = null): string
890
	{
891
		$params = [];
892
		foreach ($parameters as $key => $value) {
893
			$params['{' . $key . '}'] = $value;
894
		}
895
896
		// no translation handler provided
897
		if (self::$_application === null
898
			|| ($app = self::$_application->getGlobalization(false)) === null
899
			|| ($config = $app->getTranslationConfiguration()) === null) {
900
			return strtr($text, $params);
901
		}
902
903
		if ($catalogue === null) {
904
			$catalogue = $config['catalogue'] ?? 'messages';
905
		}
906
907
		Translation::init($catalogue);
908
909
		//globalization charset
910
		if (empty($charset)) {
911
			$charset = $app->getCharset();
912
		}
913
914
		//default charset
915
		if (empty($charset)) {
916
			$charset = $app->getDefaultCharset();
917
		}
918
919
		return Translation::formatter($catalogue)->format($text, $params, $catalogue, $charset);
920
	}
921
}
922
923
/**
924
 * Initialize Prado autoloader and error handler
925
 */
926
Prado::init();
927
928
/**
929
 * Defines Prado in global namespace
930
 */
931
class_alias(\Prado\Prado::class, 'Prado');
932