PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.2
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.2
28.6 28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 All 130 releases
wordpress-seo / vendor_prefixed / guzzlehttp / guzzle / src / Cookie / CookieJar.php

CookieJar.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 28.2, at vendor_prefixed/guzzlehttp/guzzle/src/Cookie/CookieJar.php

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