Urn::setBaseUrn()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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