Passed
Push — master ( 82e9fe...cecb4c )
by Sheldon
04:44
created

Urn   A

Complexity

Total Complexity 38

Size/Duplication

Total Lines 473
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 473
rs 9.36
c 0
b 0
f 0
wmc 38

21 Methods

Rating   Name   Duplication   Size   Complexity  
A setName() 0 7 2
A getUrn() 0 3 1
A setUrn() 0 7 2
A getVendorProduct() 0 3 1
A getVersion() 0 3 1
A validate() 0 3 1
A getExpression() 0 3 1
A getName() 0 3 1
A setBaseUrn() 0 19 4
A __construct() 0 10 2
A setVendorProduct() 0 7 2
A setExpression() 0 19 4
A getType() 0 3 1
A _parse() 0 19 4
A setValue() 0 7 2
A getValue() 0 3 1
A getBaseUrn() 0 3 1
A setVersion() 0 7 2
A setNamespace() 0 7 2
A getNamespace() 0 3 1
A setType() 0 7 2
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: sheldon
5
 * Date: 18-6-6
6
 * Time: 下午5:58.
7
 */
8
9
namespace MiotApi\Contract;
10
11
use MiotApi\Contract\Interfaces\Urn as UrnInterface;
12
use MiotApi\Exception\SpecificationErrorException;
13
14
class Urn implements UrnInterface
15
{
16
    /**
17
     * 符合 RFC 2141 的 URN正则规则.
18
     */
19
    const URN_REGEXP = '/^urn:[a-z0-9][a-z0-9-]{1,31}:([a-z0-9()+,-.:=@;$_!*\']|%(0[1-9a-f]|[1-9a-f][0-9a-f]))+$/i';
20
21
    /**
22
     * 分隔符.
23
     *
24
     * @var string
25
     */
26
    private $delimiter = ':';
27
28
    /**
29
     * 原始urn.
30
     *
31
     * @var
32
     */
33
    private $original;
34
35
    /**
36
     * 符合 RFC 2141 和 小米规范的 URN.
37
     *
38
     * @var
39
     */
40
    private $expression;
41
42
    /**
43
     * 小米 URN 规范所包含的字段
44
     * <URN> ::= "urn:"<namespace>":"<type>":"<name>":"<value>[":"<vendor-product>":"<version>].
45
     *
46
     * @var array
47
     */
48
    private $columns = [
49
        'urn',
50
        'namespace',
51
        'type',
52
        'name',
53
        'value',
54
        'vendor_product',
55
        'version',
56
    ];
57
58
    /**
59
     * 各类型的预订义的基础urn.
60
     *
61
     * @var
62
     */
63
    private $baseUrn;
64
65
    /**
66
     * 预订义的小米 URN 规范所包含的字段
67
     * <URN> ::= "urn:"<namespace>":"<type>":"<name>":"<value>.
68
     *
69
     * @var array
70
     */
71
    private $baseColumns = [
72
        'urn',
73
        'namespace',
74
        'type',
75
        'name',
76
        'value',
77
    ];
78
79
    /**
80
     * 第一个字段必须为urn,否则视为非法urn.
81
     *
82
     * @var
83
     */
84
    private $urn = 'urn';
85
86
    /**
87
     * 如果是小米定义的规范为miot-spec
88
     * 蓝牙联盟定义的规范为bluetooth-spec.
89
     *
90
     * @var
91
     */
92
    private $namespace = 'miot-spec-v2';
93
94
    /**
95
     * 合法的namespace.
96
     *
97
     * @var array
98
     */
99
    private $validNamespaces = [
100
        'miot-spec',        // 小米定义的规范
101
        'miot-spec-v2',     // 小米定义的规范版本2
102
        'bluetooth-spec',   // 蓝牙联盟定义的规范
103
    ];
104
105
    /**
106
     * SpecificationType (类型,简写为: type)
107
     * 只能是如下几个:.
108
     *
109
     * property
110
     * action
111
     * event
112
     * service
113
     * device
114
     *
115
     * @var
116
     */
117
    private $type = 'property';
118
119
    /**
120
     * 合法的type.
121
     *
122
     * @var array
123
     */
124
    private $validTypes = [
125
        'property',     // 属性
126
        'action',       // 方法
127
        'event',        // 事件
128
        'service',      // 服务
129
        'device',       // 设备
130
    ];
131
132
    /**
133
     * 有意义的单词或单词组合(小写字母)
134
     * 多个单词用"-"间隔,比如:.
135
     *
136
     * temperature
137
     * current-temperature
138
     * device-name
139
     * battery-level
140
     *
141
     * @var
142
     */
143
    private $name;
144
145
    /**
146
     * name的正则
147
     * 单词或单词组合(小写字母)
148
     * 多个单词用"-"间隔.
149
     *
150
     * @var string
151
     */
152
    private $nameReg = '/^[a-z][a-z\-]*[a-z]$/';
153
154
    /**
155
     * 16进制字符串,使用UUID前8个字符,如:.
156
     *
157
     * 00002A06
158
     * 00002A00
159
     *
160
     * @var
161
     */
162
    private $value;
163
164
    /**
165
     * value正则
166
     * 16进制字符串,使用UUID前8个字符.
167
     *
168
     * @var string
169
     */
170
    private $valueReg = '/^([0-9A-F]{8})$/';
171
172
    /**
173
     * 厂家+产品代号 (这个字段只有在设备实例定义里出现)
174
     * 有意义的单词或单词组合(小写字母),用"-"间隔,比如:.
175
     *
176
     * philips-moonlight
177
     * yeelink-c300
178
     * zhimi-vv
179
     * benz-c63
180
     *
181
     * @var string
182
     */
183
    private $vendorProduct;
184
185
    /**
186
     * 厂家+产品代号正则
187
     * 单词或单词组合(小写字母)
188
     * 多个单词用"-"间隔.
189
     *
190
     * @var string
191
     */
192
    private $vendorProductReg = '/^([a-z0-9\-]+)$/';
193
194
    /**
195
     * 版本号,只能是数字 (这个字段只有在设备实例定义里出现)
196
     * 如: 1, 2, 3.
197
     *
198
     * @var
199
     */
200
    private $version;
201
202
    /**
203
     * 版本号正则
204
     * 只能是数字.
205
     *
206
     * @var string
207
     */
208
    private $version_reg = '/^([0-9]+)$/';
209
210
    /**
211
     * Urn constructor.
212
     *
213
     * @param $urn
214
     *
215
     * @throws SpecificationErrorException
216
     */
217
    public function __construct($urn)
218
    {
219
        if (!$this->validate($urn)) {
220
            throw new SpecificationErrorException('Invalid URN!');
221
        }
222
223
        $this->original = $urn;
224
225
        // 执行解析
226
        $this->_parse();
227
    }
228
229
    /**
230
     * Validate a URN according to RFC 2141.
231
     *
232
     * @param $urn
233
     *
234
     * @return true when the URN is valid, FALSE when invalid
235
     *
236
     * @internal param the $urn URN to validate
237
     */
238
    private function validate($urn)
239
    {
240
        return (bool) preg_match(self::URN_REGEXP, $urn);
0 ignored issues
show
Bug Best Practice introduced by
The expression return (bool)preg_match(self::URN_REGEXP, $urn) returns the type boolean which is incompatible with the documented return type true.
Loading history...
241
    }
242
243
    /**
244
     * @return mixed
245
     */
246
    public function getUrn()
247
    {
248
        return $this->urn;
249
    }
250
251
    /**
252
     * @param mixed $urn
253
     *
254
     * @throws SpecificationErrorException
255
     */
256
    public function setUrn($urn)
257
    {
258
        if ($urn != $this->urn) {
259
            throw new SpecificationErrorException('必须为urn,否则视为非法urn');
260
        }
261
262
        $this->urn = $urn;
263
    }
264
265
    /**
266
     * @return mixed
267
     */
268
    public function getNamespace()
269
    {
270
        return $this->namespace;
271
    }
272
273
    /**
274
     * @param mixed $namespace
275
     *
276
     * @throws SpecificationErrorException
277
     */
278
    public function setNamespace($namespace)
279
    {
280
        if (!in_array($namespace, $this->validNamespaces)) {
281
            throw new SpecificationErrorException('非法 namespace');
282
        }
283
284
        $this->namespace = $namespace;
285
    }
286
287
    /**
288
     * @return mixed
289
     */
290
    public function getType()
291
    {
292
        return $this->type;
293
    }
294
295
    /**
296
     * @param mixed $type
297
     *
298
     * @throws SpecificationErrorException
299
     */
300
    public function setType($type)
301
    {
302
        if (!in_array($type, $this->validTypes)) {
303
            throw new SpecificationErrorException('非法 type');
304
        }
305
306
        $this->type = $type;
307
    }
308
309
    /**
310
     * @return mixed
311
     */
312
    public function getName()
313
    {
314
        return $this->name;
315
    }
316
317
    /**
318
     * @param mixed $name
319
     *
320
     * @throws SpecificationErrorException
321
     */
322
    public function setName($name)
323
    {
324
        if (!preg_match($this->nameReg, $name)) {
325
            throw new SpecificationErrorException('非法 name');
326
        }
327
328
        $this->name = $name;
329
    }
330
331
    /**
332
     * @return mixed
333
     */
334
    public function getValue()
335
    {
336
        return $this->value;
337
    }
338
339
    /**
340
     * @param mixed $value
341
     *
342
     * @throws SpecificationErrorException
343
     */
344
    public function setValue($value)
345
    {
346
        if (!preg_match($this->valueReg, $value)) {
347
            throw new SpecificationErrorException('非法 value');
348
        }
349
350
        $this->value = $value;
351
    }
352
353
    /**
354
     * @return mixed
355
     */
356
    public function getVendorProduct()
357
    {
358
        return $this->vendorProduct;
359
    }
360
361
    /**
362
     * @param mixed $vendorProduct
363
     *
364
     * @throws SpecificationErrorException
365
     */
366
    public function setVendorProduct($vendorProduct)
367
    {
368
        if (!preg_match($this->vendorProductReg, $vendorProduct)) {
369
            throw new SpecificationErrorException('非法 厂家+产品代号');
370
        }
371
372
        $this->vendorProduct = $vendorProduct;
373
    }
374
375
    /**
376
     * @return mixed
377
     */
378
    public function getVersion()
379
    {
380
        return $this->version;
381
    }
382
383
    /**
384
     * @param mixed $version
385
     *
386
     * @throws SpecificationErrorException
387
     */
388
    public function setVersion($version)
389
    {
390
        if (!preg_match($this->version_reg, $version)) {
391
            throw new SpecificationErrorException('非法 版本号');
392
        }
393
394
        $this->version = $version;
395
    }
396
397
    /**
398
     * @return mixed
399
     */
400
    public function getBaseUrn()
401
    {
402
        return $this->baseUrn;
403
    }
404
405
    /**
406
     * @return string
407
     */
408
    private function setBaseUrn()
409
    {
410
        $baseUrn = '';
411
412
        foreach ($this->baseColumns as $column) {
413
            $fncName = 'get' . ucfirst(
414
                    preg_replace_callback('/_([a-zA-Z])/', function ($match) {
415
                        return strtoupper($match[1]);
416
                    }, $column)
417
                );
418
419
            if (method_exists($this, $fncName) && $this->{$fncName}()) {
420
                $baseUrn .= $this->delimiter . $this->{$fncName}();
421
            }
422
        }
423
424
        $this->baseUrn = trim($baseUrn, $this->delimiter);
425
426
        return $this->baseUrn;
427
    }
428
429
    /**
430
     * @return mixed
431
     */
432
    public function getExpression()
433
    {
434
        return $this->expression;
435
    }
436
437
    /**
438
     * 根据各字段生成解析格式化后的urn.
439
     *
440
     * @return string
441
     */
442
    private function setExpression()
443
    {
444
        $expression = '';
445
446
        foreach ($this->columns as $column) {
447
            $fncName = 'get' . ucfirst(
448
                    preg_replace_callback('/_([a-zA-Z])/', function ($match) {
449
                        return strtoupper($match[1]);
450
                    }, $column)
451
                );
452
453
            if (method_exists($this, $fncName) && $this->{$fncName}()) {
454
                $expression .= $this->delimiter . $this->{$fncName}();
455
            }
456
        }
457
458
        $this->expression = trim($expression, $this->delimiter);
459
460
        return $this->expression;
461
    }
462
463
    /**
464
     * urn 解析器.
465
     *
466
     * @return mixed
467
     */
468
    private function _parse()
469
    {
470
        $parses = explode($this->delimiter, $this->original);
471
472
        foreach ($this->columns as $index => $column) {
473
            $fncName = 'set' . ucfirst(
474
                    preg_replace_callback('/_([a-zA-Z])/', function ($match) {
475
                        return strtoupper($match[1]);
476
                    }, $column)
477
                );
478
            if (method_exists($this, $fncName) && isset($parses[$index])) {
479
                $this->{$fncName}($parses[$index]);
480
            }
481
        }
482
483
        $this->setExpression();
484
        $this->setBaseUrn();
485
486
        return $this->expression;
487
    }
488
}
489