Passed
Push — master ( 959769...ee12a2 )
by Alexander
01:53 queued 49s
created

Aliases::set()   B

Complexity

Conditions 11
Paths 36

Size

Total Lines 33
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 11

Importance

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