Completed
Push — 2.1 ( 61dec0...0391a3 )
by Alexander
09:14
created

BaseYii::getRootAlias()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
crap 2
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii;
9
10
use Psr\Log\LoggerInterface;
11
use Psr\Log\LogLevel;
12
use yii\base\InvalidArgumentException;
13
use yii\base\InvalidConfigException;
14
use yii\di\Container;
15
use yii\di\Instance;
16
use yii\helpers\VarDumper;
17
use yii\log\Logger;
18
use yii\profile\Profiler;
19
use yii\profile\ProfilerInterface;
20
21
/**
22
 * Gets the application start timestamp.
23
 */
24
defined('YII_BEGIN_TIME') or define('YII_BEGIN_TIME', microtime(true));
25
/**
26
 * This constant defines the framework installation directory.
27
 */
28
defined('YII2_PATH') or define('YII2_PATH', __DIR__);
29
/**
30
 * This constant defines whether the application should be in debug mode or not. Defaults to false.
31
 */
32
defined('YII_DEBUG') or define('YII_DEBUG', false);
33
/**
34
 * This constant defines in which environment the application is running. Defaults to 'prod', meaning production environment.
35
 * You may define this constant in the bootstrap script. The value could be 'prod' (production), 'dev' (development), 'test', 'staging', etc.
36
 */
37
defined('YII_ENV') or define('YII_ENV', 'prod');
38
/**
39
 * Whether the the application is running in production environment
40
 */
41
defined('YII_ENV_PROD') or define('YII_ENV_PROD', YII_ENV === 'prod');
42
/**
43
 * Whether the the application is running in development environment
44
 */
45
defined('YII_ENV_DEV') or define('YII_ENV_DEV', YII_ENV === 'dev');
46
/**
47
 * Whether the the application is running in testing environment
48
 */
49
defined('YII_ENV_TEST') or define('YII_ENV_TEST', YII_ENV === 'test');
50
51
/**
52
 * This constant defines whether error handling should be enabled. Defaults to true.
53
 */
54
defined('YII_ENABLE_ERROR_HANDLER') or define('YII_ENABLE_ERROR_HANDLER', true);
55
56
/**
57
 * BaseYii is the core helper class for the Yii framework.
58
 *
59
 * Do not use BaseYii directly. Instead, use its child class [[\Yii]] which you can replace to
60
 * customize methods of BaseYii.
61
 *
62
 * @author Qiang Xue <[email protected]>
63
 * @since 2.0
64
 */
