source: vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php

Last change on this file was e3d4e0a, checked in by Vlado 222039 <vlado.popovski@…>, 7 days ago

Upload project files

  • Property mode set to 100644
File size: 9.3 KB
Line 
1<?php
2
3namespace GuzzleHttp\Cookie;
4
5use Psr\Http\Message\RequestInterface;
6use Psr\Http\Message\ResponseInterface;
7
8/**
9 * Cookie jar that stores cookies as an array
10 */
11class CookieJar implements CookieJarInterface
12{
13 /**
14 * @var SetCookie[] Loaded cookie data
15 */
16 private $cookies = [];
17
18 /**
19 * @var bool
20 */
21 private $strictMode;
22
23 /**
24 * @param bool $strictMode Set to true to throw exceptions when invalid
25 * cookies are added to the cookie jar.
26 * @param array $cookieArray Array of SetCookie objects or a hash of
27 * arrays that can be used with the SetCookie
28 * constructor
29 */
30 public function __construct(bool $strictMode = false, array $cookieArray = [])
31 {
32 $this->strictMode = $strictMode;
33
34 foreach ($cookieArray as $cookie) {
35 if (!($cookie instanceof SetCookie)) {
36 $cookie = new SetCookie($cookie);
37 }
38 $this->setCookie($cookie);
39 }
40 }
41
42 /**
43 * Create a new Cookie jar from an associative array and domain.
44 *
45 * @param array $cookies Cookies to create the jar from
46 * @param string $domain Domain to set the cookies to
47 */
48 public static function fromArray(array $cookies, string $domain): self
49 {
50 $cookieJar = new self();
51 foreach ($cookies as $name => $value) {
52 $cookieJar->setCookie(new SetCookie([
53 'Domain' => $domain,
54 'Name' => $name,
55 'Value' => $value,
56 'Discard' => true,
57 ]));
58 }
59
60 return $cookieJar;
61 }
62
63 /**
64 * Evaluate if this cookie should be persisted to storage
65 * that survives between requests.
66 *
67 * @param SetCookie $cookie Being evaluated.
68 * @param bool $allowSessionCookies If we should persist session cookies
69 */
70 public static function shouldPersist(SetCookie $cookie, bool $allowSessionCookies = false): bool
71 {
72 if ($cookie->getExpires() || $allowSessionCookies) {
73 if (!$cookie->getDiscard()) {
74 return true;
75 }
76 }
77
78 return false;
79 }
80
81 /**
82 * Finds and returns the cookie based on the name
83 *
84 * @param string $name cookie name to search for
85 *
86 * @return SetCookie|null cookie that was found or null if not found
87 */
88 public function getCookieByName(string $name): ?SetCookie
89 {
90 foreach ($this->cookies as $cookie) {
91 if ($cookie->getName() !== null && \strcasecmp($cookie->getName(), $name) === 0) {
92 return $cookie;
93 }
94 }
95
96 return null;
97 }
98
99 public function toArray(): array
100 {
101 return \array_map(static function (SetCookie $cookie): array {
102 return $cookie->toArray();
103 }, $this->getIterator()->getArrayCopy());
104 }
105
106 public function clear(?string $domain = null, ?string $path = null, ?string $name = null): void
107 {
108 if (!$domain) {
109 $this->cookies = [];
110
111 return;
112 } elseif (!$path) {
113 $this->cookies = \array_filter(
114 $this->cookies,
115 static function (SetCookie $cookie) use ($domain): bool {
116 return !$cookie->matchesDomain($domain);
117 }
118 );
119 } elseif (!$name) {
120 $this->cookies = \array_filter(
121 $this->cookies,
122 static function (SetCookie $cookie) use ($path, $domain): bool {
123 return !($cookie->matchesPath($path)
124 && $cookie->matchesDomain($domain));
125 }
126 );
127 } else {
128 $this->cookies = \array_filter(
129 $this->cookies,
130 static function (SetCookie $cookie) use ($path, $domain, $name) {
131 return !($cookie->getName() == $name
132 && $cookie->matchesPath($path)
133 && $cookie->matchesDomain($domain));
134 }
135 );
136 }
137 }
138
139 public function clearSessionCookies(): void
140 {
141 $this->cookies = \array_filter(
142 $this->cookies,
143 static function (SetCookie $cookie): bool {
144 return !$cookie->getDiscard() && $cookie->getExpires();
145 }
146 );
147 }
148
149 public function setCookie(SetCookie $cookie): bool
150 {
151 // If the name string is empty (but not 0), ignore the set-cookie
152 // string entirely.
153 $name = $cookie->getName();
154 if (!$name && $name !== '0') {
155 return false;
156 }
157
158 // Only allow cookies with set and valid domain, name, value
159 $result = $cookie->validate();
160 if ($result !== true) {
161 if ($this->strictMode) {
162 throw new \RuntimeException('Invalid cookie: '.$result);
163 }
164 $this->removeCookieIfEmpty($cookie);
165
166 return false;
167 }
168
169 // Resolve conflicts with previously set cookies
170 foreach ($this->cookies as $i => $c) {
171 // Two cookies are identical, when their path, and domain are
172 // identical.
173 if ($c->getPath() != $cookie->getPath()
174 || $c->getDomain() != $cookie->getDomain()
175 || $c->getName() != $cookie->getName()
176 ) {
177 continue;
178 }
179
180 // The previously set cookie is a discard cookie and this one is
181 // not so allow the new cookie to be set
182 if (!$cookie->getDiscard() && $c->getDiscard()) {
183 unset($this->cookies[$i]);
184 continue;
185 }
186
187 // If the new cookie's expiration is further into the future, then
188 // replace the old cookie
189 if ($cookie->getExpires() > $c->getExpires()) {
190 unset($this->cookies[$i]);
191 continue;
192 }
193
194 // If the value has changed, we better change it
195 if ($cookie->getValue() !== $c->getValue()) {
196 unset($this->cookies[$i]);
197 continue;
198 }
199
200 // The cookie exists, so no need to continue
201 return false;
202 }
203
204 $this->cookies[] = $cookie;
205
206 return true;
207 }
208
209 public function count(): int
210 {
211 return \count($this->cookies);
212 }
213
214 /**
215 * @return \ArrayIterator<int, SetCookie>
216 */
217 public function getIterator(): \ArrayIterator
218 {
219 return new \ArrayIterator(\array_values($this->cookies));
220 }
221
222 public function extractCookies(RequestInterface $request, ResponseInterface $response): void
223 {
224 if ($cookieHeader = $response->getHeader('Set-Cookie')) {
225 foreach ($cookieHeader as $cookie) {
226 $sc = SetCookie::fromString($cookie);
227 if (!$sc->getDomain()) {
228 $sc->setDomain($request->getUri()->getHost());
229 }
230 if (0 !== \strpos($sc->getPath(), '/')) {
231 $sc->setPath($this->getCookiePathFromRequest($request));
232 }
233 if (!$sc->matchesDomain($request->getUri()->getHost())) {
234 continue;
235 }
236 // Note: At this point `$sc->getDomain()` being a public suffix should
237 // be rejected, but we don't want to pull in the full PSL dependency.
238 $this->setCookie($sc);
239 }
240 }
241 }
242
243 /**
244 * Computes cookie path following RFC 6265 section 5.1.4
245 *
246 * @see https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.4
247 */
248 private function getCookiePathFromRequest(RequestInterface $request): string
249 {
250 $uriPath = $request->getUri()->getPath();
251 if ('' === $uriPath) {
252 return '/';
253 }
254 if (0 !== \strpos($uriPath, '/')) {
255 return '/';
256 }
257 if ('/' === $uriPath) {
258 return '/';
259 }
260 $lastSlashPos = \strrpos($uriPath, '/');
261 if (0 === $lastSlashPos || false === $lastSlashPos) {
262 return '/';
263 }
264
265 return \substr($uriPath, 0, $lastSlashPos);
266 }
267
268 public function withCookieHeader(RequestInterface $request): RequestInterface
269 {
270 $values = [];
271 $uri = $request->getUri();
272 $scheme = $uri->getScheme();
273 $host = $uri->getHost();
274 $path = $uri->getPath() ?: '/';
275
276 foreach ($this->cookies as $cookie) {
277 if ($cookie->matchesPath($path)
278 && $cookie->matchesDomain($host)
279 && !$cookie->isExpired()
280 && (!$cookie->getSecure() || $scheme === 'https')
281 ) {
282 $values[] = $cookie->getName().'='
283 .$cookie->getValue();
284 }
285 }
286
287 return $values
288 ? $request->withHeader('Cookie', \implode('; ', $values))
289 : $request;
290 }
291
292 /**
293 * If a cookie already exists and the server asks to set it again with a
294 * null value, the cookie must be deleted.
295 */
296 private function removeCookieIfEmpty(SetCookie $cookie): void
297 {
298 $cookieValue = $cookie->getValue();
299 if ($cookieValue === null || $cookieValue === '') {
300 $this->clear(
301 $cookie->getDomain(),
302 $cookie->getPath(),
303 $cookie->getName()
304 );
305 }
306 }
307}
Note: See TracBrowser for help on using the repository browser.