Passed
Push — master ( ba4715...959769 )
by Alexander
12:17
created

Aliases::set()   B

Complexity

Conditions 11
Paths 36

Size

Total Lines 32
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 12.2432

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 11
eloc 25
c 2
b 0
f 0
nc 36
nop 2
dl 0
loc 32
ccs 18
cts 23
cp 0.7826
crap 12.2432
rs 7.3166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 7
    public function __construct(array $config = [])
16
    {
17 7
        foreach ($config as $alias => $path) {
18 3
            $this->set($alias, $path);
19
        }
20 7
    }
21
22
    /**
23
     * Magic setter to enable simple aliases configuration.
24
     * @param string $name
25
     * @param string $value
26
     */
27 1
    public function __set(string $name, string $value): void
28
    {
29 1
        $this->set($name, $value);
30 1
    }
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 7
    public function set(string $alias, ?string $path): void
63
    {
64 7
        if (!$this->isAlias($alias)) {
65 2
            $alias = '@' . $alias;
66
        }
67 7
        $pos = strpos($alias, '/');
68 7
        $root = $pos === false ? $alias : substr($alias, 0, $pos);
69 7
        if ($path !== null) {
70 7
            if (!array_key_exists($root, $this->aliases)) {
71 7
                if ($pos === false) {
72 6
                    $this->aliases[$root] = $path;
73
                } else {
74 7
                    $this->aliases[$root] = [$alias => $path];
75
                }
76 3
            } elseif (\is_string($this->aliases[$root])) {
77 2
                if ($pos === false) {
78
                    $this->aliases[$root] = $path;
79
                } else {
80 2
                    $this->aliases[$root] = [
81 2
                        $alias => $path,
82 2
                        $root => $this->aliases[$root],
83
                    ];
84
                }
85
            } else {
86 1
                $this->aliases[$root][$alias] = $path;
87 7
                krsort($this->aliases[$root]);
88
            }
89 1
        } elseif (!array_key_exists($root, $this->aliases)) {
90
            if (\is_array($this->aliases[$root])) {
91
                unset($this->aliases[$root][$alias]);
92
            } elseif ($pos === false) {
93
                unset($this->aliases[$root]);
94
            }
95
        }
96 7
    }
97
98
    /**
99
     * Translates a path alias into an actual path.
100
     *
101
     * The translation is done according to the following procedure:
102
     *
103
     * 1. If the given alias does not start with '@', it is returned back without change;
104
     * 2. Otherwise, look for the longest registered alias that matches the beginning part
105
     *    of the given alias. If it exists, replace the matching part of the given alias with
106
     *    the corresponding registered path.
107
     * 3. Throw an exception or return false, depending on the `$throwException` parameter.
108
     *
109
     * For example, by default '@yii' is registered as the alias to the Yii framework directory,
110
     * say '/path/to/yii'. The alias '@yii/web' would then be translated into '/path/to/yii/web'.
111
     *
112
     * If you have registered two aliases '@foo' and '@foo/bar'. Then translating '@foo/bar/config'
113
     * would replace the part '@foo/bar' (instead of '@foo') with the corresponding registered path.
114
     * This is because the longest alias takes precedence.
115
     *
116
     * However, if the alias to be translated is '@foo/barbar/config', then '@foo' will be replaced
117
     * instead of '@foo/bar', because '/' serves as the boundary character.
118
     *
119
     * Note, this method does not check if the returned path exists or not.
120
     *
121
     * See the [guide article on aliases](guide:concept-aliases) for more information.
122
     *
123
     * @param string $alias the alias to be translated.
124
     * @return string the path corresponding to the alias.
125
     * @throws \InvalidArgumentException if the root alias is not previously registered.
126
     * @see setAlias()
127
     */
128 6
    public function get(string $alias): string
129
    {
130 6
        if (!$this->isAlias($alias)) {
131 4
            return $alias;
132
        }
133
134 6
        $result = $this->findAlias($alias);
135
136 6
        if (\is_array($result)) {
137 6
            $parts = $this->findAlias($result['path']);
138 6
            if ($parts === null) {
139 5
                return $result['path'];
140
            }
141
142 4
            return $this->get($parts['path']);
143
        }
144
145 2
        throw new \InvalidArgumentException("Invalid path alias: $alias");
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 the root alias, or null if no root alias is found
154
     */
155 2
    public function getRoot(string $alias): ?string
156
    {
157 2
        $result = $this->findAlias($alias);
158 2
        if (\is_array($result)) {
159 2
            $result = $result['root'];
160
        }
161
162 2
        return $result;
163
    }
164
165 7
    private function findAlias(string $alias): ?array
166
    {
167 7
        $pos = strpos($alias, '/');
168 7
        $root = $pos === false ? $alias : substr($alias, 0, $pos);
169
170 7
        if (array_key_exists($root, $this->aliases)) {
171 7
            if (\is_string($this->aliases[$root])) {
172
                return [
173 5
                    'root' => $root,
174
                    'path' => $pos === false
175 4
                        ? $this->aliases[$root]
176 5
                        : $this->aliases[$root] . substr($alias, $pos)
177
                ];
178
            }
179
180 3
            foreach ($this->aliases[$root] as $name => $path) {
181 3
                if (strpos($alias . '/', $name . '/') === 0) {
182
                    return [
183 3
                        'root' => $name,
184 3
                        'path' => $path . substr($alias, strlen($name))
185
                    ];
186
                }
187
            }
188
        }
189
190 5
        return null;
191
    }
192
193
    /**
194
     * Returns all path aliases translated into an actual paths.
195
     * @return array Actual paths indexed by alias name.
196
     */
197 1
    public function getAll(): array
198
    {
199 1
        $result = [];
200 1
        foreach ($this->aliases as $name => $path) {
201 1
            $result[$name] = $this->get($path);
202
        }
203 1
        return $result;
204
    }
205
206 7
    private function isAlias(string $alias): bool
207
    {
208 7
        return !strncmp($alias, '@', 1);
209
    }
210
}
211