Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
89.19% covered (success)
89.19%
33 / 37
75.00% covered (success)
75.00%
9 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractRequest
89.19% covered (success)
89.19%
33 / 37
75.00% covered (success)
75.00%
9 / 12
24.73
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 setUri
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 getUri
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getUriAsString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasUri
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 clearUri
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getProtocolVersion
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 withProtocolVersion
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getRequestTarget
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
5.03
 withRequestTarget
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 __clone
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 withUri
87.50% covered (success)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
6.07
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <nick@popphp.org>
8 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Http;
16
17use Psr\Http\Message\RequestInterface;
18use Psr\Http\Message\UriInterface;
19
20/**
21 * Abstract HTTP request class
22 *
23 * @category   Pop
24 * @package    Pop\Http
25 * @author     Nick Sagona, III <nick@popphp.org>
26 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
27 * @license    https://www.popphp.org/license     New BSD License
28 * @version    6.0.0
29 */
30abstract class AbstractRequest extends AbstractRequestResponse implements RequestInterface
31{
32
33    /**
34     * Request URI object
35     * @var ?Uri
36     */
37    protected ?Uri $uri = null;
38
39    /**
40     * HTTP protocol version, per PSR-7
41     * @var string
42     */
43    protected string $protocolVersion = '1.1';
44
45    /**
46     * Explicit request-target override, per PSR-7 (null = derive from the URI)
47     * @var ?string
48     */
49    protected ?string $requestTargetOverride = null;
50
51    /**
52     * Constructor
53     *
54     * Instantiate the request object
55     *
56     * @param  Uri|string|null $uri
57     * @throws Exception
58     */
59    public function __construct(Uri|string|null $uri = null)
60    {
61        if ($uri !== null) {
62            $this->setUri($uri);
63        }
64    }
65
66    /**
67     * Set URI
68     *
69     * @param  Uri|string $uri
70     * @throws Exception
71     * @return AbstractRequest
72     */
73    public function setUri(Uri|string $uri): AbstractRequest
74    {
75        $this->uri = (is_string($uri)) ? new Uri($uri) : $uri;
76        return $this;
77    }
78
79    /**
80     * Get URI, per PSR-7 (a transient, never-stored empty Uri when none is set,
81     * so hasUri() remains unaffected by calling this)
82     *
83     * @return Uri
84     */
85    public function getUri(): Uri
86    {
87        return $this->uri ?? new Uri();
88    }
89
90    /**
91     * Get URI as string
92     *
93     * @return string
94     */
95    public function getUriAsString(): string
96    {
97        return (string)$this->uri?->render();
98    }
99
100    /**
101     * Has URI
102     *
103     * @return bool
104     */
105    public function hasUri(): bool
106    {
107        return ($this->uri !== null);
108    }
109
110    /**
111     * Clear URI
112     *
113     * @return AbstractRequest
114     */
115    public function clearUri(): AbstractRequest
116    {
117        $this->uri = null;
118        return $this;
119    }
120
121    /**
122     * Get the HTTP protocol version, per PSR-7
123     *
124     * @return string
125     */
126    public function getProtocolVersion(): string
127    {
128        return $this->protocolVersion;
129    }
130
131    /**
132     * Return an instance with the specified HTTP protocol version, per PSR-7
133     *
134     * @param  string $version
135     * @return static
136     */
137    public function withProtocolVersion(string $version): static
138    {
139        $clone                  = clone $this;
140        $clone->protocolVersion = $version;
141        return $clone;
142    }
143
144    /**
145     * Get the request target, per PSR-7 (path + query, or '/' with no URI, unless overridden)
146     *
147     * @return string
148     */
149    public function getRequestTarget(): string
150    {
151        if ($this->requestTargetOverride !== null) {
152            return $this->requestTargetOverride;
153        }
154
155        if (!$this->hasUri()) {
156            return '/';
157        }
158
159        $target = $this->uri->getPath();
160        if ($target === '') {
161            $target = '/';
162        }
163
164        if ($this->uri->hasQuery()) {
165            $target .= '?' . $this->uri->getQuery();
166        }
167
168        return $target;
169    }
170
171    /**
172     * Return an instance with the specified request target, per PSR-7
173     *
174     * @param  string $requestTarget
175     * @return static
176     */
177    public function withRequestTarget(string $requestTarget): static
178    {
179        $clone                        = clone $this;
180        $clone->requestTargetOverride = $requestTarget;
181        return $clone;
182    }
183
184    /**
185     * Deep-clone the URI so a with*() clone never shares a mutable Uri
186     * instance with the instance it was cloned from
187     *
188     * @return void
189     */
190    public function __clone(): void
191    {
192        parent::__clone();
193
194        if ($this->uri !== null) {
195            $this->uri = clone $this->uri;
196        }
197    }
198
199    /**
200     * Return an instance with the specified URI, per PSR-7
201     *
202     * @param  UriInterface $uri
203     * @param  bool         $preserveHost
204     * @return static
205     */
206    public function withUri(UriInterface $uri, bool $preserveHost = false): static
207    {
208        $clone      = clone $this;
209        $clone->uri = ($uri instanceof Uri) ? $uri : new Uri((string)$uri);
210
211        if ((!$preserveHost || !$clone->hasHeader('Host')) && $clone->uri->hasHost()) {
212            $host = $clone->uri->getHost();
213            if ($clone->uri->hasPort()) {
214                $host .= ':' . $clone->uri->getPort();
215            }
216            $clone = $clone->withHeader('Host', $host);
217        }
218
219        return $clone;
220    }
221
222}