1<?php
2
3namespace Sabre\HTTP;
4
5use InvalidArgumentException;
6use Sabre\Uri;
7
8/**
9 * The Request class represents a single HTTP request.
10 *
11 * You can either simply construct the object from scratch, or if you need
12 * access to the current HTTP request, use Sapi::getRequest.
13 *
14 * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
15 * @author Evert Pot (http://evertpot.com/)
16 * @license http://sabre.io/license/ Modified BSD License
17 */
18class Request extends Message implements RequestInterface {
19
20    /**
21     * HTTP Method
22     *
23     * @var string
24     */
25    protected $method;
26
27    /**
28     * Request Url
29     *
30     * @var string
31     */
32    protected $url;
33
34    /**
35     * Creates the request object
36     *
37     * @param string $method
38     * @param string $url
39     * @param array $headers
40     * @param resource $body
41     */
42    function __construct($method = null, $url = null, array $headers = null, $body = null) {
43
44        if (is_array($method)) {
45            throw new InvalidArgumentException('The first argument for this constructor should be a string or null, not an array. Did you upgrade from sabre/http 1.0 to 2.0?');
46        }
47        if (!is_null($method))      $this->setMethod($method);
48        if (!is_null($url))         $this->setUrl($url);
49        if (!is_null($headers))     $this->setHeaders($headers);
50        if (!is_null($body))        $this->setBody($body);
51
52    }
53
54    /**
55     * Returns the current HTTP method
56     *
57     * @return string
58     */
59    function getMethod() {
60
61        return $this->method;
62
63    }
64
65    /**
66     * Sets the HTTP method
67     *
68     * @param string $method
69     * @return void
70     */
71    function setMethod($method) {
72
73        $this->method = $method;
74
75    }
76
77    /**
78     * Returns the request url.
79     *
80     * @return string
81     */
82    function getUrl() {
83
84        return $this->url;
85
86    }
87
88    /**
89     * Sets the request url.
90     *
91     * @param string $url
92     * @return void
93     */
94    function setUrl($url) {
95
96        $this->url = $url;
97
98    }
99
100    /**
101     * Returns the list of query parameters.
102     *
103     * This is equivalent to PHP's $_GET superglobal.
104     *
105     * @return array
106     */
107    function getQueryParameters() {
108
109        $url = $this->getUrl();
110        if (($index = strpos($url, '?')) === false) {
111            return [];
112        } else {
113            parse_str(substr($url, $index + 1), $queryParams);
114            return $queryParams;
115        }
116
117    }
118
119    /**
120     * Sets the absolute url.
121     *
122     * @param string $url
123     * @return void
124     */
125    function setAbsoluteUrl($url) {
126
127        $this->absoluteUrl = $url;
128
129    }
130
131    /**
132     * Returns the absolute url.
133     *
134     * @return string
135     */
136    function getAbsoluteUrl() {
137
138        return $this->absoluteUrl;
139
140    }
141
142    /**
143     * Base url
144     *
145     * @var string
146     */
147    protected $baseUrl = '/';
148
149    /**
150     * Sets a base url.
151     *
152     * This url is used for relative path calculations.
153     *
154     * @param string $url
155     * @return void
156     */
157    function setBaseUrl($url) {
158
159        $this->baseUrl = $url;
160
161    }
162
163    /**
164     * Returns the current base url.
165     *
166     * @return string
167     */
168    function getBaseUrl() {
169
170        return $this->baseUrl;
171
172    }
173
174    /**
175     * Returns the relative path.
176     *
177     * This is being calculated using the base url. This path will not start
178     * with a slash, so it will always return something like
179     * 'example/path.html'.
180     *
181     * If the full path is equal to the base url, this method will return an
182     * empty string.
183     *
184     * This method will also urldecode the path, and if the url was incoded as
185     * ISO-8859-1, it will convert it to UTF-8.
186     *
187     * If the path is outside of the base url, a LogicException will be thrown.
188     *
189     * @return string
190     */
191    function getPath() {
192
193        // Removing duplicated slashes.
194        $uri = str_replace('//', '/', $this->getUrl());
195
196        $uri = Uri\normalize($uri);
197        $baseUri = Uri\normalize($this->getBaseUrl());
198
199        if (strpos($uri, $baseUri) === 0) {
200
201            // We're not interested in the query part (everything after the ?).
202            list($uri) = explode('?', $uri);
203            return trim(URLUtil::decodePath(substr($uri, strlen($baseUri))), '/');
204
205        }
206        // A special case, if the baseUri was accessed without a trailing
207        // slash, we'll accept it as well.
208        elseif ($uri . '/' === $baseUri) {
209
210            return '';
211
212        }
213
214        throw new \LogicException('Requested uri (' . $this->getUrl() . ') is out of base uri (' . $this->getBaseUrl() . ')');
215    }
216
217    /**
218     * Equivalent of PHP's $_POST.
219     *
220     * @var array
221     */
222    protected $postData = [];
223
224    /**
225     * Sets the post data.
226     *
227     * This is equivalent to PHP's $_POST superglobal.
228     *
229     * This would not have been needed, if POST data was accessible as
230     * php://input, but unfortunately we need to special case it.
231     *
232     * @param array $postData
233     * @return void
234     */
235    function setPostData(array $postData) {
236
237        $this->postData = $postData;
238
239    }
240
241    /**
242     * Returns the POST data.
243     *
244     * This is equivalent to PHP's $_POST superglobal.
245     *
246     * @return array
247     */
248    function getPostData() {
249
250        return $this->postData;
251
252    }
253
254    /**
255     * An array containing the raw _SERVER array.
256     *
257     * @var array
258     */
259    protected $rawServerData;
260
261    /**
262     * Returns an item from the _SERVER array.
263     *
264     * If the value does not exist in the array, null is returned.
265     *
266     * @param string $valueName
267     * @return string|null
268     */
269    function getRawServerValue($valueName) {
270
271        if (isset($this->rawServerData[$valueName])) {
272            return $this->rawServerData[$valueName];
273        }
274
275    }
276
277    /**
278     * Sets the _SERVER array.
279     *
280     * @param array $data
281     * @return void
282     */
283    function setRawServerData(array $data) {
284
285        $this->rawServerData = $data;
286
287    }
288
289    /**
290     * Serializes the request object as a string.
291     *
292     * This is useful for debugging purposes.
293     *
294     * @return string
295     */
296    function __toString() {
297
298        $out = $this->getMethod() . ' ' . $this->getUrl() . ' HTTP/' . $this->getHTTPVersion() . "\r\n";
299
300        foreach ($this->getHeaders() as $key => $value) {
301            foreach ($value as $v) {
302                if ($key === 'Authorization') {
303                    list($v) = explode(' ', $v, 2);
304                    $v .= ' REDACTED';
305                }
306                $out .= $key . ": " . $v . "\r\n";
307            }
308        }
309        $out .= "\r\n";
310        $out .= $this->getBodyAsString();
311
312        return $out;
313
314    }
315
316}
317