annotate vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php @ 19:fa3358dc1485 tip

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