Completed
Push — master ( cc3500...7c787d )
by Taosikai
14:10
created

Generator::getRouteScheme()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
c 0
b 0
f 0
rs 9.6666
cc 3
eloc 6
nc 2
nop 1
1
<?php
2
/**
3
 * slince routing library
4
 * @author Tao <[email protected]>
5
 */
6
namespace Slince\Routing;
7
8
use Psr\Http\Message\ServerRequestInterface;
9
use Slince\Routing\Exception\InvalidArgumentException;
10
11
class Generator
12
{
13
    /**
14
     * The request context
15
     * @var ServerRequestInterface
16
     */
17
    protected $request;
18
19
    /**
20
     * The variable
21
     * @var array
22
     */
23
    protected $lastRouteVariables = [];
24
25
    public function __construct(ServerRequestInterface $request = null)
26
    {
27
        $this->request = $request;
28
    }
29
30
    /**
31
     * Sets the request context
32
     * @param ServerRequestInterface $request
33
     */
34
    public function setRequest(ServerRequestInterface $request)
35
    {
36
        $this->request = $request;
37
    }
38
39
    /**
40
     * Gets the request context
41
     * @return ServerRequestInterface $context
42
     */
43
    public function getRequest()
44
    {
45
        return $this->request;
46
    }
47
48
    /**
49
     * Generates the url for the route
50
     * @param Route $route
51
     * @param array $parameters
52
     * @param boolean $absolute
53
     * @return string
54
     */
55
    public function generate(Route $route, $parameters = [], $absolute = false)
56
    {
57
        if ($absolute && !$this->request) {
58
            throw new InvalidArgumentException("You must provide the request context to generate the full url");
59
        }
60
        $urlSlugs = [];
61
        //generate absolute url
62
        if ($absolute) {
63
            $urlSlugs[] = $this->getRouteSchemeAndHost($route);
64
        }
65
        $urlSlugs[] = $this->getRoutePath($route, $parameters);
66
        // Build query string
67
        $extraParameters = array_diff_key($parameters, array_flip($this->lastRouteVariables));
68
        if (!empty($extraParameters) && $query = http_build_query($extraParameters, '', '&')) {
69
            $urlSlugs[] =  '?' . $query;
70
        }
71
        return implode('', $urlSlugs);
72
    }
73
74
    /**
75
     * 获取route的scheme和port
76
     * @param Route $route
77
     * @param array $parameters
78
     * @return string
79
     */
80
    protected function getRouteSchemeAndHost(Route $route, array $parameters = [])
81
    {
82
        $scheme = $this->request->getUri()->getScheme();
83
        $requiredSchemes = $route->getSchemes();
84
        if (!empty($requiredSchemes) && !in_array($scheme, $requiredSchemes)) {
85
            $scheme = $requiredSchemes[0];
86
        }
87
        if (!$route->getHost()) {
88
            $host = $this->request->getUri()->getHost();
89
            if ($port = $this->request->getUri()->getPort()) {
90
                if (strcasecmp($scheme, 'http') == 0 && $port != 80) {
91
                    $host .= ':' . $this->request->getUri()->getPort();
92
                } elseif (strcasecmp($scheme, 'https') == 0 && $port != 443) {
93
                    $host .= ':' . $this->request->getUri()->getPort();
94
                }
95
            }
96
        } else {
97
            $host = $this->replaceRouteNamedParameters($route, $route->getHost(), $parameters);
98
        }
99
        return $scheme . '://' . $host;
100
    }
101
102
    /**
103
     * Gets the route path
104
     * @param Route $route
105
     * @param array $parameters
106
     * @return string
107
     */
108
    protected function getRoutePath(Route $route, $parameters)
109
    {
110
        return $this->replaceRouteNamedParameters($route, $route->getPath(), $parameters);
111
    }
112
113
    /**
114
     * Replaces the named parameters of the route path or host
115
     * @param Route $route
116
     * @param string $path
117
     * @param array $parameters
118
     * @throws InvalidArgumentException
119
     * @return string
120
     */
121
    protected function replaceRouteNamedParameters(Route $route, $path, $parameters)
122
    {
123
        return preg_replace_callback('#[/\.]?\{([a-zA-Z0-9_,]*)\}#', function ($match) use ($route, $parameters) {
124
            $this->lastRouteVariables[] = $match[1];
125
            //The named parameter value must be provided if the route has not the default value fot it
126
            if ((!isset($parameters[$match[1]]) || is_null($parameters[$match[1]])) && !$route->hasDefault($match[1])) {
127
                throw new InvalidArgumentException(sprintf('Missing parameter "%s"', $match[1]));
128
            }
129
            $providedValue = isset($parameters[$match[1]]) && !is_null($parameters[$match[1]]) ?
130
                $parameters[$match[1]] : $route->getDefault($match[1]);
131
132
            if ($route->hasRequirement($match[1])
133
                && !preg_match('#^' . $route->getRequirement($match[1]) . '$#', $providedValue)
134
            ){
135
                $message = sprintf('Parameter "%s" must match "%s" ("%s" given) to generate a corresponding URL.',
136
                    $match[1], $route->getRequirement($match[1]), $providedValue);
137
                throw new InvalidArgumentException($message);
138
            }
139
            if ($route->getDefault($match[1]) === $providedValue) {
140
                return '';
141
            }
142
            return str_replace('{' . $match[1] . '}', $providedValue, $match[0]);
143
        }, $path);
144
    }
145
}