Completed
Push — master ( 69716e...1c0988 )
by Fabio
10:12
created

PradoBase::getAutoloader()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
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/prado4
10
 * @copyright Copyright &copy; 2005-2016 The PRADO Group
11
 * @license https://github.com/pradosoft/prado4/blob/master/LICENSE
12
 * @package Prado
13
 */
14
15
namespace Prado;
16
use Prado\Exceptions\TInvalidDataValueException;
17
use Prado\Exceptions\TInvalidOperationException;
18
use Prado\Exceptions\TPhpErrorException;
19
use Prado\Util\TLogger;
20
use Prado\Util\TVarDumper;
21
use Prado\I18N\Translation;
22
23
/**
24
 * Defines the PRADO framework installation path.
25
 */
26
if(!defined('PRADO_DIR'))
27
	define('PRADO_DIR', __DIR__);
28
/**
29
 * Defines the default permission for writable directories and files
30
 */
31
if(!defined('PRADO_CHMOD'))
32
	define('PRADO_CHMOD',0777);
33
/**
34
 * Defines the Composer's vendor/ path.
35
 */
36
if(!defined('PRADO_VENDORDIR'))
37
{
38
	$reflector = new \ReflectionClass('\Composer\Autoload\ClassLoader');
39
	define('PRADO_VENDORDIR',dirname(dirname($reflector->getFileName())));
40
}
41
42
43
/**
44
 * PradoBase class.
45
 *
46
 * PradoBase implements a few fundamental static methods.
47
 *
48
 * To use the static methods, Use Prado as the class name rather than PradoBase.
49
 * PradoBase is meant to serve as the base class of Prado. The latter might be
50
 * rewritten for customization.
51
 *
52
 * @author Qiang Xue <[email protected]>
53
 * @package Prado
54
 * @since 3.0
55
 */
