Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
51 / 51
100.00% covered (success)
100.00%
11 / 11
CRAP
100.00% covered (success)
100.00%
1 / 1
Request
100.00% covered (success)
100.00%
51 / 51
100.00% covered (success)
100.00%
11 / 11
24
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
5
 getRequestTarget
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 withRequestTarget
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getMethod
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 withMethod
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getUri
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 withUri
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 updateHostFromUri
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
4
 setRequestTarget
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 setMethod
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setUri
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace Tal;
4
5use GuzzleHttp\Psr7\Utils;
6use function GuzzleHttp\Psr7\stream_for;
7use GuzzleHttp\Psr7\Uri;
8use InvalidArgumentException;
9use Psr\Http\Message\RequestInterface;
10use Psr\Http\Message\StreamInterface;
11use Psr\Http\Message\UriInterface;
12
13/**
14 * PSR-7 request implementation.
15 */
16abstract class Request implements RequestInterface
17{
18    use MessageTrait;
19
20    /** @var string */
21    protected $method;
22
23    /** @var null|string */
24    protected $requestTarget;
25
26    /** @var UriInterface */
27    protected $uri;
28
29    /**
30     * @param string                               $method  HTTP method
31     * @param string|UriInterface                  $uri     URI
32     * @param array                                $headers Request headers
33     * @param string|null|resource|StreamInterface $body    Request body
34     * @param string                               $version Protocol version
35     */
36    public function __construct(
37        $method,
38        $uri,
39        array $headers = [],
40        $body = null,
41        $version = '1.1'
42    ) {
43        if (!($uri instanceof UriInterface)) {
44            $uri = new Uri($uri);
45        }
46
47        $this->method = strtoupper($method);
48        $this->uri = $uri;
49        $this->setHeaders($headers);
50        $this->protocol = $version;
51
52        if (!$this->hasHeader('Host')) {
53            $this->updateHostFromUri();
54        }
55
56        if ($body !== '' && $body !== null) {
57            $this->stream = Utils::streamFor($body);
58        }
59    }
60
61    public function getRequestTarget(): string
62    {
63        if ($this->requestTarget !== null) {
64            return $this->requestTarget;
65        }
66
67        $target = $this->uri->getPath();
68        if ($target == '') {
69            $target = '/';
70        }
71        if ($this->uri->getQuery() != '') {
72            $target .= '?' . $this->uri->getQuery();
73        }
74
75        return $target;
76    }
77
78    public function withRequestTarget(string $requestTarget): RequestInterface
79    {
80        $new = clone $this;
81        return $new->setRequestTarget($requestTarget);
82    }
83
84    public function getMethod(): string
85    {
86        return $this->method;
87    }
88
89    public function withMethod(string $method): RequestInterface
90    {
91        $new = clone $this;
92        return $new->setMethod($method);
93    }
94
95    public function getUri(): UriInterface
96    {
97        return $this->uri;
98    }
99
100    public function withUri(UriInterface $uri, bool $preserveHost = false): RequestInterface
101    {
102        if ($uri === $this->uri) {
103            return $this;
104        }
105
106        $new = clone $this;
107        return $new->setUri($uri, $preserveHost);
108    }
109
110    protected function updateHostFromUri()
111    {
112        $host = $this->uri->getHost();
113
114        if ($host == '') {
115            return;
116        }
117
118        $port = $this->uri->getPort();
119        if ($port !== null) {
120            $host .= ':' . $port;
121        }
122
123        if (isset($this->headerNames['host'])) {
124            $header = $this->headerNames['host'];
125        } else {
126            $header = 'Host';
127            $this->headerNames['host'] = 'Host';
128        }
129        // Ensure Host is the first header.
130        // See: http://tools.ietf.org/html/rfc7230#section-5.4
131        $this->headers = [$header => [$host]] + $this->headers;
132    }
133
134    /**
135     * Sets the specific request-target.
136     *
137     * If the request needs a non-origin-form request-target — e.g., for
138     * specifying an absolute-form, authority-form, or asterisk-form —
139     * this method may be used to create an instance with the specified
140     * request-target, verbatim.
141     *
142     * @link http://tools.ietf.org/html/rfc7230#section-5.3 (for the various
143     *     request-target forms allowed in request messages)
144     * @param mixed $requestTarget
145     * @return static
146     */
147    protected function setRequestTarget($requestTarget)
148    {
149        if (preg_match('#\s#', $requestTarget)) {
150            throw new InvalidArgumentException(
151                'Invalid request target provided; cannot contain whitespace'
152            );
153        }
154
155        $this->requestTarget = $requestTarget;
156        return $this;
157    }
158
159    /**
160     * Sets the provided HTTP method.
161     *
162     * While HTTP method names are typically all uppercase characters, HTTP
163     * method names are case-sensitive and thus implementations SHOULD NOT
164     * modify the given string.
165     *
166     * This method MUST be implemented in such a way as to retain the
167     * immutability of the message, and MUST return an instance that has the
168     * changed request method.
169     *
170     * @param string $method Case-sensitive method.
171     * @return static
172     * @throws \InvalidArgumentException for invalid HTTP methods.
173     */
174    protected function setMethod($method)
175    {
176        $this->method = strtoupper($method);
177        return $this;
178    }
179
180    /**
181     * Sets the provided URI.
182     *
183     * This method MUST update the Host header of the returned request by
184     * default if the URI contains a host component. If the URI does not
185     * contain a host component, any pre-existing Host header MUST be carried
186     * over to the returned request.
187     *
188     * You can opt-in to preserving the original state of the Host header by
189     * setting `$preserveHost` to `true`. When `$preserveHost` is set to
190     * `true`, this method interacts with the Host header in the following ways:
191     *
192     * - If the Host header is missing or empty, and the new URI contains
193     *   a host component, this method MUST update the Host header in the returned
194     *   request.
195     * - If the Host header is missing or empty, and the new URI does not contain a
196     *   host component, this method MUST NOT update the Host header in the returned
197     *   request.
198     * - If a Host header is present and non-empty, this method MUST NOT update
199     *   the Host header in the returned request.
200     *
201     * @link http://tools.ietf.org/html/rfc3986#section-4.3
202     * @param UriInterface $uri New request URI to use.
203     * @param bool $preserveHost Preserve the original state of the Host header.
204     * @return static
205     */
206    protected function setUri(UriInterface $uri, $preserveHost = false)
207    {
208        $this->uri = $uri;
209
210        if (!$preserveHost) {
211            $this->updateHostFromUri();
212        }
213        return $this;
214    }
215}