1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* PradoBase 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
|
|
|
* @package Prado |
12
|
|
|
*/ |
13
|
|
|
|
14
|
|
|
namespace Prado; |
15
|
|
|
|
16
|
|
|
use Prado\Exceptions\TInvalidDataValueException; |
17
|
|
|
use Prado\Exceptions\TInvalidOperationException; |
18
|
|
|
use Prado\Exceptions\TPhpErrorException; |
19
|
|
|
use Prado\Exceptions\TPhpFatalErrorException; |
20
|
|
|
use Prado\Util\TLogger; |
21
|
|
|
use Prado\Util\TVarDumper; |
22
|
|
|
use Prado\I18N\Translation; |
23
|
|
|
|
24
|
|
|
// Defines the PRADO framework installation path. |
25
|
|
|
if (!defined('PRADO_DIR')) { |
26
|
|
|
define('PRADO_DIR', __DIR__); |
27
|
|
|
} |
28
|
|
|
|
29
|
|
|
// Defines the default permission for writable directories and files |
30
|
|
|
if (!defined('PRADO_CHMOD')) { |
31
|
|
|
define('PRADO_CHMOD', 0777); |
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
// Defines the Composer's vendor/ path. |
35
|
|
|
if (!defined('PRADO_VENDORDIR')) { |
36
|
|
|
$reflector = new \ReflectionClass('\Composer\Autoload\ClassLoader'); |
37
|
|
|
define('PRADO_VENDORDIR', dirname($reflector->getFileName(), 2)); |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* PradoBase class. |
42
|
|
|
* |
43
|
|
|
* PradoBase implements a few fundamental static methods. |
44
|
|
|
* |
45
|
|
|
* To use the static methods, Use Prado as the class name rather than PradoBase. |
46
|
|
|
* PradoBase is meant to serve as the base class of Prado. The latter might be |
47
|
|
|
* rewritten for customization. |
48
|
|
|
* |
49
|
|
|
* @author Qiang Xue <[email protected]> |
50
|
|
|
* @package Prado |
51
|
|
|
* @since 3.0 |
52
|
|
|
*/ |
53
|
|
|
class PradoBase |
54
|
|
|
{ |
55
|
|
|
/** |
56
|
|
|
* File extension for Prado class files. |
57
|
|
|
*/ |
58
|
|
|
const CLASS_FILE_EXT = '.php'; |
59
|
|
|
/** |
60
|
|
|
* @var array list of path aliases |
61
|
|
|
*/ |
62
|
|
|
private static $_aliases = [ |
63
|
|
|
'Prado' => PRADO_DIR, |
64
|
|
|
'Vendor' => PRADO_VENDORDIR |
65
|
|
|
]; |
66
|
|
|
/** |
67
|
|
|
* @var array list of namespaces currently in use |
68
|
|
|
*/ |
69
|
|
|
private static $_usings = [ |
70
|
|
|
'Prado' => PRADO_DIR |
71
|
|
|
]; |
72
|
|
|
/** |
73
|
|
|
* @var array list of namespaces currently in use |
74
|
|
|
*/ |
75
|
|
|
public static $classMap = []; |
76
|
|
|
/** |
77
|
|
|
* @var TApplication the application instance |
78
|
|
|
*/ |
79
|
|
|
private static $_application = null; |
80
|
|
|
/** |
81
|
|
|
* @var TLogger logger instance |
82
|
|
|
*/ |
83
|
|
|
private static $_logger = null; |
84
|
|
|
/** |
85
|
|
|
* @var array list of class exists checks |
86
|
|
|
*/ |
87
|
|
|
protected static $classExists = []; |
88
|
|
|
/** |
89
|
|
|
* @return string the version of Prado framework |
90
|
|
|
*/ |
91
|
3 |
|
public static function getVersion() |
92
|
|
|
{ |
93
|
3 |
|
return '4.1.0'; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
public static function init() |
97
|
|
|
{ |
98
|
|
|
static::initAutoloader(); |
99
|
|
|
static::initErrorHandlers(); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Loads the static classmap and registers the autoload function. |
104
|
|
|
*/ |
105
|
|
|
public static function initAutoloader() |
106
|
|
|
{ |
107
|
|
|
self::$classMap = require(__DIR__ . '/classes.php'); |
108
|
|
|
|
109
|
|
|
spl_autoload_register([get_called_class(), 'autoload']); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* Initializes error handlers. |
114
|
|
|
* This method set error and exception handlers to be functions |
115
|
|
|
* defined in this class. |
116
|
|
|
*/ |
117
|
|
|
public static function initErrorHandlers() |
118
|
|
|
{ |
119
|
|
|
/** |
120
|
|
|
* Sets error handler to be Prado::phpErrorHandler |
121
|
|
|
*/ |
122
|
|
|
set_error_handler(['\Prado\PradoBase', 'phpErrorHandler']); |
123
|
|
|
/** |
124
|
|
|
* Sets shutdown function to be Prado::phpFatalErrorHandler |
125
|
|
|
*/ |
126
|
|
|
register_shutdown_function(['PradoBase', 'phpFatalErrorHandler']); |
127
|
|
|
/** |
128
|
|
|
* Sets exception handler to be Prado::exceptionHandler |
129
|
|
|
*/ |
130
|
|
|
set_exception_handler(['\Prado\PradoBase', 'exceptionHandler']); |
131
|
|
|
/** |
132
|
|
|
* Disable php's builtin error reporting to avoid duplicated reports |
133
|
|
|
*/ |
134
|
|
|
ini_set('display_errors', 0); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Class autoload loader. |
139
|
|
|
* This method is provided to be invoked within an __autoload() magic method. |
140
|
|
|
* @param string $className class name |
141
|
|
|
*/ |
142
|
7 |
|
public static function autoload($className) |
143
|
|
|
{ |
144
|
7 |
|
static::using($className); |
145
|
7 |
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* @param int $logoType the type of "powered logo". Valid values include 0 and 1. |
149
|
|
|
* @return string a string that can be displayed on your Web page showing powered-by-PRADO information |
150
|
|
|
*/ |
151
|
|
|
public static function poweredByPrado($logoType = 0) |
152
|
|
|
{ |
153
|
|
|
$logoName = $logoType == 1 ? 'powered2' : 'powered'; |
154
|
|
|
if (self::$_application !== null) { |
155
|
|
|
$am = self::$_application->getAssetManager(); |
156
|
|
|
$url = $am->publishFilePath(self::getPathOfNamespace('Prado\\' . $logoName, '.gif')); |
157
|
|
|
} else { |
158
|
|
|
$url = 'http://pradosoft.github.io/docs/' . $logoName . '.gif'; |
159
|
|
|
} |
160
|
|
|
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>'; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* PHP error handler. |
165
|
|
|
* This method should be registered as PHP error handler using |
166
|
|
|
* {@link set_error_handler}. The method throws an exception that |
167
|
|
|
* contains the error information. |
168
|
|
|
* @param int $errno the level of the error raised |
169
|
|
|
* @param string $errstr the error message |
170
|
|
|
* @param string $errfile the filename that the error was raised in |
171
|
|
|
* @param int $errline the line number the error was raised at |
172
|
|
|
*/ |
173
|
14 |
|
public static function phpErrorHandler($errno, $errstr, $errfile, $errline) |
174
|
|
|
{ |
175
|
14 |
|
if (error_reporting() & $errno) { |
176
|
|
|
throw new TPhpErrorException($errno, $errstr, $errfile, $errline); |
177
|
|
|
} |
178
|
14 |
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* PHP shutdown function used to catch fatal errors. |
182
|
|
|
* This method should be registered as PHP error handler using |
183
|
|
|
* {@link register_shutdown_function}. The method throws an exception that |
184
|
|
|
* contains the error information. |
185
|
|
|
*/ |
186
|
|
|
public static function phpFatalErrorHandler() |
187
|
|
|
{ |
188
|
|
|
$error = error_get_last(); |
189
|
|
|
if ($error && |
|
|
|
|
190
|
|
|
TPhpErrorException::isFatalError($error) && |
191
|
|
|
error_reporting() & $error['type']) { |
192
|
|
|
self::exceptionHandler(new TPhpFatalErrorException($error['type'], $error['message'], $error['file'], $error['line'])); |
193
|
|
|
} |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* Default exception handler. |
198
|
|
|
* This method should be registered as default exception handler using |
199
|
|
|
* {@link set_exception_handler}. The method tries to use the errorhandler |
200
|
|
|
* module of the Prado application to handle the exception. |
201
|
|
|
* If the application or the module does not exist, it simply echoes the |
202
|
|
|
* exception. |
203
|
|
|
* @param Exception $exception exception that is not caught |
204
|
|
|
*/ |
205
|
|
|
public static function exceptionHandler($exception) |
206
|
|
|
{ |
207
|
|
|
if (self::$_application !== null && ($errorHandler = self::$_application->getErrorHandler()) !== null) { |
208
|
|
|
$errorHandler->handleError(null, $exception); |
209
|
|
|
} else { |
210
|
|
|
echo $exception; |
211
|
|
|
} |
212
|
|
|
exit(1); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* Stores the application instance in the class static member. |
217
|
|
|
* This method helps implement a singleton pattern for TApplication. |
218
|
|
|
* Repeated invocation of this method or the application constructor |
219
|
|
|
* will cause the throw of an exception. |
220
|
|
|
* This method should only be used by framework developers. |
221
|
|
|
* @param TApplication $application the application instance |
222
|
|
|
* @throws TInvalidOperationException if this method is invoked twice or more. |
223
|
|
|
*/ |
224
|
55 |
|
public static function setApplication($application) |
225
|
|
|
{ |
226
|
55 |
|
if (self::$_application !== null && !defined('PRADO_TEST_RUN')) { |
227
|
|
|
throw new TInvalidOperationException('prado_application_singleton_required'); |
228
|
|
|
} |
229
|
55 |
|
self::$_application = $application; |
230
|
55 |
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* @return TApplication the application singleton, null if the singleton has not be created yet. |
234
|
|
|
*/ |
235
|
96 |
|
public static function getApplication() |
236
|
|
|
{ |
237
|
96 |
|
return self::$_application; |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
/** |
241
|
|
|
* @return string the path of the framework |
242
|
|
|
*/ |
243
|
108 |
|
public static function getFrameworkPath() |
244
|
|
|
{ |
245
|
108 |
|
return PRADO_DIR; |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* Convert old Prado namespaces to PHP namespaces |
250
|
|
|
* @param string $type old class name in Prado3 namespace format |
251
|
|
|
* @return string Equivalent class name in PHP namespace format |
252
|
|
|
*/ |
253
|
116 |
|
protected static function prado3NamespaceToPhpNamespace($type) |
254
|
|
|
{ |
255
|
116 |
|
if (substr($type, 0, 6) === 'System') { |
256
|
9 |
|
$type = 'Prado' . substr($type, 6); |
257
|
|
|
} |
258
|
|
|
|
259
|
116 |
|
if (false === strpos($type, '\\')) { |
260
|
111 |
|
return str_replace('.', '\\', $type); |
261
|
|
|
} else { |
262
|
9 |
|
return $type; |
263
|
|
|
} |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* Creates a component with the specified type. |
268
|
|
|
* A component type can be either the component class name |
269
|
|
|
* or a namespace referring to the path of the component class file. |
270
|
|
|
* For example, 'TButton', '\Prado\Web\UI\WebControls\TButton' are both |
271
|
|
|
* valid component type. |
272
|
|
|
* This method can also pass parameters to component constructors. |
273
|
|
|
* All parameters passed to this method except the first one (the component type) |
274
|
|
|
* will be supplied as component constructor parameters. |
275
|
|
|
* @param string $requestedType component type |
276
|
|
|
* @param array $params |
277
|
|
|
* @throws TInvalidDataValueException if the component type is unknown |
278
|
|
|
* @return TComponent component instance of the specified type |
279
|
|
|
*/ |
280
|
103 |
|
public static function createComponent($requestedType, ...$params) |
281
|
|
|
{ |
282
|
103 |
|
$type = static::prado3NamespaceToPhpNamespace($requestedType); |
283
|
103 |
|
if (!isset(self::$classExists[$type])) { |
284
|
24 |
|
self::$classExists[$type] = class_exists($type, false); |
285
|
|
|
} |
286
|
|
|
|
287
|
103 |
|
if (!isset(self::$_usings[$type]) && !self::$classExists[$type]) { |
288
|
3 |
|
static::using($type); |
289
|
3 |
|
self::$classExists[$type] = class_exists($type, false); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/* |
293
|
|
|
* Old apps compatibility support: if the component name has been specified using the |
294
|
|
|
* old namespace syntax (eg. Application.Common.MyDataModule), assume that the calling |
295
|
|
|
* code expects the class not to be php5.3-namespaced (eg: MyDataModule instead of |
296
|
|
|
* \Application\Common\MyDataModule) |
297
|
|
|
* Skip this if the class is inside the Prado\* namespace, since all Prado classes are now namespaced |
298
|
|
|
*/ |
299
|
103 |
|
if (($pos = strrpos($type, '\\')) !== false && ($requestedType != $type) && strpos($type, 'Prado\\') !== 0) { |
300
|
|
|
$type = substr($type, $pos + 1); |
301
|
|
|
} |
302
|
|
|
|
303
|
103 |
|
if (count($params) > 0) { |
304
|
10 |
|
return new $type(...$params); |
305
|
|
|
} else { |
306
|
96 |
|
return new $type; |
307
|
|
|
} |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* Uses a namespace. |
312
|
|
|
* A namespace ending with an asterisk '*' refers to a directory, otherwise it represents a PHP file. |
313
|
|
|
* If the namespace corresponds to a directory, the directory will be appended |
314
|
|
|
* to the include path. If the namespace corresponds to a file, it will be included (include_once). |
315
|
|
|
* @param string $namespace namespace to be used |
316
|
|
|
* @param bool $checkClassExistence whether to check the existence of the class after the class file is included |
317
|
|
|
* @throws TInvalidDataValueException if the namespace is invalid |
318
|
|
|
*/ |
319
|
12 |
|
public static function using($namespace, $checkClassExistence = true) |
320
|
|
|
{ |
321
|
12 |
|
$namespace = static::prado3NamespaceToPhpNamespace($namespace); |
322
|
|
|
|
323
|
12 |
|
if (isset(self::$_usings[$namespace]) || class_exists($namespace, false)) { |
324
|
|
|
return; |
325
|
|
|
} |
326
|
|
|
|
327
|
12 |
|
if (array_key_exists($namespace, self::$classMap)) { |
328
|
|
|
// fast autoload a Prado3 class name |
329
|
7 |
|
$phpNamespace = self::$classMap[$namespace]; |
330
|
7 |
View Code Duplication |
if (class_exists($phpNamespace, true) || interface_exists($phpNamespace, true)) { |
|
|
|
|
331
|
7 |
|
if (!class_exists($namespace) && !interface_exists($namespace)) { |
332
|
7 |
|
class_alias($phpNamespace, $namespace); |
333
|
|
|
} |
334
|
7 |
|
return; |
335
|
|
|
} |
336
|
5 |
|
} elseif (($pos = strrpos($namespace, '\\')) === false) { |
337
|
|
|
// trying to autoload an old class name |
338
|
|
|
foreach (self::$_usings as $k => $v) { |
339
|
|
|
$path = $v . DIRECTORY_SEPARATOR . $namespace . self::CLASS_FILE_EXT; |
340
|
|
|
if (file_exists($path)) { |
341
|
|
|
$phpNamespace = '\\' . $k . '\\' . $namespace; |
342
|
|
View Code Duplication |
if (class_exists($phpNamespace, true) || interface_exists($phpNamespace, true)) { |
|
|
|
|
343
|
|
|
if (!class_exists($namespace) && !interface_exists($namespace)) { |
344
|
|
|
class_alias($phpNamespace, $namespace); |
345
|
|
|
} |
346
|
|
|
return; |
347
|
|
|
} |
348
|
|
|
} |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
if ($checkClassExistence && !class_exists($namespace, false) && !interface_exists($namespace, false)) { |
352
|
|
|
throw new TInvalidOperationException('prado_component_unknown', $namespace, ''); |
353
|
|
|
} |
354
|
5 |
|
} elseif (($path = self::getPathOfNamespace($namespace, self::CLASS_FILE_EXT)) !== null) { |
355
|
5 |
|
$className = substr($namespace, $pos + 1); |
356
|
5 |
|
if ($className === '*') { // a directory |
357
|
|
|
self::$_usings[substr($namespace, 0, $pos)] = $path; |
358
|
|
|
} else { // a file |
359
|
|
|
//self::$_usings[$namespace]=$path; |
360
|
5 |
|
if (!$checkClassExistence || (!class_exists($className, false) && !interface_exists($className, false))) { |
361
|
|
|
try { |
362
|
5 |
|
include_once($path); |
363
|
5 |
|
if (class_exists($namespace, false) || interface_exists($namespace, false)) { |
364
|
5 |
|
class_alias($namespace, $className); |
365
|
|
|
} |
366
|
|
|
} catch (\Exception $e) { |
367
|
|
|
if ($checkClassExistence && !class_exists($className, false)) { |
368
|
|
|
throw new TInvalidOperationException('prado_component_unknown', $className, $e->getMessage()); |
369
|
|
|
} else { |
370
|
|
|
throw $e; |
371
|
|
|
} |
372
|
|
|
} |
373
|
|
|
} |
374
|
|
|
} |
375
|
|
|
} |
376
|
5 |
|
} |
377
|
|
|
|
378
|
|
|
/** |
379
|
|
|
* Translates a namespace into a file path. |
380
|
|
|
* The first segment of the namespace is considered as a path alias |
381
|
|
|
* which is replaced with the actual path. The rest segments are |
382
|
|
|
* subdirectory names appended to the aliased path. |
383
|
|
|
* If the namespace ends with an asterisk '*', it represents a directory; |
384
|
|
|
* Otherwise it represents a file whose extension name is specified by the second parameter (defaults to empty). |
385
|
|
|
* Note, this method does not ensure the existence of the resulting file path. |
386
|
|
|
* @param string $namespace namespace |
387
|
|
|
* @param string $ext extension to be appended if the namespace refers to a file |
388
|
|
|
* @return string file path corresponding to the namespace, null if namespace is invalid |
389
|
|
|
*/ |
390
|
10 |
|
public static function getPathOfNamespace($namespace, $ext = '') |
391
|
|
|
{ |
392
|
10 |
|
$namespace = static::prado3NamespaceToPhpNamespace($namespace); |
393
|
|
|
|
394
|
10 |
|
if (self::CLASS_FILE_EXT === $ext || empty($ext)) { |
395
|
6 |
|
if (isset(self::$_usings[$namespace])) { |
396
|
|
|
return self::$_usings[$namespace]; |
397
|
|
|
} |
398
|
|
|
|
399
|
6 |
|
if (isset(self::$_aliases[$namespace])) { |
400
|
1 |
|
return self::$_aliases[$namespace]; |
401
|
|
|
} |
402
|
|
|
} |
403
|
|
|
|
404
|
10 |
|
$segs = explode('\\', $namespace); |
405
|
10 |
|
$alias = array_shift($segs); |
406
|
|
|
|
407
|
10 |
|
if (null !== ($file = array_pop($segs)) && null !== ($root = self::getPathOfAlias($alias))) { |
408
|
9 |
|
return rtrim($root . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $segs), '/\\') . (($file === '*') ? '' : DIRECTORY_SEPARATOR . $file . $ext); |
409
|
|
|
} |
410
|
|
|
|
411
|
2 |
|
return null; |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
/** |
415
|
|
|
* @param string $alias alias to the path |
416
|
|
|
* @return string the path corresponding to the alias, null if alias not defined. |
417
|
|
|
*/ |
418
|
9 |
|
public static function getPathOfAlias($alias) |
419
|
|
|
{ |
420
|
9 |
|
return isset(self::$_aliases[$alias]) ? self::$_aliases[$alias] : null; |
421
|
|
|
} |
422
|
|
|
|
423
|
|
|
protected static function getPathAliases() |
424
|
|
|
{ |
425
|
|
|
return self::$_aliases; |
426
|
|
|
} |
427
|
|
|
|
428
|
|
|
/** |
429
|
|
|
* @param string $alias alias to the path |
430
|
|
|
* @param string $path the path corresponding to the alias |
431
|
|
|
* @throws TInvalidOperationException $alias if the alias is already defined |
432
|
|
|
* @throws TInvalidDataValueException $path if the path is not a valid file path |
433
|
|
|
*/ |
434
|
61 |
|
public static function setPathOfAlias($alias, $path) |
435
|
|
|
{ |
436
|
61 |
|
if (isset(self::$_aliases[$alias]) && !defined('PRADO_TEST_RUN')) { |
437
|
|
|
throw new TInvalidOperationException('prado_alias_redefined', $alias); |
438
|
61 |
|
} elseif (($rp = realpath($path)) !== false && is_dir($rp)) { |
439
|
61 |
|
if (strpos($alias, '.') === false) { |
440
|
61 |
|
self::$_aliases[$alias] = $rp; |
441
|
|
|
} else { |
442
|
61 |
|
throw new TInvalidDataValueException('prado_aliasname_invalid', $alias); |
443
|
|
|
} |
444
|
|
|
} else { |
445
|
|
|
throw new TInvalidDataValueException('prado_alias_invalid', $alias, $path); |
446
|
|
|
} |
447
|
61 |
|
} |
448
|
|
|
|
449
|
|
|
/** |
450
|
|
|
* Fatal error handler. |
451
|
|
|
* This method displays an error message together with the current call stack. |
452
|
|
|
* The application will exit after calling this method. |
453
|
|
|
* @param string $msg error message |
454
|
|
|
*/ |
455
|
|
|
public static function fatalError($msg) |
456
|
|
|
{ |
457
|
|
|
echo '<h1>Fatal Error</h1>'; |
458
|
|
|
echo '<p>' . $msg . '</p>'; |
459
|
|
|
if (!function_exists('debug_backtrace')) { |
460
|
|
|
return; |
461
|
|
|
} |
462
|
|
|
echo '<h2>Debug Backtrace</h2>'; |
463
|
|
|
echo '<pre>'; |
464
|
|
|
$index = -1; |
465
|
|
|
foreach (debug_backtrace() as $t) { |
466
|
|
|
$index++; |
467
|
|
|
if ($index == 0) { // hide the backtrace of this function |
468
|
|
|
continue; |
469
|
|
|
} |
470
|
|
|
echo '#' . $index . ' '; |
471
|
|
|
if (isset($t['file'])) { |
472
|
|
|
echo basename($t['file']) . ':' . $t['line']; |
473
|
|
|
} else { |
474
|
|
|
echo '<PHP inner-code>'; |
475
|
|
|
} |
476
|
|
|
echo ' -- '; |
477
|
|
|
if (isset($t['class'])) { |
478
|
|
|
echo $t['class'] . $t['type']; |
479
|
|
|
} |
480
|
|
|
echo $t['function'] . '('; |
481
|
|
|
if (isset($t['args']) && count($t['args']) > 0) { |
482
|
|
|
$count = 0; |
483
|
|
|
foreach ($t['args'] as $item) { |
484
|
|
|
if (is_string($item)) { |
485
|
|
|
$str = htmlentities(str_replace("\r\n", "", $item), ENT_QUOTES); |
486
|
|
|
if (strlen($item) > 70) { |
487
|
|
|
echo "'" . substr($str, 0, 70) . "...'"; |
488
|
|
|
} else { |
489
|
|
|
echo "'" . $str . "'"; |
490
|
|
|
} |
491
|
|
|
} elseif (is_int($item) || is_float($item)) { |
492
|
|
|
echo $item; |
493
|
|
|
} elseif (is_object($item)) { |
494
|
|
|
echo get_class($item); |
495
|
|
|
} elseif (is_array($item)) { |
496
|
|
|
echo 'array(' . count($item) . ')'; |
497
|
|
|
} elseif (is_bool($item)) { |
498
|
|
|
echo $item ? 'true' : 'false'; |
499
|
|
|
} elseif ($item === null) { |
500
|
|
|
echo 'NULL'; |
501
|
|
|
} elseif (is_resource($item)) { |
502
|
|
|
echo get_resource_type($item); |
503
|
|
|
} |
504
|
|
|
$count++; |
505
|
|
|
if (count($t['args']) > $count) { |
506
|
|
|
echo ', '; |
507
|
|
|
} |
508
|
|
|
} |
509
|
|
|
} |
510
|
|
|
echo ")\n"; |
511
|
|
|
} |
512
|
|
|
echo '</pre>'; |
513
|
|
|
exit(1); |
514
|
|
|
} |
515
|
|
|
|
516
|
|
|
/** |
517
|
|
|
* Returns a list of user preferred languages. |
518
|
|
|
* The languages are returned as an array. Each array element |
519
|
|
|
* represents a single language preference. The languages are ordered |
520
|
|
|
* according to user preferences. The first language is the most preferred. |
521
|
|
|
* @return array list of user preferred languages. |
522
|
|
|
*/ |
523
|
1 |
|
public static function getUserLanguages() |
524
|
|
|
{ |
525
|
1 |
|
static $languages = null; |
526
|
1 |
|
if ($languages === null) { |
527
|
1 |
|
if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { |
528
|
|
|
$languages[0] = 'en'; |
529
|
|
|
} else { |
530
|
1 |
|
$languages = []; |
531
|
1 |
|
foreach (explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']) as $language) { |
532
|
1 |
|
$array = explode(';q=', trim($language)); |
533
|
1 |
|
$languages[trim($array[0])] = isset($array[1]) ? (float) $array[1] : 1.0; |
534
|
|
|
} |
535
|
1 |
|
arsort($languages); |
536
|
1 |
|
$languages = array_keys($languages); |
537
|
1 |
|
if (empty($languages)) { |
538
|
|
|
$languages[0] = 'en'; |
539
|
|
|
} |
540
|
|
|
} |
541
|
|
|
} |
542
|
1 |
|
return $languages; |
543
|
|
|
} |
544
|
|
|
|
545
|
|
|
/** |
546
|
|
|
* Returns the most preferred language by the client user. |
547
|
|
|
* @return string the most preferred language by the client user, defaults to English. |
548
|
|
|
*/ |
549
|
113 |
|
public static function getPreferredLanguage() |
550
|
|
|
{ |
551
|
113 |
|
static $language = null; |
552
|
113 |
|
if ($language === null) { |
553
|
1 |
|
$langs = Prado::getUserLanguages(); |
554
|
1 |
|
$lang = explode('-', $langs[0]); |
555
|
1 |
|
if (empty($lang[0]) || !ctype_alpha($lang[0])) { |
556
|
|
|
$language = 'en'; |
557
|
|
|
} else { |
558
|
1 |
|
$language = $lang[0]; |
559
|
|
|
} |
560
|
|
|
} |
561
|
113 |
|
return $language; |
562
|
|
|
} |
563
|
|
|
|
564
|
|
|
/** |
565
|
|
|
* Writes a log message. |
566
|
|
|
* This method wraps {@link log()} by checking the application mode. |
567
|
|
|
* When the application is in Debug mode, debug backtrace information is appended |
568
|
|
|
* to the message and the message is logged at DEBUG level. |
569
|
|
|
* When the application is in Performance mode, this method does nothing. |
570
|
|
|
* Otherwise, the message is logged at INFO level. |
571
|
|
|
* @param string $msg message to be logged |
572
|
|
|
* @param string $category category of the message |
573
|
|
|
* @param (string|TControl) $ctl control of the message |
574
|
|
|
* @see log, getLogger |
575
|
|
|
*/ |
576
|
32 |
|
public static function trace($msg, $category = 'Uncategorized', $ctl = null) |
577
|
|
|
{ |
578
|
32 |
|
if (self::$_application && self::$_application->getMode() === TApplicationMode::Performance) { |
579
|
|
|
return; |
580
|
|
|
} |
581
|
32 |
|
if (!self::$_application || self::$_application->getMode() === TApplicationMode::Debug) { |
582
|
32 |
|
$trace = debug_backtrace(); |
583
|
32 |
|
if (isset($trace[0]['file']) && isset($trace[0]['line'])) { |
584
|
32 |
|
$msg .= " (line {$trace[0]['line']}, {$trace[0]['file']})"; |
585
|
|
|
} |
586
|
32 |
|
$level = TLogger::DEBUG; |
587
|
|
|
} else { |
588
|
|
|
$level = TLogger::INFO; |
589
|
|
|
} |
590
|
32 |
|
self::log($msg, $level, $category, $ctl); |
591
|
32 |
|
} |
592
|
|
|
|
593
|
|
|
/** |
594
|
|
|
* Logs a message. |
595
|
|
|
* Messages logged by this method may be retrieved via {@link TLogger::getLogs} |
596
|
|
|
* and may be recorded in different media, such as file, email, database, using |
597
|
|
|
* {@link TLogRouter}. |
598
|
|
|
* @param string $msg message to be logged |
599
|
|
|
* @param int $level level of the message. Valid values include |
600
|
|
|
* TLogger::DEBUG, TLogger::INFO, TLogger::NOTICE, TLogger::WARNING, |
601
|
|
|
* TLogger::ERROR, TLogger::ALERT, TLogger::FATAL. |
602
|
|
|
* @param string $category category of the message |
603
|
|
|
* @param (string|TControl) $ctl control of the message |
604
|
|
|
*/ |
605
|
32 |
|
public static function log($msg, $level = TLogger::INFO, $category = 'Uncategorized', $ctl = null) |
606
|
|
|
{ |
607
|
32 |
|
if (self::$_logger === null) { |
608
|
1 |
|
self::$_logger = new TLogger; |
609
|
|
|
} |
610
|
32 |
|
self::$_logger->log($msg, $level, $category, $ctl); |
611
|
32 |
|
} |
612
|
|
|
|
613
|
|
|
/** |
614
|
|
|
* @return TLogger message logger |
615
|
|
|
*/ |
616
|
|
|
public static function getLogger() |
617
|
|
|
{ |
618
|
|
|
if (self::$_logger === null) { |
619
|
|
|
self::$_logger = new TLogger; |
620
|
|
|
} |
621
|
|
|
return self::$_logger; |
622
|
|
|
} |
623
|
|
|
|
624
|
|
|
/** |
625
|
|
|
* Converts a variable into a string representation. |
626
|
|
|
* This method achieves the similar functionality as var_dump and print_r |
627
|
|
|
* but is more robust when handling complex objects such as PRADO controls. |
628
|
|
|
* @param mixed $var variable to be dumped |
629
|
|
|
* @param int $depth maximum depth that the dumper should go into the variable. Defaults to 10. |
630
|
|
|
* @param bool $highlight whether to syntax highlight the output. Defaults to false. |
631
|
|
|
* @return string the string representation of the variable |
632
|
|
|
*/ |
633
|
|
|
public static function varDump($var, $depth = 10, $highlight = false) |
634
|
|
|
{ |
635
|
|
|
return TVarDumper::dump($var, $depth, $highlight); |
636
|
|
|
} |
637
|
|
|
|
638
|
|
|
/** |
639
|
|
|
* Localize a text to the locale/culture specified in the globalization handler. |
640
|
|
|
* @param string $text text to be localized. |
641
|
|
|
* @param array $parameters a set of parameters to substitute. |
642
|
|
|
* @param string $catalogue a different catalogue to find the localize text. |
643
|
|
|
* @param string $charset the input AND output charset. |
644
|
|
|
* @return string localized text. |
645
|
|
|
* @see TTranslate::formatter() |
646
|
|
|
* @see TTranslate::init() |
647
|
|
|
*/ |
648
|
|
|
public static function localize($text, $parameters = [], $catalogue = null, $charset = null) |
649
|
|
|
{ |
650
|
|
|
$app = Prado::getApplication()->getGlobalization(false); |
651
|
|
|
|
652
|
|
|
$params = []; |
653
|
|
|
foreach ($parameters as $key => $value) { |
654
|
|
|
$params['{' . $key . '}'] = $value; |
655
|
|
|
} |
656
|
|
|
|
657
|
|
|
//no translation handler provided |
658
|
|
|
if ($app === null || ($config = $app->getTranslationConfiguration()) === null) { |
659
|
|
|
return strtr($text, $params); |
660
|
|
|
} |
661
|
|
|
|
662
|
|
|
if ($catalogue === null) { |
663
|
|
|
$catalogue = $config['catalogue'] ?? 'messages'; |
664
|
|
|
} |
665
|
|
|
|
666
|
|
|
Translation::init($catalogue); |
667
|
|
|
|
668
|
|
|
//globalization charset |
669
|
|
|
$appCharset = $app === null ? '' : $app->getCharset(); |
670
|
|
|
|
671
|
|
|
//default charset |
672
|
|
|
$defaultCharset = ($app === null) ? 'UTF-8' : $app->getDefaultCharset(); |
673
|
|
|
|
674
|
|
|
//fall back |
675
|
|
|
if (empty($charset)) { |
676
|
|
|
$charset = $appCharset; |
677
|
|
|
} |
678
|
|
|
if (empty($charset)) { |
679
|
|
|
$charset = $defaultCharset; |
680
|
|
|
} |
681
|
|
|
|
682
|
|
|
return Translation::formatter($catalogue)->format($text, $params, $catalogue, $charset); |
683
|
|
|
} |
684
|
|
|
} |
685
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.