Passed
Push — master ( 516163...07b934 )
by 世昌
02:15
created

MatcherHelper::buildUri()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 15
nc 2
nop 3
dl 0
loc 25
rs 9.4555
c 0
b 0
f 0
1
<?php
2
namespace nebula\component\route\uri;
3
4
use Exception;
5
use nebula\component\route\uri\UriMatcher;
6
use nebula\component\route\uri\parameter\IntParameter;
7
use nebula\component\route\uri\parameter\UrlParameter;
8
use nebula\component\route\uri\parameter\FloatParameter;
9
use nebula\component\route\uri\parameter\StringParameter;
10
11
/**
12
 * 可执行命令表达式
13
 *
14
 */
15
class MatcherHelper
16
{
17
    protected static $parameters = [
18
        'float' => FloatParameter::class,
19
        'int' => IntParameter::class,
20
        'string' => StringParameter::class,
21
        'url' => UrlParameter::class,
22
    ];
23
24
    public static function build(string $uri):UriMatcher
25
    {
26
        // 参数
27
        $parameters = [];
28
        // 转义正则
29
        $url=preg_replace('/([\/\.\\\\\+\(\^\)\$\!\<\>\-\?\*])/', '\\\\$1', $uri);
30
        // 添加忽略
31
        $url=preg_replace('/(\[)([^\[\]]+)(?(1)\])/', '(?:$2)?', $url);
32
        // 添加 * ? 匹配
33
        $url=str_replace(['\*','\?'], ['[^/]*?','[^/]'], $url);
34
        // 编译页面参数
35
        $url=preg_replace_callback('/\{(\w+)(?:\:([^}]+?))?\}/', function ($match) use (&$parameters) {
36
            $name = $match[1];
37
            $type = 'string';
38
            $extra = '';
39
            if (isset($match[2])) {
40
                if (strpos($match[2], '=')!==false) {
41
                    list($type, $extra) = \explode('=', $match[2]);
42
                } else {
43
                    $type = $match[2];
44
                }
45
            }
46
            if (!\in_array($type, array_keys(static::$parameters))) {
47
                throw new Exception(sprintf('unknown parameter type %s', $type), 1);
48
            }
49
            $parameter = static::$parameters[$type]::build($name, $extra);
50
            $parameters[] = $parameter;
51
            return $parameter->getMatch();
52
        }, $url);
53
54
        return new UriMatcher($uri, $url, $parameters);
55
    }
56
57
    public static function buildUri(UriMatcher $matcher, array $parameter, bool $allowQuery = true):string
58
    {
59
        $uri =  $matcher->getUri();
60
        // 拆分参数
61
        list($mapper, $query) = static::analyseParameter($matcher, $parameter);
62
        // for * ?
63
        $url=\str_replace(['*','?'], ['','-'], $uri);
64
        // for ignore value
65
        $url=preg_replace_callback('/\[(.+?)\]/', function ($match) use ($matcher, $parameter, $mapper) {
66
            if (preg_match('/\{(\w+).+?\}/', $match[1])) {
67
                $count = 0;
68
                $subUrl= static::replaceParameter($match[1], $matcher, $parameter, $mapper, $count);
69
                if ($count > 0) {
70
                    return $subUrl;
71
                }
72
            }
73
            return '';
74
        }, $url);
75
 
76
        $url= static::replaceParameter($url, $matcher, $parameter, $mapper);
77
78
        if (count($query) && $allowQuery) {
79
            return $url.'?'.http_build_query($query, 'v', '&', PHP_QUERY_RFC3986);
80
        }
81
        return $url;
82
    }
83
84
    protected static function analyseParameter(UriMatcher $matcher, array $parameter):array
85
    {
86
        $query = [];
87
        $mapper = [];
88
89
        foreach ($parameter as $key => $value) {
90
            $mp = $matcher->getParameter($key);
91
            // 多余参数
92
            if (is_null($mp)) {
93
                $query[$key] = $value;
94
            }
95
            $mapper[$key] = $mp;
96
        }
97
        return [$mapper, $query];
98
    }
99
100
    protected static function replaceParameter(string $input, UriMatcher $matcher, array $parameter, array $mapper, ?int &$count=null)
101
    {
102
        return preg_replace_callback('/\{(\w+).+?\}/', function ($match) use ($matcher, $parameter, $mapper) {
103
            if (\array_key_exists($match[1], $mapper)) {
104
                return $mapper[$match[1]]->packValue($parameter[$match[1]]);
105
            }
106
            if ($default = $matcher->getParameter($match[1])) {
107
                return $default->getDefaultValue();
108
            }
109
            throw new Exception(sprintf('unknown parameter %s in %s', $match[1], $matcher->getUri()), 1);
110
        }, $input, -1, $count);
111
    }
112
}
113