diff options
Diffstat (limited to 'vendor/guzzlehttp/guzzle/src/Cookie')
-rw-r--r-- | vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php | 307 | ||||
-rw-r--r-- | vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php | 80 | ||||
-rw-r--r-- | vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php | 101 | ||||
-rw-r--r-- | vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php | 77 | ||||
-rw-r--r-- | vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php | 488 |
5 files changed, 1053 insertions, 0 deletions
diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php new file mode 100644 index 0000000..fa2b10a --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php @@ -0,0 +1,307 @@ +<?php + +namespace GuzzleHttp\Cookie; + +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Cookie jar that stores cookies as an array + */ +class CookieJar implements CookieJarInterface +{ + /** + * @var SetCookie[] Loaded cookie data + */ + private $cookies = []; + + /** + * @var bool + */ + private $strictMode; + + /** + * @param bool $strictMode Set to true to throw exceptions when invalid + * cookies are added to the cookie jar. + * @param array $cookieArray Array of SetCookie objects or a hash of + * arrays that can be used with the SetCookie + * constructor + */ + public function __construct(bool $strictMode = false, array $cookieArray = []) + { + $this->strictMode = $strictMode; + + foreach ($cookieArray as $cookie) { + if (!($cookie instanceof SetCookie)) { + $cookie = new SetCookie($cookie); + } + $this->setCookie($cookie); + } + } + + /** + * Create a new Cookie jar from an associative array and domain. + * + * @param array $cookies Cookies to create the jar from + * @param string $domain Domain to set the cookies to + */ + public static function fromArray(array $cookies, string $domain): self + { + $cookieJar = new self(); + foreach ($cookies as $name => $value) { + $cookieJar->setCookie(new SetCookie([ + 'Domain' => $domain, + 'Name' => $name, + 'Value' => $value, + 'Discard' => true, + ])); + } + + return $cookieJar; + } + + /** + * Evaluate if this cookie should be persisted to storage + * that survives between requests. + * + * @param SetCookie $cookie Being evaluated. + * @param bool $allowSessionCookies If we should persist session cookies + */ + public static function shouldPersist(SetCookie $cookie, bool $allowSessionCookies = false): bool + { + if ($cookie->getExpires() || $allowSessionCookies) { + if (!$cookie->getDiscard()) { + return true; + } + } + + return false; + } + + /** + * Finds and returns the cookie based on the name + * + * @param string $name cookie name to search for + * + * @return SetCookie|null cookie that was found or null if not found + */ + public function getCookieByName(string $name): ?SetCookie + { + foreach ($this->cookies as $cookie) { + if ($cookie->getName() !== null && \strcasecmp($cookie->getName(), $name) === 0) { + return $cookie; + } + } + + return null; + } + + public function toArray(): array + { + return \array_map(static function (SetCookie $cookie): array { + return $cookie->toArray(); + }, $this->getIterator()->getArrayCopy()); + } + + public function clear(string $domain = null, string $path = null, string $name = null): void + { + if (!$domain) { + $this->cookies = []; + + return; + } elseif (!$path) { + $this->cookies = \array_filter( + $this->cookies, + static function (SetCookie $cookie) use ($domain): bool { + return !$cookie->matchesDomain($domain); + } + ); + } elseif (!$name) { + $this->cookies = \array_filter( + $this->cookies, + static function (SetCookie $cookie) use ($path, $domain): bool { + return !($cookie->matchesPath($path) + && $cookie->matchesDomain($domain)); + } + ); + } else { + $this->cookies = \array_filter( + $this->cookies, + static function (SetCookie $cookie) use ($path, $domain, $name) { + return !($cookie->getName() == $name + && $cookie->matchesPath($path) + && $cookie->matchesDomain($domain)); + } + ); + } + } + + public function clearSessionCookies(): void + { + $this->cookies = \array_filter( + $this->cookies, + static function (SetCookie $cookie): bool { + return !$cookie->getDiscard() && $cookie->getExpires(); + } + ); + } + + public function setCookie(SetCookie $cookie): bool + { + // If the name string is empty (but not 0), ignore the set-cookie + // string entirely. + $name = $cookie->getName(); + if (!$name && $name !== '0') { + return false; + } + + // Only allow cookies with set and valid domain, name, value + $result = $cookie->validate(); + if ($result !== true) { + if ($this->strictMode) { + throw new \RuntimeException('Invalid cookie: '.$result); + } + $this->removeCookieIfEmpty($cookie); + + return false; + } + + // Resolve conflicts with previously set cookies + foreach ($this->cookies as $i => $c) { + // Two cookies are identical, when their path, and domain are + // identical. + if ($c->getPath() != $cookie->getPath() + || $c->getDomain() != $cookie->getDomain() + || $c->getName() != $cookie->getName() + ) { + continue; + } + + // The previously set cookie is a discard cookie and this one is + // not so allow the new cookie to be set + if (!$cookie->getDiscard() && $c->getDiscard()) { + unset($this->cookies[$i]); + continue; + } + + // If the new cookie's expiration is further into the future, then + // replace the old cookie + if ($cookie->getExpires() > $c->getExpires()) { + unset($this->cookies[$i]); + continue; + } + + // If the value has changed, we better change it + if ($cookie->getValue() !== $c->getValue()) { + unset($this->cookies[$i]); + continue; + } + + // The cookie exists, so no need to continue + return false; + } + + $this->cookies[] = $cookie; + + return true; + } + + public function count(): int + { + return \count($this->cookies); + } + + /** + * @return \ArrayIterator<int, SetCookie> + */ + public function getIterator(): \ArrayIterator + { + return new \ArrayIterator(\array_values($this->cookies)); + } + + public function extractCookies(RequestInterface $request, ResponseInterface $response): void + { + if ($cookieHeader = $response->getHeader('Set-Cookie')) { + foreach ($cookieHeader as $cookie) { + $sc = SetCookie::fromString($cookie); + if (!$sc->getDomain()) { + $sc->setDomain($request->getUri()->getHost()); + } + if (0 !== \strpos($sc->getPath(), '/')) { + $sc->setPath($this->getCookiePathFromRequest($request)); + } + if (!$sc->matchesDomain($request->getUri()->getHost())) { + continue; + } + // Note: At this point `$sc->getDomain()` being a public suffix should + // be rejected, but we don't want to pull in the full PSL dependency. + $this->setCookie($sc); + } + } + } + + /** + * Computes cookie path following RFC 6265 section 5.1.4 + * + * @see https://tools.ietf.org/html/rfc6265#section-5.1.4 + */ + private function getCookiePathFromRequest(RequestInterface $request): string + { + $uriPath = $request->getUri()->getPath(); + if ('' === $uriPath) { + return '/'; + } + if (0 !== \strpos($uriPath, '/')) { + return '/'; + } + if ('/' === $uriPath) { + return '/'; + } + $lastSlashPos = \strrpos($uriPath, '/'); + if (0 === $lastSlashPos || false === $lastSlashPos) { + return '/'; + } + + return \substr($uriPath, 0, $lastSlashPos); + } + + public function withCookieHeader(RequestInterface $request): RequestInterface + { + $values = []; + $uri = $request->getUri(); + $scheme = $uri->getScheme(); + $host = $uri->getHost(); + $path = $uri->getPath() ?: '/'; + + foreach ($this->cookies as $cookie) { + if ($cookie->matchesPath($path) + && $cookie->matchesDomain($host) + && !$cookie->isExpired() + && (!$cookie->getSecure() || $scheme === 'https') + ) { + $values[] = $cookie->getName().'=' + .$cookie->getValue(); + } + } + + return $values + ? $request->withHeader('Cookie', \implode('; ', $values)) + : $request; + } + + /** + * If a cookie already exists and the server asks to set it again with a + * null value, the cookie must be deleted. + */ + private function removeCookieIfEmpty(SetCookie $cookie): void + { + $cookieValue = $cookie->getValue(); + if ($cookieValue === null || $cookieValue === '') { + $this->clear( + $cookie->getDomain(), + $cookie->getPath(), + $cookie->getName() + ); + } + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php new file mode 100644 index 0000000..8c55cc6 --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php @@ -0,0 +1,80 @@ +<?php + +namespace GuzzleHttp\Cookie; + +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + +/** + * Stores HTTP cookies. + * + * It extracts cookies from HTTP requests, and returns them in HTTP responses. + * CookieJarInterface instances automatically expire contained cookies when + * necessary. Subclasses are also responsible for storing and retrieving + * cookies from a file, database, etc. + * + * @see https://docs.python.org/2/library/cookielib.html Inspiration + * + * @extends \IteratorAggregate<SetCookie> + */ +interface CookieJarInterface extends \Countable, \IteratorAggregate +{ + /** + * Create a request with added cookie headers. + * + * If no matching cookies are found in the cookie jar, then no Cookie + * header is added to the request and the same request is returned. + * + * @param RequestInterface $request Request object to modify. + * + * @return RequestInterface returns the modified request. + */ + public function withCookieHeader(RequestInterface $request): RequestInterface; + + /** + * Extract cookies from an HTTP response and store them in the CookieJar. + * + * @param RequestInterface $request Request that was sent + * @param ResponseInterface $response Response that was received + */ + public function extractCookies(RequestInterface $request, ResponseInterface $response): void; + + /** + * Sets a cookie in the cookie jar. + * + * @param SetCookie $cookie Cookie to set. + * + * @return bool Returns true on success or false on failure + */ + public function setCookie(SetCookie $cookie): bool; + + /** + * Remove cookies currently held in the cookie jar. + * + * Invoking this method without arguments will empty the whole cookie jar. + * If given a $domain argument only cookies belonging to that domain will + * be removed. If given a $domain and $path argument, cookies belonging to + * the specified path within that domain are removed. If given all three + * arguments, then the cookie with the specified name, path and domain is + * removed. + * + * @param string|null $domain Clears cookies matching a domain + * @param string|null $path Clears cookies matching a domain and path + * @param string|null $name Clears cookies matching a domain, path, and name + */ + public function clear(string $domain = null, string $path = null, string $name = null): void; + + /** + * Discard all sessions cookies. + * + * Removes cookies that don't have an expire field or a have a discard + * field set to true. To be called when the user agent shuts down according + * to RFC 2965. + */ + public function clearSessionCookies(): void; + + /** + * Converts the cookie jar to an array. + */ + public function toArray(): array; +} diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php b/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php new file mode 100644 index 0000000..290236d --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php @@ -0,0 +1,101 @@ +<?php + +namespace GuzzleHttp\Cookie; + +use GuzzleHttp\Utils; + +/** + * Persists non-session cookies using a JSON formatted file + */ +class FileCookieJar extends CookieJar +{ + /** + * @var string filename + */ + private $filename; + + /** + * @var bool Control whether to persist session cookies or not. + */ + private $storeSessionCookies; + + /** + * Create a new FileCookieJar object + * + * @param string $cookieFile File to store the cookie data + * @param bool $storeSessionCookies Set to true to store session cookies + * in the cookie jar. + * + * @throws \RuntimeException if the file cannot be found or created + */ + public function __construct(string $cookieFile, bool $storeSessionCookies = false) + { + parent::__construct(); + $this->filename = $cookieFile; + $this->storeSessionCookies = $storeSessionCookies; + + if (\file_exists($cookieFile)) { + $this->load($cookieFile); + } + } + + /** + * Saves the file when shutting down + */ + public function __destruct() + { + $this->save($this->filename); + } + + /** + * Saves the cookies to a file. + * + * @param string $filename File to save + * + * @throws \RuntimeException if the file cannot be found or created + */ + public function save(string $filename): void + { + $json = []; + /** @var SetCookie $cookie */ + foreach ($this as $cookie) { + if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { + $json[] = $cookie->toArray(); + } + } + + $jsonStr = Utils::jsonEncode($json); + if (false === \file_put_contents($filename, $jsonStr, \LOCK_EX)) { + throw new \RuntimeException("Unable to save file {$filename}"); + } + } + + /** + * Load cookies from a JSON formatted file. + * + * Old cookies are kept unless overwritten by newly loaded ones. + * + * @param string $filename Cookie file to load. + * + * @throws \RuntimeException if the file cannot be loaded. + */ + public function load(string $filename): void + { + $json = \file_get_contents($filename); + if (false === $json) { + throw new \RuntimeException("Unable to load file {$filename}"); + } + if ($json === '') { + return; + } + + $data = Utils::jsonDecode($json, true); + if (\is_array($data)) { + foreach ($data as $cookie) { + $this->setCookie(new SetCookie($cookie)); + } + } elseif (\is_scalar($data) && !empty($data)) { + throw new \RuntimeException("Invalid cookie file: {$filename}"); + } + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php b/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php new file mode 100644 index 0000000..cb3e67c --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php @@ -0,0 +1,77 @@ +<?php + +namespace GuzzleHttp\Cookie; + +/** + * Persists cookies in the client session + */ +class SessionCookieJar extends CookieJar +{ + /** + * @var string session key + */ + private $sessionKey; + + /** + * @var bool Control whether to persist session cookies or not. + */ + private $storeSessionCookies; + + /** + * Create a new SessionCookieJar object + * + * @param string $sessionKey Session key name to store the cookie + * data in session + * @param bool $storeSessionCookies Set to true to store session cookies + * in the cookie jar. + */ + public function __construct(string $sessionKey, bool $storeSessionCookies = false) + { + parent::__construct(); + $this->sessionKey = $sessionKey; + $this->storeSessionCookies = $storeSessionCookies; + $this->load(); + } + + /** + * Saves cookies to session when shutting down + */ + public function __destruct() + { + $this->save(); + } + + /** + * Save cookies to the client session + */ + public function save(): void + { + $json = []; + /** @var SetCookie $cookie */ + foreach ($this as $cookie) { + if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { + $json[] = $cookie->toArray(); + } + } + + $_SESSION[$this->sessionKey] = \json_encode($json); + } + + /** + * Load the contents of the client session into the data array + */ + protected function load(): void + { + if (!isset($_SESSION[$this->sessionKey])) { + return; + } + $data = \json_decode($_SESSION[$this->sessionKey], true); + if (\is_array($data)) { + foreach ($data as $cookie) { + $this->setCookie(new SetCookie($cookie)); + } + } elseif (\strlen($data)) { + throw new \RuntimeException('Invalid cookie data'); + } + } +} diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php b/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php new file mode 100644 index 0000000..d74915b --- /dev/null +++ b/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php @@ -0,0 +1,488 @@ +<?php + +namespace GuzzleHttp\Cookie; + +/** + * Set-Cookie object + */ +class SetCookie +{ + /** + * @var array + */ + private static $defaults = [ + 'Name' => null, + 'Value' => null, + 'Domain' => null, + 'Path' => '/', + 'Max-Age' => null, + 'Expires' => null, + 'Secure' => false, + 'Discard' => false, + 'HttpOnly' => false, + ]; + + /** + * @var array Cookie data + */ + private $data; + + /** + * Create a new SetCookie object from a string. + * + * @param string $cookie Set-Cookie header string + */ + public static function fromString(string $cookie): self + { + // Create the default return array + $data = self::$defaults; + // Explode the cookie string using a series of semicolons + $pieces = \array_filter(\array_map('trim', \explode(';', $cookie))); + // The name of the cookie (first kvp) must exist and include an equal sign. + if (!isset($pieces[0]) || \strpos($pieces[0], '=') === false) { + return new self($data); + } + + // Add the cookie pieces into the parsed data array + foreach ($pieces as $part) { + $cookieParts = \explode('=', $part, 2); + $key = \trim($cookieParts[0]); + $value = isset($cookieParts[1]) + ? \trim($cookieParts[1], " \n\r\t\0\x0B") + : true; + + // Only check for non-cookies when cookies have been found + if (!isset($data['Name'])) { + $data['Name'] = $key; + $data['Value'] = $value; + } else { + foreach (\array_keys(self::$defaults) as $search) { + if (!\strcasecmp($search, $key)) { + if ($search === 'Max-Age') { + if (is_numeric($value)) { + $data[$search] = (int) $value; + } + } else { + $data[$search] = $value; + } + continue 2; + } + } + $data[$key] = $value; + } + } + + return new self($data); + } + + /** + * @param array $data Array of cookie data provided by a Cookie parser + */ + public function __construct(array $data = []) + { + $this->data = self::$defaults; + + if (isset($data['Name'])) { + $this->setName($data['Name']); + } + + if (isset($data['Value'])) { + $this->setValue($data['Value']); + } + + if (isset($data['Domain'])) { + $this->setDomain($data['Domain']); + } + + if (isset($data['Path'])) { + $this->setPath($data['Path']); + } + + if (isset($data['Max-Age'])) { + $this->setMaxAge($data['Max-Age']); + } + + if (isset($data['Expires'])) { + $this->setExpires($data['Expires']); + } + + if (isset($data['Secure'])) { + $this->setSecure($data['Secure']); + } + + if (isset($data['Discard'])) { + $this->setDiscard($data['Discard']); + } + + if (isset($data['HttpOnly'])) { + $this->setHttpOnly($data['HttpOnly']); + } + + // Set the remaining values that don't have extra validation logic + foreach (array_diff(array_keys($data), array_keys(self::$defaults)) as $key) { + $this->data[$key] = $data[$key]; + } + + // Extract the Expires value and turn it into a UNIX timestamp if needed + if (!$this->getExpires() && $this->getMaxAge()) { + // Calculate the Expires date + $this->setExpires(\time() + $this->getMaxAge()); + } elseif (null !== ($expires = $this->getExpires()) && !\is_numeric($expires)) { + $this->setExpires($expires); + } + } + + public function __toString() + { + $str = $this->data['Name'].'='.($this->data['Value'] ?? '').'; '; + foreach ($this->data as $k => $v) { + if ($k !== 'Name' && $k !== 'Value' && $v !== null && $v !== false) { + if ($k === 'Expires') { + $str .= 'Expires='.\gmdate('D, d M Y H:i:s \G\M\T', $v).'; '; + } else { + $str .= ($v === true ? $k : "{$k}={$v}").'; '; + } + } + } + + return \rtrim($str, '; '); + } + + public function toArray(): array + { + return $this->data; + } + + /** + * Get the cookie name. + * + * @return string + */ + public function getName() + { + return $this->data['Name']; + } + + /** + * Set the cookie name. + * + * @param string $name Cookie name + */ + public function setName($name): void + { + if (!is_string($name)) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['Name'] = (string) $name; + } + + /** + * Get the cookie value. + * + * @return string|null + */ + public function getValue() + { + return $this->data['Value']; + } + + /** + * Set the cookie value. + * + * @param string $value Cookie value + */ + public function setValue($value): void + { + if (!is_string($value)) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['Value'] = (string) $value; + } + + /** + * Get the domain. + * + * @return string|null + */ + public function getDomain() + { + return $this->data['Domain']; + } + + /** + * Set the domain of the cookie. + * + * @param string|null $domain + */ + public function setDomain($domain): void + { + if (!is_string($domain) && null !== $domain) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string or null to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['Domain'] = null === $domain ? null : (string) $domain; + } + + /** + * Get the path. + * + * @return string + */ + public function getPath() + { + return $this->data['Path']; + } + + /** + * Set the path of the cookie. + * + * @param string $path Path of the cookie + */ + public function setPath($path): void + { + if (!is_string($path)) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['Path'] = (string) $path; + } + + /** + * Maximum lifetime of the cookie in seconds. + * + * @return int|null + */ + public function getMaxAge() + { + return null === $this->data['Max-Age'] ? null : (int) $this->data['Max-Age']; + } + + /** + * Set the max-age of the cookie. + * + * @param int|null $maxAge Max age of the cookie in seconds + */ + public function setMaxAge($maxAge): void + { + if (!is_int($maxAge) && null !== $maxAge) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing an int or null to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['Max-Age'] = $maxAge === null ? null : (int) $maxAge; + } + + /** + * The UNIX timestamp when the cookie Expires. + * + * @return string|int|null + */ + public function getExpires() + { + return $this->data['Expires']; + } + + /** + * Set the unix timestamp for which the cookie will expire. + * + * @param int|string|null $timestamp Unix timestamp or any English textual datetime description. + */ + public function setExpires($timestamp): void + { + if (!is_int($timestamp) && !is_string($timestamp) && null !== $timestamp) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing an int, string or null to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['Expires'] = null === $timestamp ? null : (\is_numeric($timestamp) ? (int) $timestamp : \strtotime((string) $timestamp)); + } + + /** + * Get whether or not this is a secure cookie. + * + * @return bool + */ + public function getSecure() + { + return $this->data['Secure']; + } + + /** + * Set whether or not the cookie is secure. + * + * @param bool $secure Set to true or false if secure + */ + public function setSecure($secure): void + { + if (!is_bool($secure)) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a bool to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['Secure'] = (bool) $secure; + } + + /** + * Get whether or not this is a session cookie. + * + * @return bool|null + */ + public function getDiscard() + { + return $this->data['Discard']; + } + + /** + * Set whether or not this is a session cookie. + * + * @param bool $discard Set to true or false if this is a session cookie + */ + public function setDiscard($discard): void + { + if (!is_bool($discard)) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a bool to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['Discard'] = (bool) $discard; + } + + /** + * Get whether or not this is an HTTP only cookie. + * + * @return bool + */ + public function getHttpOnly() + { + return $this->data['HttpOnly']; + } + + /** + * Set whether or not this is an HTTP only cookie. + * + * @param bool $httpOnly Set to true or false if this is HTTP only + */ + public function setHttpOnly($httpOnly): void + { + if (!is_bool($httpOnly)) { + trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a bool to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); + } + + $this->data['HttpOnly'] = (bool) $httpOnly; + } + + /** + * Check if the cookie matches a path value. + * + * A request-path path-matches a given cookie-path if at least one of + * the following conditions holds: + * + * - The cookie-path and the request-path are identical. + * - The cookie-path is a prefix of the request-path, and the last + * character of the cookie-path is %x2F ("/"). + * - The cookie-path is a prefix of the request-path, and the first + * character of the request-path that is not included in the cookie- + * path is a %x2F ("/") character. + * + * @param string $requestPath Path to check against + */ + public function matchesPath(string $requestPath): bool + { + $cookiePath = $this->getPath(); + + // Match on exact matches or when path is the default empty "/" + if ($cookiePath === '/' || $cookiePath == $requestPath) { + return true; + } + + // Ensure that the cookie-path is a prefix of the request path. + if (0 !== \strpos($requestPath, $cookiePath)) { + return false; + } + + // Match if the last character of the cookie-path is "/" + if (\substr($cookiePath, -1, 1) === '/') { + return true; + } + + // Match if the first character not included in cookie path is "/" + return \substr($requestPath, \strlen($cookiePath), 1) === '/'; + } + + /** + * Check if the cookie matches a domain value. + * + * @param string $domain Domain to check against + */ + public function matchesDomain(string $domain): bool + { + $cookieDomain = $this->getDomain(); + if (null === $cookieDomain) { + return true; + } + + // Remove the leading '.' as per spec in RFC 6265. + // https://tools.ietf.org/html/rfc6265#section-5.2.3 + $cookieDomain = \ltrim(\strtolower($cookieDomain), '.'); + + $domain = \strtolower($domain); + + // Domain not set or exact match. + if ('' === $cookieDomain || $domain === $cookieDomain) { + return true; + } + + // Matching the subdomain according to RFC 6265. + // https://tools.ietf.org/html/rfc6265#section-5.1.3 + if (\filter_var($domain, \FILTER_VALIDATE_IP)) { + return false; + } + + return (bool) \preg_match('/\.'.\preg_quote($cookieDomain, '/').'$/', $domain); + } + + /** + * Check if the cookie is expired. + */ + public function isExpired(): bool + { + return $this->getExpires() !== null && \time() > $this->getExpires(); + } + + /** + * Check if the cookie is valid according to RFC 6265. + * + * @return bool|string Returns true if valid or an error message if invalid + */ + public function validate() + { + $name = $this->getName(); + if ($name === '') { + return 'The cookie name must not be empty'; + } + + // Check if any of the invalid characters are present in the cookie name + if (\preg_match( + '/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5c\x7b\x7d\x7f]/', + $name + )) { + return 'Cookie name must not contain invalid characters: ASCII ' + .'Control characters (0-31;127), space, tab and the ' + .'following characters: ()<>@,;:\"/?={}'; + } + + // Value must not be null. 0 and empty string are valid. Empty strings + // are technically against RFC 6265, but known to happen in the wild. + $value = $this->getValue(); + if ($value === null) { + return 'The cookie value must not be empty'; + } + + // Domains must not be empty, but can be 0. "0" is not a valid internet + // domain, but may be used as server name in a private network. + $domain = $this->getDomain(); + if ($domain === null || $domain === '') { + return 'The cookie domain must not be empty'; + } + + return true; + } +} |