Passed
Push — master ( 6ce998...91641e )
by Fabio
05:04
created

Prado::prado3NamespaceToPhpNamespace()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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