1<?php 2 3namespace Facebook\WebDriver; 4 5use InvalidArgumentException; 6 7/** 8 * Set values of an cookie. 9 * 10 * Implements ArrayAccess for backwards compatibility. 11 * 12 * @see https://w3c.github.io/webdriver/webdriver-spec.html#cookies 13 */ 14class Cookie implements \ArrayAccess 15{ 16 /** @var array */ 17 protected $cookie = []; 18 19 /** 20 * @param string $name The name of the cookie; may not be null or an empty string. 21 * @param string $value The cookie value; may not be null. 22 */ 23 public function __construct($name, $value) 24 { 25 $this->validateCookieName($name); 26 $this->validateCookieValue($value); 27 28 $this->cookie['name'] = $name; 29 $this->cookie['value'] = $value; 30 } 31 32 /** 33 * @param array $cookieArray The cookie fields; must contain name and value. 34 * @return Cookie 35 */ 36 public static function createFromArray(array $cookieArray) 37 { 38 if (!isset($cookieArray['name'])) { 39 throw new InvalidArgumentException('Cookie name should be set'); 40 } 41 if (!isset($cookieArray['value'])) { 42 throw new InvalidArgumentException('Cookie value should be set'); 43 } 44 $cookie = new self($cookieArray['name'], $cookieArray['value']); 45 46 if (isset($cookieArray['path'])) { 47 $cookie->setPath($cookieArray['path']); 48 } 49 if (isset($cookieArray['domain'])) { 50 $cookie->setDomain($cookieArray['domain']); 51 } 52 if (isset($cookieArray['expiry'])) { 53 $cookie->setExpiry($cookieArray['expiry']); 54 } 55 if (isset($cookieArray['secure'])) { 56 $cookie->setSecure($cookieArray['secure']); 57 } 58 if (isset($cookieArray['httpOnly'])) { 59 $cookie->setHttpOnly($cookieArray['httpOnly']); 60 } 61 if (isset($cookieArray['sameSite'])) { 62 $cookie->setSameSite($cookieArray['sameSite']); 63 } 64 65 return $cookie; 66 } 67 68 /** 69 * @return string 70 */ 71 public function getName() 72 { 73 return $this->offsetGet('name'); 74 } 75 76 /** 77 * @return string 78 */ 79 public function getValue() 80 { 81 return $this->offsetGet('value'); 82 } 83 84 /** 85 * The path the cookie is visible to. Defaults to "/" if omitted. 86 * 87 * @param string $path 88 */ 89 public function setPath($path) 90 { 91 $this->offsetSet('path', $path); 92 } 93 94 /** 95 * @return string|null 96 */ 97 public function getPath() 98 { 99 return $this->offsetGet('path'); 100 } 101 102 /** 103 * The domain the cookie is visible to. Defaults to the current browsing context's document's URL domain if omitted. 104 * 105 * @param string $domain 106 */ 107 public function setDomain($domain) 108 { 109 if (mb_strpos($domain, ':') !== false) { 110 throw new InvalidArgumentException(sprintf('Cookie domain "%s" should not contain a port', $domain)); 111 } 112 113 $this->offsetSet('domain', $domain); 114 } 115 116 /** 117 * @return string|null 118 */ 119 public function getDomain() 120 { 121 return $this->offsetGet('domain'); 122 } 123 124 /** 125 * The cookie's expiration date, specified in seconds since Unix Epoch. 126 * 127 * @param int $expiry 128 */ 129 public function setExpiry($expiry) 130 { 131 $this->offsetSet('expiry', (int) $expiry); 132 } 133 134 /** 135 * @return int|null 136 */ 137 public function getExpiry() 138 { 139 return $this->offsetGet('expiry'); 140 } 141 142 /** 143 * Whether this cookie requires a secure connection (https). Defaults to false if omitted. 144 * 145 * @param bool $secure 146 */ 147 public function setSecure($secure) 148 { 149 $this->offsetSet('secure', $secure); 150 } 151 152 /** 153 * @return bool|null 154 */ 155 public function isSecure() 156 { 157 return $this->offsetGet('secure'); 158 } 159 160 /** 161 * Whether the cookie is an HTTP only cookie. Defaults to false if omitted. 162 * 163 * @param bool $httpOnly 164 */ 165 public function setHttpOnly($httpOnly) 166 { 167 $this->offsetSet('httpOnly', $httpOnly); 168 } 169 170 /** 171 * @return bool|null 172 */ 173 public function isHttpOnly() 174 { 175 return $this->offsetGet('httpOnly'); 176 } 177 178 /** 179 * The cookie's same-site value. 180 * 181 * @param string $sameSite 182 */ 183 public function setSameSite($sameSite) 184 { 185 $this->offsetSet('sameSite', $sameSite); 186 } 187 188 /** 189 * @return string|null 190 */ 191 public function getSameSite() 192 { 193 return $this->offsetGet('sameSite'); 194 } 195 196 /** 197 * @return array 198 */ 199 public function toArray() 200 { 201 $cookie = $this->cookie; 202 if (!isset($cookie['secure'])) { 203 // Passing a boolean value for the "secure" flag is mandatory when using geckodriver 204 $cookie['secure'] = false; 205 } 206 207 return $cookie; 208 } 209 210 /** 211 * @param mixed $offset 212 * @return bool 213 */ 214 #[\ReturnTypeWillChange] 215 public function offsetExists($offset) 216 { 217 return isset($this->cookie[$offset]); 218 } 219 220 /** 221 * @param mixed $offset 222 * @return mixed 223 */ 224 #[\ReturnTypeWillChange] 225 public function offsetGet($offset) 226 { 227 return $this->offsetExists($offset) ? $this->cookie[$offset] : null; 228 } 229 230 /** 231 * @param mixed $offset 232 * @param mixed $value 233 * @return void 234 */ 235 #[\ReturnTypeWillChange] 236 public function offsetSet($offset, $value) 237 { 238 if ($value === null) { 239 unset($this->cookie[$offset]); 240 } else { 241 $this->cookie[$offset] = $value; 242 } 243 } 244 245 /** 246 * @param mixed $offset 247 * @return void 248 */ 249 #[\ReturnTypeWillChange] 250 public function offsetUnset($offset) 251 { 252 unset($this->cookie[$offset]); 253 } 254 255 /** 256 * @param string $name 257 */ 258 protected function validateCookieName($name) 259 { 260 if ($name === null || $name === '') { 261 throw new InvalidArgumentException('Cookie name should be non-empty'); 262 } 263 264 if (mb_strpos($name, ';') !== false) { 265 throw new InvalidArgumentException('Cookie name should not contain a ";"'); 266 } 267 } 268 269 /** 270 * @param string $value 271 */ 272 protected function validateCookieValue($value) 273 { 274 if ($value === null) { 275 throw new InvalidArgumentException('Cookie value is required when setting a cookie'); 276 } 277 } 278} 279