Passed
Push — master ( a50b30...2c49a0 )
by 世昌
04:23
created

ModuleBag::getUniqueName()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 1
dl 0
loc 11
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace suda\application;
4
5
use function array_key_exists;
6
use ArrayIterator;
7
use function explode;
8
use function implode;
9
use function in_array;
10
use IteratorAggregate;
11
use function sprintf;
12
use function strpos;
13
use function strrpos;
14
use function strtolower;
15
use function substr;
16
use suda\framework\filesystem\FileSystem;
17
use suda\application\exception\ApplicationException;
18
use function version_compare;
19
20
/**
21
 * 模块名
22
 */
23
class ModuleBag implements IteratorAggregate
24
{
25
    /**
26
     * 模块
27
     *
28
     * @var Module[]
29
     */
30
    protected $module = [];
31
32
    /**
33
     * 已知全部全名
34
     *
35
     * @var array
36
     */
37
    protected $version = [];
38
39
    /**
40
     * 查找缓存
41
     *
42
     * @var array
43
     */
44
    protected $cache = [];
45
46
    /**
47
     * 添加模块
48
     *
49
     * @param Module $module
50
     * @return void
51
     */
52
    public function add(Module $module)
53
    {
54
        $name    = $module->getName();
55
        $version = $module->getVersion();
56
        $unique  = $module->getUnique();
57
        $version = $this->formatVersion($version);
58
        if (!in_array($name, $this->version)) {
59
            $this->version[$name][$version] = $unique;
60
            uksort($this->version[$name], [$this, 'sort']);
61
        }
62
        if (in_array($unique, $this->module)) {
63
            throw new ApplicationException(sprintf('conflict module unique name %s', $unique), ApplicationException::ERR_CONFLICT_MODULE_UNIQUE);
64
        }
65
        $this->module[$unique] = $module;
66
    }
67
68
    /**
69
     * 合并模块包
70
     *
71
     * @param ModuleBag $module
72
     * @return void
73
     */
74
    public function merge(ModuleBag $module)
75
    {
76
        $this->module  = array_merge($this->module, $module->module);
77
        $this->version = array_merge($this->version, $module->version);
78
        $this->cache   = array_merge($this->cache, $module->cache);
79
    }
80
81
    /**
82
     * 推测文件所在模块
83
     *
84
     * @param string $path
85
     * @return Module|null
86
     */
87
    public function guess(string $path): ?Module
88
    {
89
        foreach ($this->module as $module) {
90
            if (FileSystem::isOverflowPath($module->getPath(), $path) === false) {
91
                return $module;
92
            }
93
        }
94
        return null;
95
    }
96
97
    /**
98
     * 根据路径获取所在模块
99
     *
100
     * @param string $path
101
     * @return Module
102
     */
103
    public function getModuleFromPath(string $path): Module
104
    {
105
        if (($module = $this->guess($path)) !== null) {
106
            return $module;
107
        }
108
        throw new ApplicationException(
109
            sprintf('path %s not exist in any module', $path),
110
            ApplicationException::ERR_PATH_NOT_EXISTS_IN_MODULE
111
        );
112
    }
113
114
    /**
115
     * 获取模块
116
     *
117
     * @param string $name
118
     * @return Module|null
119
     */
120
    public function get(string $name): ?Module
121
    {
122
        $uniqueName = $this->getUniqueName($name);
123
        return $this->module[$uniqueName] ?? null;
124
    }
125
126
    /**
127
     * 获取所有模块
128
     *
129
     * @return Module[]
130
     */
131
    public function all(): array
132
    {
133
        return $this->module;
134
    }
135
136
    public function getIterator()
137
    {
138
        return new ArrayIterator($this->module);
139
    }
140
141
    /**
142
     * 检测模块是否存在
143
     *
144
     * @param string $name
145
     * @return boolean
146
     */
147
    public function exist(string $name): bool
148
    {
149
        return $this->get($name) !== null;
150
    }
151
152
    /**
153
     * 拆分名称
154
     * [namespace/]name[:version]:data -> [namespace/name:version,data]
155
     *
156
     * @param string $name
157
     * @param string|null $default
158
     * @return array
159
     */
160
    public function info(string $name, ?string $default = null): array
161
    {
162
        $rp = strrpos($name, ':');
163
        if ($rp > 0) {
164
            $module     = substr($name, 0, $rp);
165
            $name       = substr($name, $rp + 1);
166
            $uniqueName = $this->getUniqueName($module);
167
            return [$uniqueName, $name];
168
        }
169
        if ($rp === 0) {
170
            return [$default, substr($name, 1)];
171
        }
172
        return [$default, $name];
173
    }
174
175
    /**
176
     * 获取模块全名
177
     *
178
     * @param string $name
179
     * @return string
180
     */
181
    public function getUniqueName(string $name): string
182
    {
183
        if (array_key_exists($name, $this->module)) {
184
            return $name;
185
        }
186
        if (array_key_exists($name, $this->cache)) {
187
            return $this->cache[$name];
188
        }
189
        $uniqueName         = $this->createUniqueName($name);
190
        $this->cache[$name] = $uniqueName;
191
        return $uniqueName;
192
    }
193
194
    /**
195
     * 创建模块全名
196
     *
197
     * @param string $name
198
     * @return string
199
     */
200
    protected function createUniqueName(string $name)
201
    {
202
        $version    = null;
203
        $hasVersion = false;
204
        if (strpos($name, ':')) {
205
            $hasVersion = true;
206
            list($shortName, $version) = explode(':', $name, 2);
207
        } else {
208
            $shortName = $name;
209
        }
210
        $shortName = $this->getLikeName($shortName);
211
        if (array_key_exists($shortName, $this->version) === false) {
212
            return $name;
213
        }
214
        if (array_key_exists($version, $this->version[$shortName])) {
215
            return $this->version[$shortName][$version];
216
        }
217
        return $hasVersion ? $shortName . ':' . $version : end($this->version[$shortName]);
218
    }
219
220
    protected function getLikeName(string $name): string
221
    {
222
        $names = [];
223
        foreach (array_keys($this->version) as $keyName) {
224
            if (strpos($keyName, $name) !== false) {
225
                $names[] = $keyName;
226
            }
227
        }
228
        if (count($names) === 0) {
229
            return $name;
230
        }
231
        if (count($names) > 1 && in_array($name, $names) === false) {
232
            throw new ApplicationException(sprintf('conflict module name %s in %s', $name, implode(',', $names)), ApplicationException::ERR_CONFLICT_MODULE_NAME);
233
        }
234
        return $names[array_search($name, $names)];
235
    }
236
237
    protected function sort(string $a, string $b)
238
    {
239
        return version_compare($a, $b);
240
    }
241
242
    protected function formatVersion(string $version)
243
    {
244
        $version = strtolower($version);
245
        if (strpos($version, 'v') === 0) {
246
            return substr($version, 1);
247
        }
248
        return $version;
249
    }
250
}
251