├── tools
├── .gitignore
├── psalm
│ └── composer.json
└── infection
│ └── composer.json
├── config
├── params.php
└── di.php
├── composer-require-checker.json
├── rector.php
├── LICENSE.md
├── CHANGELOG.md
├── src
├── AliasReference.php
└── Aliases.php
├── composer.json
└── README.md
/tools/.gitignore:
--------------------------------------------------------------------------------
1 | /*/vendor
2 | /*/composer.lock
3 |
--------------------------------------------------------------------------------
/tools/psalm/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require-dev": {
3 | "vimeo/psalm": "^5.26.1 || ^6.13"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/config/params.php:
--------------------------------------------------------------------------------
1 | [
7 | 'aliases' => [],
8 | ],
9 | ];
10 |
--------------------------------------------------------------------------------
/tools/infection/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require-dev": {
3 | "infection/infection": "^0.26 || ^0.31.9"
4 | },
5 | "config": {
6 | "allow-plugins": {
7 | "infection/extension-installer": true
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/config/di.php:
--------------------------------------------------------------------------------
1 | [
11 | '__construct()' => [$params['yiisoft/aliases']['aliases']],
12 | ],
13 | ];
14 |
--------------------------------------------------------------------------------
/composer-require-checker.json:
--------------------------------------------------------------------------------
1 | {
2 | "symbol-whitelist": [
3 | "Psr\\Container\\ContainerInterface",
4 | "Yiisoft\\Definitions\\Contract\\ReferenceInterface"
5 | ],
6 | "php-core-extensions": [
7 | "Core",
8 | "date",
9 | "json",
10 | "pcre",
11 | "Phar",
12 | "Reflection",
13 | "SPL",
14 | "standard",
15 | "fileinfo"
16 | ],
17 | "scan-files": []
18 | }
19 |
--------------------------------------------------------------------------------
/rector.php:
--------------------------------------------------------------------------------
1 | paths([
12 | __DIR__ . '/src',
13 | __DIR__ . '/tests',
14 | ]);
15 |
16 | // register a single rule
17 | $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
18 |
19 | // define sets of rules
20 | $rectorConfig->sets([
21 | LevelSetList::UP_TO_PHP_74,
22 | ]);
23 |
24 | $rectorConfig->skip([
25 | ClosureToArrowFunctionRector::class,
26 | ]);
27 | };
28 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright © 2008 by Yii Software ()
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions
6 | are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright
11 | notice, this list of conditions and the following disclaimer in
12 | the documentation and/or other materials provided with the
13 | distribution.
14 | * Neither the name of Yii Software nor the names of its
15 | contributors may be used to endorse or promote products derived
16 | from this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 | POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Yii Aliases Change Log
2 |
3 | ## 3.1.2 under development
4 |
5 | - no changes in this release.
6 |
7 | ## 3.1.1 December 04, 2025
8 |
9 | - Enh #85: Add PHP 8.5 support (@vjik)
10 |
11 | ## 3.1.0 August 12, 2025
12 |
13 | - New #84: Add `AliasReference` (@vjik)
14 | - Chg #81, #83: Change PHP constraint in `composer.json` to `8.1 - 8.4` (@vjik)
15 | - Chg #83: Bump PHP minimal version to 8.1 (@vjik)
16 |
17 | ## 3.0.0 February 13, 2023
18 |
19 | - New #44: Add method `Aliases::getArray()` that bulk translates path aliases into actual paths (@vjik)
20 | - Chg #59: Adapt configuration group names to Yii conventions (@vjik)
21 |
22 | ## 2.0.0 April 13, 2021
23 |
24 | - Chg #40: Remove magic `__set()` that was leading to weird side-effects (@samdark)
25 |
26 | ## 1.1.4 April 13, 2021
27 |
28 | - Chg: Adjust config for yiisoft/factory changes (@vjik, @samdark)
29 |
30 | ## 1.1.3 March 23, 2021
31 |
32 | - Chg: Adjust config for new config plugin (@samdark)
33 |
34 | ## 1.1.2 December 24, 2020
35 |
36 | - Bug #35: Don't throw TypeError from method `getAll()`, when alias is nested (@Fantom409)
37 |
38 | ## 1.1.1 November 06, 2020
39 |
40 | - Bug #28: Add `params` to `config-plugin` section in `composer.json` (@vjik)
41 |
42 | ## 1.1.0 November 4, 2020
43 |
44 | - Chg #26: Change Yii configuration `$params['aliases']` to `$params['yiisoft/aliases']['aliases']` (@vjik)
45 |
46 | ## 1.0.3 October 6, 2020
47 |
48 | - Enh: Don't error on config assembly in case aliases aren't set (@samdark)
49 |
50 | ## 1.0.2 October 5, 2020
51 |
52 | - Enh: Add a config for composer-config-plugin (@xepozz)
53 |
54 | ## 1.0.1 August 21, 2020
55 |
56 | - Enh: Allow installing on PHP 8 (@samdark)
57 |
58 | ## 1.0.0 June 7, 2020
59 |
60 | - Initial release.
61 |
--------------------------------------------------------------------------------
/src/AliasReference.php:
--------------------------------------------------------------------------------
1 | alias = $alias;
29 | }
30 |
31 | /**
32 | * Creates a new alias reference.
33 | *
34 | * @param string $id The alias to be resolved.
35 | *
36 | * @return self An instance of reference.
37 | *
38 | * @psalm-suppress MoreSpecificImplementedParamType, DocblockTypeContradiction
39 | */
40 | public static function to(mixed $id): self
41 | {
42 | if (!is_string($id)) {
43 | throw new InvalidArgumentException('Alias must be a string.');
44 | }
45 | return new self($id);
46 | }
47 |
48 | /**
49 | * Retrieves {@see Aliases} from the container and uses it to resolve the alias to its actual path.
50 | *
51 | * @param ContainerInterface $container The DI container.
52 | *
53 | * @return string The resolved path for the alias.
54 | */
55 | public function resolve(ContainerInterface $container): string
56 | {
57 | /** @var Aliases $aliases */
58 | $aliases = $container->get(Aliases::class);
59 | return $aliases->get($this->alias);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "yiisoft/aliases",
3 | "type": "library",
4 | "description": "Named paths and URLs storage",
5 | "keywords": [
6 | "alias"
7 | ],
8 | "homepage": "https://www.yiiframework.com/",
9 | "license": "BSD-3-Clause",
10 | "support": {
11 | "issues": "https://github.com/yiisoft/aliases/issues?state=open",
12 | "source": "https://github.com/yiisoft/aliases",
13 | "forum": "https://www.yiiframework.com/forum/",
14 | "wiki": "https://www.yiiframework.com/wiki/",
15 | "irc": "ircs://irc.libera.chat:6697/yii",
16 | "chat": "https://t.me/yii3en"
17 | },
18 | "funding": [
19 | {
20 | "type": "opencollective",
21 | "url": "https://opencollective.com/yiisoft"
22 | },
23 | {
24 | "type": "github",
25 | "url": "https://github.com/sponsors/yiisoft"
26 | }
27 | ],
28 | "require": {
29 | "php": "8.1 - 8.5"
30 | },
31 | "require-dev": {
32 | "bamarni/composer-bin-plugin": "^1.8.3",
33 | "maglnet/composer-require-checker": "^4.7.1",
34 | "phpunit/phpunit": "^10.5.48",
35 | "psr/container": "^2.0.2",
36 | "rector/rector": "^2.1.2",
37 | "spatie/phpunit-watcher": "^1.24.0",
38 | "yiisoft/definitions": "^3.4",
39 | "yiisoft/di": "^1.4",
40 | "yiisoft/test-support": "^3.0.2"
41 | },
42 | "autoload": {
43 | "psr-4": {
44 | "Yiisoft\\Aliases\\": "src"
45 | }
46 | },
47 | "autoload-dev": {
48 | "psr-4": {
49 | "Yiisoft\\Aliases\\Tests\\": "tests"
50 | }
51 | },
52 | "config": {
53 | "sort-packages": true,
54 | "allow-plugins": {
55 | "bamarni/composer-bin-plugin": true,
56 | "composer/package-versions-deprecated": true
57 | }
58 | },
59 | "extra": {
60 | "bamarni-bin": {
61 | "bin-links": true,
62 | "target-directory": "tools",
63 | "forward-command": true
64 | },
65 | "config-plugin-options": {
66 | "source-directory": "config"
67 | },
68 | "config-plugin": {
69 | "params": "params.php",
70 | "di": "di.php"
71 | }
72 | },
73 | "scripts": {
74 | "test": "phpunit --testdox --no-interaction",
75 | "test-watch": "phpunit-watcher watch"
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Yii Aliases
6 |
7 |
8 |
9 | [](https://packagist.org/packages/yiisoft/aliases)
10 | [](https://packagist.org/packages/yiisoft/aliases)
11 | [](https://github.com/yiisoft/aliases/actions/workflows/build.yml)
12 | [](https://codecov.io/gh/yiisoft/aliases)
13 | [](https://dashboard.stryker-mutator.io/reports/github.com/yiisoft/aliases/master)
14 | [](https://github.com/yiisoft/aliases/actions/workflows/static.yml?query=branch%3Amaster)
15 | [](https://shepherd.dev/github/yiisoft/aliases)
16 |
17 | The package aim is to store path aliases, i.e. short name representing a long path (a file path, a URL, etc.).
18 | Path alias value may have another value as its part. For example, `@vendor` may store path to `vendor` directory
19 | while `@bin` may store `@vendor/bin`.
20 |
21 | ## Requirements
22 |
23 | - PHP 8.1 - 8.5.
24 |
25 | ## Installation
26 |
27 | The package could be installed with [Composer](https://getcomposer.org):
28 |
29 | ```shell
30 | composer require yiisoft/aliases
31 | ```
32 |
33 | ## General usage
34 |
35 | A path alias must start with the character '@' so that it can be easily differentiated from non-alias paths.
36 |
37 | ```php
38 | use Yiisoft\Aliases\Aliases;
39 |
40 | $aliases = new Aliases([
41 | '@root' => __DIR__,
42 | ]);
43 | $aliases->set('@vendor', '@root/vendor');
44 | $aliases->set('@bin', '@vendor/bin');
45 |
46 | echo $aliases->get('@bin/phpunit');
47 | ```
48 |
49 | The code about would output "/path/to/vendor/bin/phpunit".
50 |
51 | Note that `set()` method does not check if the given path exists or not. All it does is to associate the alias with
52 | the path.
53 |
54 | The path could be:
55 |
56 | - a directory or a file path (e.g. `/tmp`, `/tmp/main.txt`)
57 | - a URL (e.g. `https://www.yiiframework.com`)
58 | - a path alias (e.g. `@yii/base`). It will be resolved on {@see get()} call.
59 |
60 | Any trailing `/` and `\` characters in the given path will be trimmed.
61 |
62 | To bulk translate path aliases into actual paths use `getArray()` method:
63 |
64 | ```php
65 | $aliases = new Aliases([
66 | '@root' => '/my/app',
67 | ]);
68 |
69 | // Value will be ['src' => '/my/app/src', 'tests' => '/my/app/tests']
70 | $directories = $aliases->getAll(['src' => '@root/src', 'tests' => '@root/tests']);
71 | ```
72 |
73 | ### Alias priorities
74 |
75 | In case multiple aliases are registered with same root (prefix), then the most specific has precedence:
76 |
77 | ```php
78 | use Yiisoft\Aliases\Aliases;
79 |
80 | $aliases = new Aliases([
81 | '@vendor' => __DIR__ . '/vendor',
82 | '@vendor/test' => '/special/location'
83 | ]);
84 | echo $aliases->get('@vendor/test');
85 | ```
86 |
87 | That would output `/special/location` since `@vendor/test` is more specific match than `@vendor`.
88 |
89 | ### Alias removal
90 |
91 | If you need to remove alias runtime:
92 |
93 | ```php
94 | use Yiisoft\Aliases\Aliases;
95 |
96 | $aliases = new Aliases([
97 | '@root' => __DIR__,
98 | ]);
99 | $aliases->remove('@root');
100 | ```
101 |
102 | ### Alias references
103 |
104 | The package provides `AliasReference` class that implements `ReferenceInterface` from
105 | [Yii Definition](https://github.com/yiisoft/definitions). It allows you to create references to aliases
106 | that will be resolved at runtime:
107 |
108 | ```php
109 | // Create a reference to an alias
110 | $reference = \Yiisoft\Aliases\AliasReference::to('@public/assets');
111 |
112 | // The reference will be resolved when needed
113 | $configPath = $reference->resolve($container);
114 | ```
115 |
116 | This is particularly useful in dependency injection configurations where you want to inject resolved paths
117 | but the aliases are not available at configuration time.
118 |
119 | ## Documentation
120 |
121 | - [Internals](docs/internals.md)
122 |
123 | If you need help or have a question, the [Yii Forum](https://forum.yiiframework.com/c/yii-3-0/63) is a good place for that.
124 | You may also check out other [Yii Community Resources](https://www.yiiframework.com/community).
125 |
126 | ## License
127 |
128 | The Yii Aliases is free software. It is released under the terms of the BSD License.
129 | Please see [`LICENSE`](./LICENSE.md) for more information.
130 |
131 | Maintained by [Yii Software](https://www.yiiframework.com/).
132 |
133 | ## Support the project
134 |
135 | [](https://opencollective.com/yiisoft)
136 |
137 | ## Follow updates
138 |
139 | [](https://www.yiiframework.com/)
140 | [](https://twitter.com/yiiframework)
141 | [](https://t.me/yii3en)
142 | [](https://www.facebook.com/groups/yiitalk)
143 | [](https://yiiframework.com/go/slack)
144 |
--------------------------------------------------------------------------------
/src/Aliases.php:
--------------------------------------------------------------------------------
1 | >
18 | */
19 | private array $aliases = [];
20 |
21 | /**
22 | * @psalm-param array $config
23 | *
24 | * @throws InvalidArgumentException If `$path` is an invalid alias.
25 | *
26 | * @see set()
27 | * @see get()
28 | */
29 | public function __construct(array $config = [])
30 | {
31 | foreach ($config as $alias => $path) {
32 | $this->set($alias, $path);
33 | }
34 | }
35 |
36 | /**
37 | * Registers a path alias.
38 | *
39 | * A path alias is a short name representing a long path (a file path, a URL, etc.)
40 | *
41 | * For example, `@vendor` may store path to `vendor` directory.
42 | *
43 | * A path alias must start with the character '@' so that it can be easily differentiated
44 | * from non-alias paths.
45 | *
46 | * Note that this method does not check if the given path exists or not. All it does is
47 | * to associate the alias with the path.
48 | *
49 | * Any trailing '/' and '\' characters in the given path will be trimmed.
50 | *
51 | * @param string $alias the alias name (e.g. "@vendor"). It must start with a '@' character.
52 | * It may contain the forward slash '/' which serves as boundary character when performing
53 | * alias translation by {@see get()}.
54 | * @param string $path the path corresponding to the alias.
55 | * Trailing '/' and '\' characters will be trimmed. This can be
56 | *
57 | * - a directory or a file path (e.g. `/tmp`, `/tmp/main.txt`)
58 | * - a URL (e.g. `https://www.yiiframework.com`)
59 | * - a path alias (e.g. `@vendor/yiisoft`). It will be resolved on {@see get()} call.
60 | *
61 | * @see get()
62 | */
63 | public function set(string $alias, string $path): void
64 | {
65 | if (!$this->isAlias($alias)) {
66 | $alias = '@' . $alias;
67 | }
68 | $pos = strpos($alias, '/');
69 | /** @psalm-var string $root */
70 | $root = $pos === false ? $alias : substr($alias, 0, $pos);
71 |
72 | $path = rtrim($path, '\\/');
73 | if (!array_key_exists($root, $this->aliases)) {
74 | if ($pos === false) {
75 | $this->aliases[$root] = $path;
76 | } else {
77 | $this->aliases[$root] = [$alias => $path];
78 | }
79 | } elseif (is_string($this->aliases[$root])) {
80 | if ($pos === false) {
81 | $this->aliases[$root] = $path;
82 | } else {
83 | $this->aliases[$root] = [
84 | $alias => $path,
85 | $root => $this->aliases[$root],
86 | ];
87 | }
88 | } else {
89 | $this->aliases[$root][$alias] = $path;
90 | krsort($this->aliases[$root]);
91 | }
92 | }
93 |
94 | /**
95 | * Remove alias.
96 | *
97 | * @param string $alias Alias to be removed.
98 | */
99 | public function remove(string $alias): void
100 | {
101 | if (!$this->isAlias($alias)) {
102 | $alias = '@' . $alias;
103 | }
104 | $pos = strpos($alias, '/');
105 | $root = $pos === false ? $alias : substr($alias, 0, $pos);
106 |
107 | if (array_key_exists($root, $this->aliases)) {
108 | if (is_array($this->aliases[$root])) {
109 | unset($this->aliases[$root][$alias]);
110 | } elseif ($pos === false) {
111 | unset($this->aliases[$root]);
112 | }
113 | }
114 | }
115 |
116 | /**
117 | * Translates a path alias into an actual path.
118 | *
119 | * The translation is done according to the following procedure:
120 | *
121 | * 1. If the given alias does not start with '@', it is returned back without change;
122 | * 2. Otherwise, look for the longest registered alias that matches the beginning part
123 | * of the given alias. If it exists, replace the matching part of the given alias with
124 | * the corresponding registered path.
125 | * 3. Throw an exception if path alias cannot be resolved.
126 | *
127 | * For example, if '@vendor' is registered as the alias to the vendor directory,
128 | * say '/path/to/vendor'. The alias '@vendor/yiisoft' would then be translated into '/path/to/vendor/yiisoft'.
129 | *
130 | * If you have registered two aliases '@foo' and '@foo/bar'. Then translating '@foo/bar/config'
131 | * would replace the part '@foo/bar' (instead of '@foo') with the corresponding registered path.
132 | * This is because the longest alias takes precedence.
133 | *
134 | * However, if the alias to be translated is '@foo/barbar/config', then '@foo' will be replaced
135 | * instead of '@foo/bar', because '/' serves as the boundary character.
136 | *
137 | * Note, this method does not check if the returned path exists or not.
138 | *
139 | * @param string $alias The alias to be translated.
140 | *
141 | * @throws InvalidArgumentException If the root alias is not previously registered.
142 | *
143 | * @return string The path corresponding to the alias.
144 | *
145 | * @see setAlias()
146 | */
147 | public function get(string $alias): string
148 | {
149 | if (!$this->isAlias($alias)) {
150 | return $alias;
151 | }
152 |
153 | $foundAlias = $this->findAlias($alias);
154 |
155 | if ($foundAlias === null) {
156 | throw new InvalidArgumentException("Invalid path alias: $alias");
157 | }
158 |
159 | $foundSubAlias = $this->findAlias($foundAlias);
160 | if ($foundSubAlias === null) {
161 | return $foundAlias;
162 | }
163 |
164 | return $this->get($foundSubAlias);
165 | }
166 |
167 | /**
168 | * Bulk translates path aliases into actual paths.
169 | *
170 | * @param string[] $aliases Aliases to be translated.
171 | *
172 | * @throws InvalidArgumentException If the root alias was not previously registered.
173 | *
174 | * @return string[] The paths corresponding to the aliases.
175 | */
176 | public function getArray(array $aliases): array
177 | {
178 | return array_map(
179 | fn (string $alias) => $this->get($alias),
180 | $aliases,
181 | );
182 | }
183 |
184 | /**
185 | * Returns all path aliases translated into an actual paths.
186 | *
187 | * @return array Actual paths indexed by alias name.
188 | */
189 | public function getAll(): array
190 | {
191 | $result = [];
192 | foreach ($this->aliases as $name => $path) {
193 | if (is_array($path)) {
194 | foreach ($path as $innerName => $innerPath) {
195 | $result[$innerName] = $innerPath;
196 | }
197 | } else {
198 | $result[$name] = $this->get($path);
199 | }
200 | }
201 |
202 | return $result;
203 | }
204 |
205 | private function findAlias(string $alias): ?string
206 | {
207 | $pos = strpos($alias, '/');
208 | $root = $pos === false ? $alias : substr($alias, 0, $pos);
209 |
210 | if (array_key_exists($root, $this->aliases)) {
211 | if (is_string($this->aliases[$root])) {
212 | return $pos === false ? $this->aliases[$root] : $this->aliases[$root] . substr($alias, $pos);
213 | }
214 |
215 | foreach ($this->aliases[$root] as $name => $path) {
216 | if (strpos($alias . '/', $name . '/') === 0) {
217 | return $path . substr($alias, strlen($name));
218 | }
219 | }
220 | }
221 |
222 | return null;
223 | }
224 |
225 | private function isAlias(string $alias): bool
226 | {
227 | return !strncmp($alias, '@', 1);
228 | }
229 | }
230 |
--------------------------------------------------------------------------------