Skip to content

Commit b0dcfaf

Browse files
committed
WIP: Cookies
1 parent 7f15008 commit b0dcfaf

2 files changed

Lines changed: 219 additions & 2 deletions

File tree

src/Client.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ class Client implements ClientInterface
2525
{
2626
private const string DEFAULT_USER_AGENT = 'PSR-18 Shark Client';
2727

28+
/**
29+
* @var list<Cookie>
30+
*/
31+
private array $cookies = [];
32+
2833
/**
2934
* @var CurlHandle
3035
*/
@@ -96,6 +101,10 @@ public function sendRequest(RequestInterface $request): ResponseInterface
96101
if (isset($this->curl)) {
97102
curl_close($this->curl);
98103
}
104+
$cookies = $response->getHeader('Set-Cookie');
105+
foreach ($cookies as $cookie) {
106+
$this->cookies[] = Cookie::createFromHeaderLine($cookie, $request->getUri());
107+
}
99108
return $response;
100109
}
101110

@@ -142,7 +151,11 @@ static function (CurlHandle $curl, string $header) use ($responseData): int {
142151
return $len;
143152
}
144153
$headerName = strtolower(trim($headerArray[0]));
145-
$responseData->headers[$headerName] = [trim($headerArray[1])];
154+
if (array_key_exists($headerName, $responseData->headers)) {
155+
$responseData->headers[$headerName][] = trim($headerArray[1]);
156+
} else {
157+
$responseData->headers[$headerName] = [trim($headerArray[1])];
158+
}
146159

147160
return $len;
148161
}
@@ -218,7 +231,7 @@ protected function getResponse(ResponseData $responseData): ResponseInterface
218231
$response = $this->responseFactory->createResponse($statusCode);
219232
$response = $response->withBody($this->streamFactory->createStreamFromResource($responseData->streamHandle));
220233
foreach ($responseData->headers as $headerName => $headerValue) {
221-
$response = $response->withHeader($headerName, $headerValue);
234+
$response = $response->withAddedHeader($headerName, $headerValue);
222235
}
223236
$response->getBody()->rewind();
224237
if (null !== $this->responseMutationHandlerCollection) {

src/Cookie.php

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SharkMachine\Psr18Shark;
6+
7+
use DateTimeImmutable;
8+
use DateTimeInterface;
9+
use Psr\Http\Message\UriInterface;
10+
use Throwable;
11+
12+
class Cookie
13+
{
14+
/**
15+
* @var string
16+
*/
17+
private string $domain;
18+
19+
/**
20+
* @var string
21+
*/
22+
private string $path = '/';
23+
24+
/**
25+
* @var string
26+
*/
27+
private string $name;
28+
29+
/**
30+
* @var string
31+
*/
32+
private string $value;
33+
34+
/**
35+
* @var bool
36+
*/
37+
private bool $secure = false;
38+
39+
/**
40+
* @var DateTimeInterface|null
41+
*/
42+
private ?DateTimeInterface $expires = null;
43+
44+
/**
45+
* @param non-empty-string $headerLine
46+
* @param UriInterface $uri
47+
*
48+
* @return self
49+
*/
50+
public static function createFromHeaderLine(string $headerLine, UriInterface $uri): self
51+
{
52+
$headerLine = preg_replace('/[\x00-\x1F\x7F]/', '', $headerLine);
53+
/** @var list<non-empty-string> $parts */
54+
$parts = array_filter(explode(';', $headerLine), 'trim');
55+
[$name, $value] = explode('=', $parts[0], 2);
56+
$instance = new self();
57+
$instance->setName($name);
58+
$instance->setValue($value);
59+
unset($parts[0]);
60+
foreach ($parts as $part) {
61+
if ('secure' === strtolower($part)) {
62+
$instance->setSecure(true);
63+
continue;
64+
}
65+
if (str_starts_with(strtolower($part), 'path')) {
66+
$pathParts = explode('=', $part, 2);
67+
if (array_key_exists(1, $pathParts)) {
68+
$instance->setPath($pathParts[1]);
69+
}
70+
continue;
71+
}
72+
if (str_starts_with(strtolower($part), 'expires')) {
73+
$expiresParts = explode('=', $part, 2);
74+
if (array_key_exists(1, $expiresParts)) {
75+
try {
76+
$dateTime = DateTimeImmutable::createFromFormat(DateTimeInterface::RFC1123, $expiresParts[1]);
77+
if (false !== $dateTime) {
78+
$instance->setExpires($dateTime);
79+
}
80+
} catch (Throwable) {
81+
// Unknown datetime, do not set expiration time
82+
}
83+
}
84+
}
85+
if (str_starts_with(strtolower($part), 'domain')) {
86+
$domainParts = explode('=', $part, 2);
87+
if (array_key_exists(1, $domainParts)) {
88+
$instance->setDomain($domainParts[1]);
89+
}
90+
} else {
91+
$instance->setDomain($uri->getHost());
92+
}
93+
}
94+
return $instance;
95+
}
96+
97+
/**
98+
* @return string
99+
*/
100+
public function getDomain(): string
101+
{
102+
return $this->domain;
103+
}
104+
105+
/**
106+
* @param string $domain
107+
*
108+
* @return void
109+
*/
110+
public function setDomain(string $domain): void
111+
{
112+
$this->domain = $domain;
113+
}
114+
115+
/**
116+
* @return string
117+
*/
118+
public function getPath(): string
119+
{
120+
return $this->path;
121+
}
122+
123+
/**
124+
* @param string $path
125+
*
126+
* @return void
127+
*/
128+
public function setPath(string $path): void
129+
{
130+
$this->path = $path;
131+
}
132+
133+
/**
134+
* @return string
135+
*/
136+
public function getName(): string
137+
{
138+
return $this->name;
139+
}
140+
141+
/**
142+
* @param string $name
143+
*
144+
* @return void
145+
*/
146+
public function setName(string $name): void
147+
{
148+
$this->name = $name;
149+
}
150+
151+
/**
152+
* @return string
153+
*/
154+
public function getValue(): string
155+
{
156+
return $this->value;
157+
}
158+
159+
/**
160+
* @param string $value
161+
*
162+
* @return void
163+
*/
164+
public function setValue(string $value): void
165+
{
166+
$this->value = $value;
167+
}
168+
169+
/**
170+
* @return bool
171+
*/
172+
public function isSecure(): bool
173+
{
174+
return $this->secure;
175+
}
176+
177+
/**
178+
* @param bool $secure
179+
*
180+
* @return void
181+
*/
182+
public function setSecure(bool $secure): void
183+
{
184+
$this->secure = $secure;
185+
}
186+
187+
/**
188+
* @return DateTimeInterface|null
189+
*/
190+
public function getExpires(): ?DateTimeInterface
191+
{
192+
return $this->expires;
193+
}
194+
195+
/**
196+
* @param DateTimeInterface $expires
197+
*
198+
* @return void
199+
*/
200+
public function setExpires(DateTimeInterface $expires): void
201+
{
202+
$this->expires = $expires;
203+
}
204+
}

0 commit comments

Comments
 (0)