Passed
Push — master ( aa6590...763f4b )
by Alexander
02:05
created

Aliases::getRoot()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 2
b 0
f 0
nc 2
nop 1
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 2
rs 10
1
<?php
2
3
namespace Yiisoft\Aliases;
4
5
final class Aliases
6
{
7
    private array $aliases = [];
8
9
    /**
10
     * @param array $config
11
     * @throws \InvalidArgumentException if $path is an invalid alias.
12
     * @see set()
13
     * @see get()
14
     */
15 4
    public function __construct(array $config = [])
16
    {
17 4
        foreach ($config as $alias => $path) {
18 1
            $this->set($alias, $path);
19
        }
20
    }
21
22
    /**
23
     * Magic setter to enable simple aliases configuration.
24
     * @param string $name
25
     * @param string $value
26
     */
27
    public function __set(string $name, string $value): void
28
    {
29
        $this->set($name, $value);
30
    }
31
32
    /**
33
     * Registers a path alias.
34
     *
35
     * A path alias is a short name representing a long path (a file path, a URL, etc.)
36
     * For example, we use '@yii' as the alias of the path to the Yii framework directory.
37
     *
38
     * A path alias must start with the character '@' so that it can be easily differentiated
39
     * from non-alias paths.
40
     *
41
     * Note that this method does not check if the given path exists or not. All it does is
42
     * to associate the alias with the path.
43
     *
44
     * Any trailing '/' and '\' characters in the given path will be trimmed.
45
     *
46
     * See the [guide article on aliases](guide:concept-aliases) for more information.
47
     *
48
     * @param string $alias the alias name (e.g. "@yii"). It must start with a '@' character.
49
     * It may contain the forward slash '/' which serves as boundary character when performing
50
     * alias translation by [[get()]].
51
     * @param string $path the path corresponding to the alias. If this is null, the alias will
52
     * be removed. Trailing '/' and '\' characters will be trimmed. This can be
53
     *
54
     * - a directory or a file path (e.g. `/tmp`, `/tmp/main.txt`)
55
     * - a URL (e.g. `http://www.yiiframework.com`)
56
     * - a path alias (e.g. `@yii/base`). In this case, the path alias will be converted into the
57
     *   actual path first by calling [[get()]].
58
     *
59
     * @throws \InvalidArgumentException if $path is an invalid alias.
60
     * @see get()
61
     */
62 4
    public function set(string $alias, ?string $path): void
63
    {
64 4
        if (!$this->isAlias($alias)) {
65 1
            $alias = '@' . $alias;
66
        }
67 4
        $pos = strpos($alias, '/');
68 4
        $root = $pos === false ? $alias : substr($alias, 0, $pos);
69 4
        if ($path !== null) {
70 4
            $path = !$this->isAlias($path) ? rtrim($path, '\\/') : $this->get($path);
71 4
            if (!isset($this->aliases[$root])) {
72 4
                if ($pos === false) {
73 3
                    $this->aliases[$root] = $path;
74
                } else {
75 4
                    $this->aliases[$root] = [$alias => $path];
76
                }
77 3
            } elseif (\is_string($this->aliases[$root])) {
78 2
                if ($pos === false) {
79
                    $this->aliases[$root] = $path;
80
                } else {
81 2
                    $this->aliases[$root] = [
82 2
                        $alias => $path,
83 2
                        $root => $this->aliases[$root],
84
                    ];
85
                }
86
            } else {
87 1
                $this->aliases[$root][$alias] = $path;
88 4
                krsort($this->aliases[$root]);
89
            }
90 1
        } elseif (isset($this->aliases[$root])) {
91 1
            if (\is_array($this->aliases[$root])) {
92 1
                unset($this->aliases[$root][$alias]);
93
            } elseif ($pos === false) {
94
                unset($this->aliases[$root]);
95
            }
96
        }
97
    }
98
99
    /**
100
     * Translates a path alias into an actual path.
101
     *
102
     * The translation is done according to the following procedure:
103
     *
104
     * 1. If the given alias does not start with '@', it is returned back without change;
105
     * 2. Otherwise, look for the longest registered alias that matches the beginning part
106
     *    of the given alias. If it exists, replace the matching part of the given alias with
107
     *    the corresponding registered path.
108
     * 3. Throw an exception or return false, depending on the `$throwException` parameter.
109
     *
110
     * For example, by default '@yii' is registered as the alias to the Yii framework directory,
111
     * say '/path/to/yii'. The alias '@yii/web' would then be translated into '/path/to/yii/web'.
112
     *
113
     * If you have registered two aliases '@foo' and '@foo/bar'. Then translating '@foo/bar/config'
114
     * would replace the part '@foo/bar' (instead of '@foo') with the corresponding registered path.
115
     * This is because the longest alias takes precedence.
116
     *
117
     * However, if the alias to be translated is '@foo/barbar/config', then '@foo' will be replaced
118
     * instead of '@foo/bar', because '/' serves as the boundary character.
119
     *
120
     * Note, this method does not check if the returned path exists or not.
121
     *
122
     * See the [guide article on aliases](guide:concept-aliases) for more information.
123
     *
124
     * @param string $alias the alias to be translated.
125
     * @return string the path corresponding to the alias.
126
     * @throws \InvalidArgumentException if the root alias is not previously registered.
127
     * @see setAlias()
128
     */
129 3
    public function get(string $alias): string
130
    {
131 3
        if (!$this->isAlias($alias)) {
132 1
            return $alias;
133
        }
134
135 3
        $result = $this->findAlias($alias);
136
137 3
        if (\is_array($result)) {
138 3
            return $result['path'];
139
        }
140
141 2
        throw new \InvalidArgumentException("Invalid path alias: $alias");
142
    }
143
144
    /**
145
     * Returns the root alias part of a given alias.
146
     * A root alias is an alias that has been registered via [[setAlias()]] previously.
147
     * If a given alias matches multiple root aliases, the longest one will be returned.
148
     * @param string $alias the alias
149
     * @return string the root alias, or null if no root alias is found
150
     */
151 2
    public function getRoot(string $alias): ?string
152
    {
153 2
        $result = $this->findAlias($alias);
154 2
        if (\is_array($result)) {
155 2
            $result = $result['root'];
156
        }
157
158 2
        return $result;
159
    }
160
161 4
    private function findAlias(string $alias): ?array
162
    {
163 4
        $pos = strpos($alias, '/');
164 4
        $root = $pos === false ? $alias : substr($alias, 0, $pos);
165
166 4
        if (isset($this->aliases[$root])) {
167 4
            if (\is_string($this->aliases[$root])) {
168
                return [
169 3
                    'root' => $root,
170
                    'path' => $pos === false
171 3
                        ? $this->aliases[$root]
172 3
                        : $this->aliases[$root] . substr($alias, $pos)
173
                ];
174
            }
175
176 3
            foreach ($this->aliases[$root] as $name => $path) {
177 3
                if (strpos($alias . '/', $name . '/') === 0) {
178
                    return [
179 3
                        'root' => $name,
180 3
                        'path' => $path . substr($alias, strlen($name))
181
                    ];
182
                }
183
            }
184
        }
185
186 2
        return null;
187
    }
188
189
    public function getAll(): array
190
    {
191
        return $this->aliases;
192
    }
193
194 4
    private function isAlias(string $alias): bool
195
    {
196 4
        return !strncmp($alias, '@', 1);
197
    }
198
}
199