Skip to content

Commit eb5dca8

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

File tree

2 files changed

+221
-2
lines changed

2 files changed

+221
-2
lines changed

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: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
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+
/** @var non-empty-string $headerLine */
53+
$headerLine = preg_replace('/[\x00-\x1F\x7F]/', '', $headerLine);
54+
/** @var list<non-empty-string> $parts */
55+
$parts = explode(';', $headerLine);
56+
[$name, $value] = explode('=', $parts[0], 2);
57+
$instance = new self();
58+
$instance->setName($name);
59+
$instance->setValue($value);
60+
unset($parts[0]);
61+
foreach ($parts as $part) {
62+
$part = trim($part);
63+
if ('secure' === strtolower($part)) {
64+
$instance->setSecure(true);
65+
continue;
66+
}
67+
if (str_starts_with(strtolower($part), 'path')) {
68+
$pathParts = explode('=', $part, 2);
69+
if (array_key_exists(1, $pathParts)) {
70+
$instance->setPath($pathParts[1]);
71+
}
72+
continue;
73+
}
74+
if (str_starts_with(strtolower($part), 'expires')) {
75+
$expiresParts = explode('=', $part, 2);
76+
if (array_key_exists(1, $expiresParts)) {
77+
try {
78+
$dateTime = DateTimeImmutable::createFromFormat(DateTimeInterface::RFC1123, $expiresParts[1]);
79+
if (false !== $dateTime) {
80+
$instance->setExpires($dateTime);
81+
}
82+
} catch (Throwable) {
83+
// Unknown datetime, do not set expiration time
84+
}
85+
}
86+
}
87+
if (str_starts_with(strtolower($part), 'domain')) {
88+
$domainParts = explode('=', $part, 2);
89+
if (array_key_exists(1, $domainParts)) {
90+
$instance->setDomain($domainParts[1]);
91+
}
92+
} else {
93+
$instance->setDomain($uri->getHost());
94+
}
95+
}
96+
return $instance;
97+
}
98+
99+
/**
100+
* @return string
101+
*/
102+
public function getDomain(): string
103+
{
104+
return $this->domain;
105+
}
106+
107+
/**
108+
* @param string $domain
109+
*
110+
* @return void
111+
*/
112+
public function setDomain(string $domain): void
113+
{
114+
$this->domain = $domain;
115+
}
116+
117+
/**
118+
* @return string
119+
*/
120+
public function getPath(): string
121+
{
122+
return $this->path;
123+
}
124+
125+
/**
126+
* @param string $path
127+
*
128+
* @return void
129+
*/
130+
public function setPath(string $path): void
131+
{
132+
$this->path = $path;
133+
}
134+
135+
/**
136+
* @return string
137+
*/
138+
public function getName(): string
139+
{
140+
return $this->name;
141+
}
142+
143+
/**
144+
* @param string $name
145+
*
146+
* @return void
147+
*/
148+
public function setName(string $name): void
149+
{
150+
$this->name = $name;
151+
}
152+
153+
/**
154+
* @return string
155+
*/
156+
public function getValue(): string
157+
{
158+
return $this->value;
159+
}
160+
161+
/**
162+
* @param string $value
163+
*
164+
* @return void
165+
*/
166+
public function setValue(string $value): void
167+
{
168+
$this->value = $value;
169+
}
170+
171+
/**
172+
* @return bool
173+
*/
174+
public function isSecure(): bool
175+
{
176+
return $this->secure;
177+
}
178+
179+
/**
180+
* @param bool $secure
181+
*
182+
* @return void
183+
*/
184+
public function setSecure(bool $secure): void
185+
{
186+
$this->secure = $secure;
187+
}
188+
189+
/**
190+
* @return DateTimeInterface|null
191+
*/
192+
public function getExpires(): ?DateTimeInterface
193+
{
194+
return $this->expires;
195+
}
196+
197+
/**
198+
* @param DateTimeInterface $expires
199+
*
200+
* @return void
201+
*/
202+
public function setExpires(DateTimeInterface $expires): void
203+
{
204+
$this->expires = $expires;
205+
}
206+
}

0 commit comments

Comments
 (0)