65
class BaseYii
66
{
67
    /**
68
     * @var \yii\console\Application|\yii\web\Application the application instance
69
     */
70
    public static $app;
71
    /**
72
     * @var array registered path aliases
73
     * @see getAlias()
74
     * @see setAlias()
75
     */
76
    public static $aliases = ['@yii' => __DIR__];
77
    /**
78
     * @var Container the dependency injection (DI) container used by [[createObject()]].
79
     * You may use [[Container::set()]] to set up the needed dependencies of classes and
80
     * their initial property values.
81
     * @see createObject()
82
     * @see Container
83
     */
84
    public static $container;
85
86
87
    /**
88
     * Returns a string representing the current version of the Yii framework.
89
     * @return string the version of Yii framework
90
     */
91 60
    public static function getVersion()
92
    {
93 60
        return '2.0.14-dev';
94
    }
95
96
    /**
97
     * Translates a path alias into an actual path.
98
     *
99
     * The translation is done according to the following procedure:
100
     *
101
     * 1. If the given alias does not start with '@', it is returned back without change;
102
     * 2. Otherwise, look for the longest registered alias that matches the beginning part
103
     *    of the given alias. If it exists, replace the matching part of the given alias with
104
     *    the corresponding registered path.
105
     * 3. Throw an exception or return false, depending on the `$throwException` parameter.
106
     *
107
     * For example, by default '@yii' is registered as the alias to the Yii framework directory,
108
     * say '/path/to/yii'. The alias '@yii/web' would then be translated into '/path/to/yii/web'.
109
     *
110
     * If you have registered two aliases '@foo' and '@foo/bar'. Then translating '@foo/bar/config'
111
     * would replace the part '@foo/bar' (instead of '@foo') with the corresponding registered path.
112
     * This is because the longest alias takes precedence.
113
     *
114
     * However, if the alias to be translated is '@foo/barbar/config', then '@foo' will be replaced
115
     * instead of '@foo/bar', because '/' serves as the boundary character.
116
     *
117
     * Note, this method does not check if the returned path exists or not.
118
     *
119
     * See the [guide article on aliases](guide:concept-aliases) for more information.
120
     *
121
     * @param string $alias the alias to be translated.
122
     * @param bool $throwException whether to throw an exception if the given alias is invalid.
123
     * If this is false and an invalid alias is given, false will be returned by this method.
124
     * @return string|bool the path corresponding to the alias, false if the root alias is not previously registered.
125
     * @throws InvalidArgumentException if the alias is invalid while $throwException is true.
126
     * @see setAlias()
127
     */
128 3208
    public static function getAlias($alias, $throwException = true)
129
    {
130 3208
        if (strncmp($alias, '@', 1)) {
131
            // not an alias
132 2997
            return $alias;
133
        }
134
135 3127
        $result = static::findAlias($alias);
136
137 3127
        if (is_array($result)) {
138 3127
            return $result['path'];
139
        }
140
141 1
        if ($throwException) {
142 1
            throw new InvalidArgumentException("Invalid path alias: $alias");
143
        }
144
145 1
        return false;
146
    }
147
148
    /**
149
     * Returns the root alias part of a given alias.
150
     * A root alias is an alias that has been registered via [[setAlias()]] previously.
151
     * If a given alias matches multiple root aliases, the longest one will be returned.
152
     * @param string $alias the alias
153
     * @return string|bool the root alias, or false if no root alias is found
154
     */
155 1
    public static function getRootAlias($alias)
156
    {
157 1
        $result = static::findAlias($alias);
158 1
        if (is_array($result)) {
159 1
            $result = $result['root'];
160
        }
161 1
        return $result;
162
    }
163
164
    /**
165
     * @param string $alias
166
     * @return array|bool
167
     */
168 3128
    protected static function findAlias(string $alias)
169
    {
170 3128
        $pos = strpos($alias, '/');
171 3128
        $root = $pos === false ? $alias : substr($alias, 0, $pos);
172
173 3128
        if (isset(static::$aliases[$root])) {
174 3128
            if (is_string(static::$aliases[$root])) {
175 3127
                return ['root' => $root, 'path' => $pos === false ? static::$aliases[$root] : static::$aliases[$root] . substr($alias, $pos)];
176
            }
177
178 3
            foreach (static::$aliases[$root] as $name => $path) {
179 3
                if (strpos($alias . '/', $name . '/') === 0) {
180 3
                    return ['root' => $name, 'path' => $path . substr($alias, strlen($name))];
181
                }
182
            }
183
        }
184
185 1
        return false;
186
    }
187
188
    /**
189
     * Registers a path alias.
190
     *
191
     * A path alias is a short name representing a long path (a file path, a URL, etc.)
192
     * For example, we use '@yii' as the alias of the path to the Yii framework directory.
193
     *
194
     * A path alias must start with the character '@' so that it can be easily differentiated
195
     * from non-alias paths.
196
     *
197
     * Note that this method does not check if the given path exists or not. All it does is
198
     * to associate the alias with the path.
199
     *
200
     * Any trailing '/' and '\' characters in the given path will be trimmed.
201
     *
202
     * See the [guide article on aliases](guide:concept-aliases) for more information.
203
     *
204
     * @param string $alias the alias name (e.g. "@yii"). It must start with a '@' character.
205
     * It may contain the forward slash '/' which serves as boundary character when performing
206
     * alias translation by [[getAlias()]].
207
     * @param string $path the path corresponding to the alias. If this is null, the alias will
208
     * be removed. Trailing '/' and '\' characters will be trimmed. This can be
209
     *
210
     * - a directory or a file path (e.g. `/tmp`, `/tmp/main.txt`)
211
     * - a URL (e.g. `http://www.yiiframework.com`)
212
     * - a path alias (e.g. `@yii/base`). In this case, the path alias will be converted into the
213
     *   actual path first by calling [[getAlias()]].
214
     *
215
     * @throws InvalidArgumentException if $path is an invalid alias.
216
     * @see getAlias()
217
     */
218 2899
    public static function setAlias($alias, $path)
219
    {
220 2899
        if (strncmp($alias, '@', 1)) {
221 1
            $alias = '@' . $alias;
222
        }
223 2899
        $pos = strpos($alias, '/');
224 2899
        $root = $pos === false ? $alias : substr($alias, 0, $pos);
225 2899
        if ($path !== null) {
226 2899
            $path = strncmp($path, '@', 1) ? rtrim($path, '\\/') : static::getAlias($path);
227 2899
            if (!isset(static::$aliases[$root])) {
228 10
                if ($pos === false) {
229 9
                    static::$aliases[$root] = $path;
230
                } else {
231 10
                    static::$aliases[$root] = [$alias => $path];
232
                }
233 2898
            } elseif (is_string(static::$aliases[$root])) {
234 2897
                if ($pos === false) {
235 2895
                    static::$aliases[$root] = $path;
236
                } else {
237 2
                    static::$aliases[$root] = [
238 2
                        $alias => $path,
239 2897
                        $root => static::$aliases[$root],
240
                    ];
241
                }
242
            } else {
243 1
                static::$aliases[$root][$alias] = $path;
244 2899
                krsort(static::$aliases[$root]);
245
            }
246 3
        } elseif (isset(static::$aliases[$root])) {
247 3
            if (is_array(static::$aliases[$root])) {
248 1
                unset(static::$aliases[$root][$alias]);
249 2
            } elseif ($pos === false) {
250 2
                unset(static::$aliases[$root]);
251
            }
252
        }
253 2899
    }
254
255
    /**
256
     * Creates a new object using the given configuration.
257
     *
258
     * You may view this method as an enhanced version of the `new` operator.
259
     * The method supports creating an object based on a class name, a configuration array or
260
     * an anonymous function.
261
     *
262
     * Below are some usage examples:
263
     *
264
     * ```php
265
     * // create an object using a class name
266
     * $object = Yii::createObject(\yii\db\Connection::class);
267
     *
268
     * // create an object using a configuration array
269
     * $object = Yii::createObject([
270
     *     'class' => \yii\db\Connection::class,
271
     *     'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
272
     *     'username' => 'root',
273
     *     'password' => '',
274
     *     'charset' => 'utf8',
275
     * ]);
276
     *
277
     * // create an object with two constructor parameters
278
     * $object = \Yii::createObject('MyClass', [$param1, $param2]);
279
     * ```
280
     *
281
     * Using [[\yii\di\Container|dependency injection container]], this method can also identify
282
     * dependent objects, instantiate them and inject them into the newly created object.
283
     *
284
     * @param string|array|callable $type the object type. This can be specified in one of the following forms:
285
     *
286
     * - a string: representing the class name of the object to be created
287
     * - a configuration array: the array must contain a `class` element which is treated as the object class,
288
     *   and the rest of the name-value pairs will be used to initialize the corresponding object properties
289
     * - a PHP callable: either an anonymous function or an array representing a class method (`[$class or $object, $method]`).
290
     *   The callable should return a new instance of the object being created.
291
     *
292
     * @param array $params the constructor parameters
293
     * @return object the created object
294
     * @throws InvalidConfigException if the configuration is invalid.
295
     * @see \yii\di\Container
296
     */
297 2750
    public static function createObject($type, array $params = [])
298
    {
299 2750
        if (is_string($type)) {
300 919
            return static::$container->get($type, $params);
301 2710
        } elseif (is_array($type) && isset($type['class'])) {
302 2706
            $class = $type['class'];
303 2706
            unset($type['class']);
304 2706
            return static::$container->get($class, $params, $type);
305 8
        } elseif (is_callable($type, true)) {
306 6
            return static::$container->invoke($type, $params);
307 2
        } elseif (is_array($type)) {
308 1
            throw new InvalidConfigException('Object configuration must be an array containing a "class" element.');
309
        }
310
311 1
        throw new InvalidConfigException('Unsupported configuration type: ' . gettype($type));
312
    }
313
314
    /**
315
     * @var LoggerInterface logger instance.
316
     */
317
    private static $_logger;
318
319
    /**
320
     * @return LoggerInterface message logger
321
     */
322 1644
    public static function getLogger()
323
    {
324 1644
        if (self::$_logger !== null) {
325 1644
            return self::$_logger;
326
        }
327
328 1
        return self::$_logger = Instance::ensure(['class' => Logger::class], LoggerInterface::class);
329
    }
330
331
    /**
332
     * Sets the logger object.
333
     * @param LoggerInterface|\Closure|array|null $logger the logger object or its DI compatible configuration.
334
     */
335 26
    public static function setLogger($logger)
336
    {
337 26
        if ($logger === null) {
338 20
            self::$_logger = null;
339 20
            return;
340
        }
341
342 19
        if (is_array($logger)) {
343 7
            if (!isset($logger['class']) && is_object(self::$_logger)) {
344 7
                static::configure(self::$_logger, $logger);
345 7
                return;
346
            }
347 1
            $logger = array_merge(['class' => Logger::class], $logger);
348 13
        } elseif ($logger instanceof \Closure) {
349 1
            $logger = call_user_func($logger);
350
        }
351
352 13
        self::$_logger = Instance::ensure($logger, LoggerInterface::class);
353 13
    }
354
355
    /**
356
     * @var ProfilerInterface profiler instance.
357
     * @since 2.1
358
     */
359
    private static $_profiler;
360
361
    /**
362
     * @return ProfilerInterface profiler instance.
363
     * @since 2.1
364
     */
365 1418
    public static function getProfiler()
366
    {
367 1418
        if (self::$_profiler !== null) {
368 1418
            return self::$_profiler;
369
        }
370 2
        return self::$_profiler = Instance::ensure(['class' => Profiler::class], ProfilerInterface::class);
371
    }
372
373
    /**
374
     * @param ProfilerInterface|\Closure|array|null $profiler profiler instance or its DI compatible configuration.
375
     * @since 2.1
376
     */
377 9
    public static function setProfiler($profiler)
378
    {
379 9
        if ($profiler === null) {
380 9
            self::$_profiler = null;
381 9
            return;
382
        }
383
384 1
        if (is_array($profiler)) {
385 1
            if (!isset($profiler['class']) && is_object(self::$_profiler)) {
386 1
                static::configure(self::$_profiler, $profiler);
387 1
                return;
388
            }
389 1
            $profiler = array_merge(['class' => Profiler::class], $profiler);
390 1
        } elseif ($profiler instanceof \Closure) {
391 1
            $profiler = call_user_func($profiler);
392
        }
393
394 1
        self::$_profiler = Instance::ensure($profiler, ProfilerInterface::class);
395 1
    }
396
397
    /**
398
     * Logs a message with category.
399
     * @param string $level log level.
400
     * @param mixed $message the message to be logged. This can be a simple string or a more
401
     * complex data structure, such as array.
402
     * @param string $category the category of the message.
403
     * @since 2.1.0
404
     */
405 1643
    public static function log($level, $message, $category = 'application')
406
    {
407 1643
        $context = ['category' => $category];
408 1643
        if (!is_string($message)) {
409 12
            if ($message instanceof \Throwable) {
410
                // exceptions are string-convertable, thus should be passed as it is to the logger
411
                // if exception instance is given to produce a stack trace, it MUST be in a key named "exception".
412 1
                $context['exception'] = $message;
413
            } else {
414
                // exceptions may not be serializable if in the call stack somewhere is a Closure
415 11
                $message = VarDumper::export($message);
416
            }
417
        }
418 1643
        static::getLogger()->log($level, $message, $context);
0 ignored issues
show
Bug introduced by
It seems like $message defined by parameter $message on line 405 can also be of type object<Throwable>; however, Psr\Log\LoggerInterface::log() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
419 1643
    }
420
421
    /**
422
     * Logs a debug message.
423
     * Trace messages are logged mainly for development purpose to see
424
     * the execution work flow of some code.
425
     * @param string|array $message the message to be logged. This can be a simple string or a more
426
     * complex data structure, such as array.
427
     * @param string $category the category of the message.
428
     */
429 1509
    public static function debug($message, $category = 'application')
430
    {
431 1509
        if (YII_DEBUG) {
432 1509
            static::log(LogLevel::DEBUG, $message, $category);
433
        }
434 1509
    }
435
436
    /**
437
     * Logs an error message.
438
     * An error message is typically logged when an unrecoverable error occurs
439
     * during the execution of an application.
440
     * @param string|array $message the message to be logged. This can be a simple string or a more
441
     * complex data structure, such as array.
442
     * @param string $category the category of the message.
443
     */
444 6
    public static function error($message, $category = 'application')
445
    {
446 6
        static::log(LogLevel::ERROR, $message, $category);
447 6
    }
448
449
    /**
450
     * Logs a warning message.
451
     * A warning message is typically logged when an error occurs while the execution
452
     * can still continue.
453
     * @param string|array $message the message to be logged. This can be a simple string or a more
454
     * complex data structure, such as array.
455
     * @param string $category the category of the message.
456
     */
457 13
    public static function warning($message, $category = 'application')
458
    {
459 13
        static::log(LogLevel::WARNING, $message, $category);
460 13
    }
461
462
    /**
463
     * Logs an informative message.
464
     * An informative message is typically logged by an application to keep record of
465
     * something important (e.g. an administrator logs in).
466
     * @param string|array $message the message to be logged. This can be a simple string or a more
467
     * complex data structure, such as array.
468
     * @param string $category the category of the message.
469
     */
470 1455
    public static function info($message, $category = 'application')
471
    {
472 1455
        static::log(LogLevel::INFO, $message, $category);
473 1455
    }
474
475
    /**
476
     * Marks the beginning of a code block for profiling.
477
     *
478
     * This has to be matched with a call to [[endProfile]] with the same category name.
479
     * The begin- and end- calls must also be properly nested. For example,
480
     *
481
     * ```php
482
     * \Yii::beginProfile('block1');
483
     * // some code to be profiled
484
     *     \Yii::beginProfile('block2');
485
     *     // some other code to be profiled
486
     *     \Yii::endProfile('block2');
487
     * \Yii::endProfile('block1');
488
     * ```
489
     * @param string $token token for the code block
490
     * @param string $category the category of this log message
491
     * @see endProfile()
492
     */
493 1418
    public static function beginProfile($token, $category = 'application')
494
    {
495 1418
        static::getProfiler()->begin($token, ['category' => $category]);
496 1418
    }
497
498
    /**
499
     * Marks the end of a code block for profiling.
500
     * This has to be matched with a previous call to [[beginProfile]] with the same category name.
501
     * @param string $token token for the code block
502
     * @param string $category the category of this log message
503
     * @see beginProfile()
504
     */
505 1418
    public static function endProfile($token, $category = 'application')
506
    {
507 1418
        static::getProfiler()->end($token, ['category' => $category]);
508 1418
    }
509
510
    /**
511
     * Translates a message to the specified language.
512
     *
513
     * This is a shortcut method of [[\yii\i18n\I18N::translate()]].
514
     *
515
     * The translation will be conducted according to the message category and the target language will be used.
516
     *
517
     * You can add parameters to a translation message that will be substituted with the corresponding value after
518
     * translation. The format for this is to use curly brackets around the parameter name as you can see in the following example:
519
     *
520
     * ```php
521
     * $username = 'Alexander';
522
     * echo \Yii::t('app', 'Hello, {username}!', ['username' => $username]);
523
     * ```
524
     *
525
     * Further formatting of message parameters is supported using the [PHP intl extensions](http://www.php.net/manual/en/intro.intl.php)
526
     * message formatter. See [[\yii\i18n\I18N::translate()]] for more details.
527
     *
528
     * @param string $category the message category.
529
     * @param string $message the message to be translated.
530
     * @param array $params the parameters that will be used to replace the corresponding placeholders in the message.
531
     * @param string $language the language code (e.g. `en-US`, `en`). If this is null, the current
532
     * [[\yii\base\Application::language|application language]] will be used.
533
     * @return string the translated message.
534
     */
535 730
    public static function t($category, $message, $params = [], $language = null)
536
    {
537 730
        if (static::$app !== null) {
538 525
            return static::$app->getI18n()->translate($category, $message, $params, $language ?: static::$app->language);
539
        }
540
541 205
        $placeholders = [];
542 205
        foreach ((array) $params as $name => $value) {
543 3
            $placeholders['{' . $name . '}'] = $value;
544
        }
545
546 205
        return ($placeholders === []) ? $message : strtr($message, $placeholders);
547
    }
548
549
    /**
550
     * Configures an object with the initial property values.
551
     * @param object $object the object to be configured
552
     * @param array $properties the property initial values given in terms of name-value pairs.
553
     * @return object the object itself
554
     */
555 3399
    public static function configure($object, $properties)
556
    {
557 3399
        foreach ($properties as $name => $value) {
558 3399
            $object->$name = $value;
559
        }
560
561 3399
        return $object;
562
    }
563
564
    /**
565
     * Returns the public member variables of an object.
566
     * This method is provided such that we can get the public member variables of an object.
567
     * It is different from "get_object_vars()" because the latter will return private
568
     * and protected variables if it is called within the object itself.
569
     * @param object $object the object to be handled
570
     * @return array the public member variables of the object
571
     */
572 3
    public static function getObjectVars($object)
573
    {
574 3
        return get_object_vars($object);
575
    }
576
}
577