56
class PradoBase
57
{
58
	/**
59
	 * File extension for Prado class files.
60
	 */
61
	const CLASS_FILE_EXT='.php';
62
	/**
63
	 * @var array list of path aliases
64
	 */
65
	private static $_aliases=array(
66
		'Prado'=>PRADO_DIR,
67
		'Vendor'=>PRADO_VENDORDIR
68
		);
69
	/**
70
	 * @var array list of namespaces currently in use
71
	 */
72
	private static $_usings=array(
73
		'Prado'=>PRADO_DIR
74
		);
75
	/**
76
	 * @var array list of namespaces currently in use
77
	 */
78
	public static $classMap=array();	
79
	/**
80
	 * @var TApplication the application instance
81
	 */
82
	private static $_application=null;
83
	/**
84
	 * @var TLogger logger instance
85
	 */
86
	private static $_logger=null;
87
	/**
88
	 * @var array list of class exists checks
89
	 */
90
	protected static $classExists = array();
91
	/**
92
	 * @return string the version of Prado framework
93
	 */
94
	public static function getVersion()
95
	{
96
		return '4.0.0';
97
	}
98
99
	public static function init()
100
	{
101
		static::initAutoloader();
102
		static::initErrorHandlers();
103
	}
104
105
	/**
106
	 * Loads the static classmap and registers the autoload function.
107
	 */
108
	public static function initAutoloader()
109
	{
110
		self::$classMap = require(__DIR__ . '/classes.php');
111
112
		spl_autoload_register(array(get_called_class(), 'autoload'));
113
	}
114
115
	/**
116
	 * Initializes error handlers.
117
	 * This method set error and exception handlers to be functions
118
	 * defined in this class.
119
	 */
120
	public static function initErrorHandlers()
121
	{
122
		/**
123
		 * Sets error handler to be Prado::phpErrorHandler
124
		 */
125
		set_error_handler(array('\Prado\PradoBase','phpErrorHandler'));
126
		/**
127
		 * Sets shutdown function to be Prado::phpFatalErrorHandler
128
		 */
129
		register_shutdown_function(array('PradoBase','phpFatalErrorHandler'));
130
		/**
131
		 * Sets exception handler to be Prado::exceptionHandler
132
		 */
133
		set_exception_handler(array('\Prado\PradoBase','exceptionHandler'));
134
		/**
135
		 * Disable php's builtin error reporting to avoid duplicated reports
136
		 */
137
		ini_set('display_errors', 0);
138
	}
139
140
	/**
141
	 * Class autoload loader.
142
	 * This method is provided to be invoked within an __autoload() magic method.
143
	 * @param string class name
144
	 */
145
	public static function autoload($className)
146
	{
147
		static::using($className);
148
	}
149
150
	/**
151
	 * @param integer the type of "powered logo". Valid values include 0 and 1.
152
	 * @return string a string that can be displayed on your Web page showing powered-by-PRADO information
153
	 */
154
	public static function poweredByPrado($logoType=0)
155
	{
156
		$logoName=$logoType==1?'powered2':'powered';
157
		if(self::$_application!==null)
158
		{
159
			$am=self::$_application->getAssetManager();
160
			$url=$am->publishFilePath(self::getPathOfNamespace('Prado\\'.$logoName,'.gif'));
161
		}
162
		else
163
			$url='http://pradosoft.github.io/docs/'.$logoName.'.gif';
164
		return '<a title="Powered by PRADO" href="https://github.com/pradosoft/prado4" target="_blank"><img src="'.$url.'" style="border-width:0px;" alt="Powered by PRADO" /></a>';
165
	}
166
167
	/**
168
	 * PHP error handler.
169
	 * This method should be registered as PHP error handler using
170
	 * {@link set_error_handler}. The method throws an exception that
171
	 * contains the error information.
172
	 * @param integer the level of the error raised
173
	 * @param string the error message
174
	 * @param string the filename that the error was raised in
175
	 * @param integer the line number the error was raised at
176
	 */
177
	public static function phpErrorHandler($errno,$errstr,$errfile,$errline)
178
	{
179
		if(error_reporting() & $errno)
180
			throw new TPhpErrorException($errno,$errstr,$errfile,$errline);
181
	}
182
183
	/**
184
	 * PHP shutdown function used to catch fatal errors.
185
	 * This method should be registered as PHP error handler using
186
	 * {@link register_shutdown_function}. The method throws an exception that
187
	 * contains the error information.
188
	 */
189
	public static function phpFatalErrorHandler()
190
	{
191
		$error = error_get_last();
192
		if($error && 
0 ignored issues
show
Bug Best Practice introduced by
The expression $error of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
193
			TPhpErrorException::isFatalError($error) &&
194
			error_reporting() & $error['type'])
195
		{
196
			self::exceptionHandler(new TPhpErrorException($error['type'],$error['message'],$error['file'],$error['line']));
197
		}
198
	}
199
200
	/**
201
	 * Default exception handler.
202
	 * This method should be registered as default exception handler using
203
	 * {@link set_exception_handler}. The method tries to use the errorhandler
204
	 * module of the Prado application to handle the exception.
205
	 * If the application or the module does not exist, it simply echoes the
206
	 * exception.
207
	 * @param Exception exception that is not caught
208
	 */
209
	public static function exceptionHandler($exception)
210
	{
211
		if(self::$_application!==null && ($errorHandler=self::$_application->getErrorHandler())!==null)
212
		{
213
			$errorHandler->handleError(null,$exception);
214
		}
215
		else
216
		{
217
			echo $exception;
218
		}
219
		exit(1);
0 ignored issues
show
Coding Style Compatibility introduced by
The method exceptionHandler() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
220
	}
221
222
	/**
223
	 * Stores the application instance in the class static member.
224
	 * This method helps implement a singleton pattern for TApplication.
225
	 * Repeated invocation of this method or the application constructor
226
	 * will cause the throw of an exception.
227
	 * This method should only be used by framework developers.
228
	 * @param TApplication the application instance
229
	 * @throws TInvalidOperationException if this method is invoked twice or more.
230
	 */
231
	public static function setApplication($application)
232
	{
233
		if(self::$_application!==null && !defined('PRADO_TEST_RUN'))
234
			throw new TInvalidOperationException('prado_application_singleton_required');
235
		self::$_application=$application;
236
	}
237
238
	/**
239
	 * @return TApplication the application singleton, null if the singleton has not be created yet.
240
	 */
241
	public static function getApplication()
242
	{
243
		return self::$_application;
244
	}
245
246
	/**
247
	 * @return string the path of the framework
248
	 */
249
	public static function getFrameworkPath()
250
	{
251
		return PRADO_DIR;
252
	}
253
254
	/**
255
	 * Convert old Prado namespaces to PHP namespaces
256
	 * @param string old class name in Prado3 namespace format
257
	 * @return string Equivalent class name in PHP namespace format
258
	 */
259
260
	protected static function prado3NamespaceToPhpNamespace($type)
261
	{
262
		if(substr($type, 0, 6) === 'System')
263
			$type='Prado'.substr($type, 6);
264
265
		return str_replace('.', '\\', $type);
266
	}
267
268
	/**
269
	 * Creates a component with the specified type.
270
	 * A component type can be either the component class name
271
	 * or a namespace referring to the path of the component class file.
272
	 * For example, 'TButton', '\Prado\Web\UI\WebControls\TButton' are both
273
	 * valid component type.
274
	 * This method can also pass parameters to component constructors.
275
	 * All parameters passed to this method except the first one (the component type)
276
	 * will be supplied as component constructor parameters.
277
	 * @param string component type
278
	 * @return TComponent component instance of the specified type
279
	 * @throws TInvalidDataValueException if the component type is unknown
280
	 */
281
	public static function createComponent($requestedType)
282
	{
283
		$type = static::prado3NamespaceToPhpNamespace($requestedType);
284
		if(!isset(self::$classExists[$type]))
285
			self::$classExists[$type] = class_exists($type, false);
286
287
		if( !isset(self::$_usings[$type]) && !self::$classExists[$type]) {
288
			static::using($type);
289
			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
		if( ($pos = strrpos($type, '\\')) !== false && ($requestedType != $type) && strpos($type, 'Prado\\') !== 0)
300
			$type = substr($type,$pos+1);
301
302
		if(($n=func_num_args())>1)
303
		{
304
			$args = func_get_args();
305
			switch($n) {
306
				case 2:
307
					return new $type($args[1]);
308
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
309
				case 3:
310
					return new $type($args[1], $args[2]);
311
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
312
				case 4:
313
					return new $type($args[1], $args[2], $args[3]);
314
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
315
				case 5:
316
					return new $type($args[1], $args[2], $args[3], $args[4]);
317
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
318
				default:
319
					$s='$args[1]';
320
					for($i=2;$i<$n;++$i)
321
						$s.=",\$args[$i]";
322
					eval("\$component=new $type($s);");
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
323
					return $component;
0 ignored issues
show
Bug introduced by
The variable $component does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
324
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
325
			}
326
		}
327
		else
328
			return new $type;
329
	}
330
331
	/**
332
	 * Uses a namespace.
333
	 * A namespace ending with an asterisk '*' refers to a directory, otherwise it represents a PHP file.
334
	 * If the namespace corresponds to a directory, the directory will be appended
335
	 * to the include path. If the namespace corresponds to a file, it will be included (include_once).
336
	 * @param string namespace to be used
337
	 * @param boolean whether to check the existence of the class after the class file is included
338
	 * @throws TInvalidDataValueException if the namespace is invalid
339
	 */
340
	public static function using($namespace,$checkClassExistence=true)
341
	{
342
		$namespace = static::prado3NamespaceToPhpNamespace($namespace);
343
344
		if(isset(self::$_usings[$namespace]) || class_exists($namespace,false))
345
			return;
346
347
		if(array_key_exists($namespace, self::$classMap))
348
		{
349
			// fast autoload a Prado3 class name
350
			$phpNamespace = self::$classMap[$namespace];
351
			if(class_exists($phpNamespace, true) || interface_exists($phpNamespace, true))
352
			{
353
				if(!class_exists($namespace) && !interface_exists($namespace))
354
					class_alias($phpNamespace, $namespace, true);
355
				return;
356
			}			
357
		} elseif(($pos=strrpos($namespace,'\\'))===false) {
358
			// trying to autoload an old class name
359
			foreach(self::$_usings as $k => $v)
360
			{
361
				$path = $v . DIRECTORY_SEPARATOR . $namespace . self::CLASS_FILE_EXT;
362
				if(file_exists($path))
363
				{
364
					$phpNamespace = '\\'. $k.'\\'.$namespace;
365
					if(class_exists($phpNamespace, true) || interface_exists($phpNamespace, true))
366
					{
367
						if(!class_exists($namespace) && !interface_exists($namespace))
368
							class_alias($phpNamespace, $namespace, true);
369
						return;
370
					}
371
				}
372
			}
373
374
			if($checkClassExistence && !class_exists($namespace,false) && !interface_exists($namespace,false))
375
				throw new TInvalidOperationException('prado_component_unknown',$namespace,'');
376
		} elseif(($path=self::getPathOfNamespace($namespace,self::CLASS_FILE_EXT))!==null) {
377
			$className=substr($namespace,$pos+1);
378
			if($className==='*')  // a directory
379
			{
380
				self::$_usings[substr($namespace, 0, $pos)]=$path;
381
			}
382
			else  // a file
383
			{
384
				//self::$_usings[$namespace]=$path;
385
				if(!$checkClassExistence || (!class_exists($className,false) && !interface_exists($className, false)))
386
				{
387
					try
388
					{
389
						include_once($path);
390
						if(class_exists($namespace, false) || interface_exists($namespace, false))
391
							class_alias($namespace, $className, true);
392
					}
393
					catch(\Exception $e)
394
					{
395
						if($checkClassExistence && !class_exists($className,false))
396
							throw new TInvalidOperationException('prado_component_unknown',$className,$e->getMessage());
397
						else
398
							throw $e;
399
					}
400
				}
401
			}
402
		}
403
	}
404
405
	/**
406
	 * Translates a namespace into a file path.
407
	 * The first segment of the namespace is considered as a path alias
408
	 * which is replaced with the actual path. The rest segments are
409
	 * subdirectory names appended to the aliased path.
410
	 * If the namespace ends with an asterisk '*', it represents a directory;
411
	 * Otherwise it represents a file whose extension name is specified by the second parameter (defaults to empty).
412
	 * Note, this method does not ensure the existence of the resulting file path.
413
	 * @param string namespace
414
	 * @param string extension to be appended if the namespace refers to a file
415
	 * @return string file path corresponding to the namespace, null if namespace is invalid
416
	 */
417
	public static function getPathOfNamespace($namespace, $ext='')
418
	{
419
		$namespace = static::prado3NamespaceToPhpNamespace($namespace);
420
421
		if(self::CLASS_FILE_EXT === $ext || empty($ext))
422
		{
423
			if(isset(self::$_usings[$namespace]))
424
				return self::$_usings[$namespace];
425
426
			if(isset(self::$_aliases[$namespace]))
427
				return self::$_aliases[$namespace];
428
		}
429
430
		$segs = explode('\\',$namespace);
431
		$alias = array_shift($segs);
432
433
		if(null !== ($file = array_pop($segs)) && null !== ($root = self::getPathOfAlias($alias)))
434
			return rtrim($root.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR ,$segs),'/\\').(($file === '*') ? '' : DIRECTORY_SEPARATOR.$file.$ext);
435
436
		return null;
437
	}
438
439
	/**
440
	 * @param string alias to the path
441
	 * @return string the path corresponding to the alias, null if alias not defined.
442
	 */
443
	public static function getPathOfAlias($alias)
444
	{
445
		return isset(self::$_aliases[$alias])?self::$_aliases[$alias]:null;
446
	}
447
448
	protected static function getPathAliases()
449
	{
450
		return self::$_aliases;
451
	}
452
453
	/**
454
	 * @param string alias to the path
455
	 * @param string the path corresponding to the alias
456
	 * @throws TInvalidOperationException if the alias is already defined
457
	 * @throws TInvalidDataValueException if the path is not a valid file path
458
	 */
459
	public static function setPathOfAlias($alias,$path)
460
	{
461
		if(isset(self::$_aliases[$alias]) && !defined('PRADO_TEST_RUN'))
462
			throw new TInvalidOperationException('prado_alias_redefined',$alias);
463
		else if(($rp=realpath($path))!==false && is_dir($rp))
464
		{
465
			if(strpos($alias,'.')===false)
466
				self::$_aliases[$alias]=$rp;
467
			else
468
				throw new TInvalidDataValueException('prado_aliasname_invalid',$alias);
469
		}
470
		else
471
			throw new TInvalidDataValueException('prado_alias_invalid',$alias,$path);
472
	}
473
474
	/**
475
	 * Fatal error handler.
476
	 * This method displays an error message together with the current call stack.
477
	 * The application will exit after calling this method.
478
	 * @param string error message
479
	 */
480
	public static function fatalError($msg)
481
	{
482
		echo '<h1>Fatal Error</h1>';
483
		echo '<p>'.$msg.'</p>';
484
		if(!function_exists('debug_backtrace'))
485
			return;
486
		echo '<h2>Debug Backtrace</h2>';
487
		echo '<pre>';
488
		$index=-1;
489
		foreach(debug_backtrace() as $t)
490
		{
491
			$index++;
492
			if($index==0)  // hide the backtrace of this function
493
				continue;
494
			echo '#'.$index.' ';
495
			if(isset($t['file']))
496
				echo basename($t['file']) . ':' . $t['line'];
497
			else
498
				 echo '<PHP inner-code>';
499
			echo ' -- ';
500
			if(isset($t['class']))
501
				echo $t['class'] . $t['type'];
502
			echo $t['function'] . '(';
503
			if(isset($t['args']) && sizeof($t['args']) > 0)
504
			{
505
				$count=0;
506
				foreach($t['args'] as $item)
507
				{
508
					if(is_string($item))
509
					{
510
						$str=htmlentities(str_replace("\r\n", "", $item), ENT_QUOTES);
511
						if (strlen($item) > 70)
512
							echo "'". substr($str, 0, 70) . "...'";
513
						else
514
							echo "'" . $str . "'";
515
					}
516
					else if (is_int($item) || is_float($item))
517
						echo $item;
518
					else if (is_object($item))
519
						echo get_class($item);
520
					else if (is_array($item))
521
						echo 'array(' . count($item) . ')';
522
					else if (is_bool($item))
523
						echo $item ? 'true' : 'false';
524
					else if ($item === null)
525
						echo 'NULL';
526
					else if (is_resource($item))
527
						echo get_resource_type($item);
528
					$count++;
529
					if (count($t['args']) > $count)
530
						echo ', ';
531
				}
532
			}
533
			echo ")\n";
534
		}
535
		echo '</pre>';
536
		exit(1);
0 ignored issues
show
Coding Style Compatibility introduced by
The method fatalError() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
537
	}
538
539
	/**
540
	 * Returns a list of user preferred languages.
541
	 * The languages are returned as an array. Each array element
542
	 * represents a single language preference. The languages are ordered
543
	 * according to user preferences. The first language is the most preferred.
544
	 * @return array list of user preferred languages.
545
	 */
546
	public static function getUserLanguages()
0 ignored issues
show
Coding Style introduced by
getUserLanguages uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
547
	{
548
		static $languages=null;
549
		if($languages===null)
550
		{
551
			if(!isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
552
				$languages[0]='en';
553
			else
554
			{
555
				$languages=array();
556
				foreach(explode(',',$_SERVER['HTTP_ACCEPT_LANGUAGE']) as $language)
557
				{
558
					$array=explode(';q=',trim($language));
559
					$languages[trim($array[0])]=isset($array[1])?(float)$array[1]:1.0;
560
				}
561
				arsort($languages);
562
				$languages=array_keys($languages);
563
				if(empty($languages))
564
					$languages[0]='en';
565
			}
566
		}
567
		return $languages;
568
	}
569
570
	/**
571
	 * Returns the most preferred language by the client user.
572
	 * @return string the most preferred language by the client user, defaults to English.
573
	 */
574
	public static function getPreferredLanguage()
575
	{
576
		static $language=null;
577
		if($language===null)
578
		{
579
			$langs=Prado::getUserLanguages();
580
			$lang=explode('-',$langs[0]);
581
			if(empty($lang[0]) || !ctype_alpha($lang[0]))
582
				$language='en';
583
			else
584
				$language=$lang[0];
585
		}
586
		return $language;
587
	}
588
589
	/**
590
	 * Writes a log message.
591
	 * This method wraps {@link log()} by checking the application mode.
592
	 * When the application is in Debug mode, debug backtrace information is appended
593
	 * to the message and the message is logged at DEBUG level.
594
	 * When the application is in Performance mode, this method does nothing.
595
	 * Otherwise, the message is logged at INFO level.
596
	 * @param string message to be logged
597
	 * @param string category of the message
598
	 * @param (string|TControl) control of the message
599
	 * @see log, getLogger
600
	 */
601
	public static function trace($msg,$category='Uncategorized',$ctl=null)
602
	{
603
		if(self::$_application && self::$_application->getMode()===TApplicationMode::Performance)
604
			return;
605
		if(!self::$_application || self::$_application->getMode()===TApplicationMode::Debug)
606
		{
607
			$trace=debug_backtrace();
608
			if(isset($trace[0]['file']) && isset($trace[0]['line']))
609
				$msg.=" (line {$trace[0]['line']}, {$trace[0]['file']})";
610
			$level=TLogger::DEBUG;
611
		}
612
		else
613
			$level=TLogger::INFO;
614
		self::log($msg,$level,$category,$ctl);
615
	}
616
617
	/**
618
	 * Logs a message.
619
	 * Messages logged by this method may be retrieved via {@link TLogger::getLogs}
620
	 * and may be recorded in different media, such as file, email, database, using
621
	 * {@link TLogRouter}.
622
	 * @param string message to be logged
623
	 * @param integer level of the message. Valid values include
624
	 * TLogger::DEBUG, TLogger::INFO, TLogger::NOTICE, TLogger::WARNING,
625
	 * TLogger::ERROR, TLogger::ALERT, TLogger::FATAL.
626
	 * @param string category of the message
627
	 * @param (string|TControl) control of the message
628
	 */
629
	public static function log($msg,$level=TLogger::INFO,$category='Uncategorized',$ctl=null)
630
	{
631
		if(self::$_logger===null)
632
			self::$_logger=new TLogger;
633
		self::$_logger->log($msg,$level,$category,$ctl);
634
	}
635
636
	/**
637
	 * @return TLogger message logger
638
	 */
639
	public static function getLogger()
640
	{
641
		if(self::$_logger===null)
642
			self::$_logger=new TLogger;
643
		return self::$_logger;
644
	}
645
646
	/**
647
	 * Converts a variable into a string representation.
648
	 * This method achieves the similar functionality as var_dump and print_r
649
	 * but is more robust when handling complex objects such as PRADO controls.
650
	 * @param mixed variable to be dumped
651
	 * @param integer maximum depth that the dumper should go into the variable. Defaults to 10.
652
	 * @param boolean whether to syntax highlight the output. Defaults to false.
653
	 * @return string the string representation of the variable
654
	 */
655
	public static function varDump($var,$depth=10,$highlight=false)
656
	{
657
		return TVarDumper::dump($var,$depth,$highlight);
658
	}
659
660
	/**
661
	 * Localize a text to the locale/culture specified in the globalization handler.
662
	 * @param string text to be localized.
663
	 * @param array a set of parameters to substitute.
664
	 * @param string a different catalogue to find the localize text.
665
	 * @param string the input AND output charset.
666
	 * @return string localized text.
667
	 * @see TTranslate::formatter()
668
	 * @see TTranslate::init()
669
	 */
670
	public static function localize($text, $parameters=array(), $catalogue=null, $charset=null)
671
	{
672
		$app = Prado::getApplication()->getGlobalization(false);
673
674
		$params = array();
675
		foreach($parameters as $key => $value)
676
			$params['{'.$key.'}'] = $value;
677
678
		//no translation handler provided
679
		if($app===null || ($config = $app->getTranslationConfiguration())===null)
680
			return strtr($text, $params);
681
682
		if ($catalogue===null)
683
			$catalogue=isset($config['catalogue'])?$config['catalogue']:'messages';
684
685
		Translation::init($catalogue);
686
687
		//globalization charset
688
		$appCharset = $app===null ? '' : $app->getCharset();
689
690
		//default charset
691
		$defaultCharset = ($app===null) ? 'UTF-8' : $app->getDefaultCharset();
692
693
		//fall back
694
		if(empty($charset)) $charset = $appCharset;
695
		if(empty($charset)) $charset = $defaultCharset;
696
697
		return Translation::formatter($catalogue)->format($text,$params,$catalogue,$charset);
698
	}
699
}
700