| <?php |
| |
| |
| |
| |
| |
|
|
| namespace WpOrg\Requests; |
|
|
| use WpOrg\Requests\Exception\InvalidArgument; |
| use WpOrg\Requests\Iri; |
| use WpOrg\Requests\Response\Headers; |
| use WpOrg\Requests\Utility\CaseInsensitiveDictionary; |
| use WpOrg\Requests\Utility\InputValidator; |
|
|
| |
| |
| |
| |
| |
| class Cookie { |
| |
| |
| |
| |
| |
| public $name; |
|
|
| |
| |
| |
| |
| |
| public $value; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public $attributes = []; |
|
|
| |
| |
| |
| |
| |
| |
| |
| public $flags = []; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public $reference_time = 0; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public function __construct($name, $value, $attributes = [], $flags = [], $reference_time = null) { |
| if (is_string($name) === false) { |
| throw InvalidArgument::create(1, '$name', 'string', gettype($name)); |
| } |
|
|
| if (is_string($value) === false) { |
| throw InvalidArgument::create(2, '$value', 'string', gettype($value)); |
| } |
|
|
| if (InputValidator::has_array_access($attributes) === false || InputValidator::is_iterable($attributes) === false) { |
| throw InvalidArgument::create(3, '$attributes', 'array|ArrayAccess&Traversable', gettype($attributes)); |
| } |
|
|
| if (is_array($flags) === false) { |
| throw InvalidArgument::create(4, '$flags', 'array', gettype($flags)); |
| } |
|
|
| if ($reference_time !== null && is_int($reference_time) === false) { |
| throw InvalidArgument::create(5, '$reference_time', 'integer|null', gettype($reference_time)); |
| } |
|
|
| $this->name = $name; |
| $this->value = $value; |
| $this->attributes = $attributes; |
| $default_flags = [ |
| 'creation' => time(), |
| 'last-access' => time(), |
| 'persistent' => false, |
| 'host-only' => true, |
| ]; |
| $this->flags = array_merge($default_flags, $flags); |
|
|
| $this->reference_time = time(); |
| if ($reference_time !== null) { |
| $this->reference_time = $reference_time; |
| } |
|
|
| $this->normalize(); |
| } |
|
|
| |
| |
| |
| |
| |
| public function __toString() { |
| return $this->value; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public function is_expired() { |
| |
| |
| |
| |
| if (isset($this->attributes['max-age'])) { |
| $max_age = $this->attributes['max-age']; |
| return $max_age < $this->reference_time; |
| } |
|
|
| if (isset($this->attributes['expires'])) { |
| $expires = $this->attributes['expires']; |
| return $expires < $this->reference_time; |
| } |
|
|
| return false; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public function uri_matches(Iri $uri) { |
| if (!$this->domain_matches($uri->host)) { |
| return false; |
| } |
|
|
| if (!$this->path_matches($uri->path)) { |
| return false; |
| } |
|
|
| return empty($this->attributes['secure']) || $uri->scheme === 'https'; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public function domain_matches($domain) { |
| if (is_string($domain) === false) { |
| return false; |
| } |
|
|
| if (!isset($this->attributes['domain'])) { |
| |
| |
| return true; |
| } |
|
|
| $cookie_domain = $this->attributes['domain']; |
| if ($cookie_domain === $domain) { |
| |
| return true; |
| } |
|
|
| |
| |
| if ($this->flags['host-only'] === true) { |
| return false; |
| } |
|
|
| if (strlen($domain) <= strlen($cookie_domain)) { |
| |
| |
| return false; |
| } |
|
|
| if (substr($domain, -1 * strlen($cookie_domain)) !== $cookie_domain) { |
| |
| return false; |
| } |
|
|
| $prefix = substr($domain, 0, strlen($domain) - strlen($cookie_domain)); |
| if (substr($prefix, -1) !== '.') { |
| |
| |
| return false; |
| } |
|
|
| |
| return !preg_match('#^(.+\.)\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#', $domain); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public function path_matches($request_path) { |
| if (empty($request_path)) { |
| |
| $request_path = '/'; |
| } |
|
|
| if (!isset($this->attributes['path'])) { |
| |
| |
| return true; |
| } |
|
|
| if (is_scalar($request_path) === false) { |
| return false; |
| } |
|
|
| $cookie_path = $this->attributes['path']; |
|
|
| if ($cookie_path === $request_path) { |
| |
| return true; |
| } |
|
|
| if (strlen($request_path) > strlen($cookie_path) && substr($request_path, 0, strlen($cookie_path)) === $cookie_path) { |
| if (substr($cookie_path, -1) === '/') { |
| |
| |
| return true; |
| } |
|
|
| if (substr($request_path, strlen($cookie_path), 1) === '/') { |
| |
| |
| |
| return true; |
| } |
| } |
|
|
| return false; |
| } |
|
|
| |
| |
| |
| |
| |
| public function normalize() { |
| foreach ($this->attributes as $key => $value) { |
| $orig_value = $value; |
|
|
| if (is_string($key)) { |
| $value = $this->normalize_attribute($key, $value); |
| } |
|
|
| if ($value === null) { |
| unset($this->attributes[$key]); |
| continue; |
| } |
|
|
| if ($value !== $orig_value) { |
| $this->attributes[$key] = $value; |
| } |
| } |
|
|
| return true; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| protected function normalize_attribute($name, $value) { |
| switch (strtolower($name)) { |
| case 'expires': |
| |
| if (is_int($value)) { |
| return $value; |
| } |
|
|
| $expiry_time = strtotime($value); |
| if ($expiry_time === false) { |
| return null; |
| } |
|
|
| return $expiry_time; |
|
|
| case 'max-age': |
| |
| if (is_int($value)) { |
| return $value; |
| } |
|
|
| |
| if (!preg_match('/^-?\d+$/', $value)) { |
| return null; |
| } |
|
|
| $delta_seconds = (int) $value; |
| if ($delta_seconds <= 0) { |
| $expiry_time = 0; |
| } else { |
| $expiry_time = $this->reference_time + $delta_seconds; |
| } |
|
|
| return $expiry_time; |
|
|
| case 'domain': |
| |
| if (empty($value)) { |
| return null; |
| } |
|
|
| |
| if ($value[0] === '.') { |
| $value = substr($value, 1); |
| } |
|
|
| return $value; |
|
|
| default: |
| return $value; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| public function format_for_header() { |
| return sprintf('%s=%s', $this->name, $this->value); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public function format_for_set_cookie() { |
| $header_value = $this->format_for_header(); |
| if (!empty($this->attributes)) { |
| $parts = []; |
| foreach ($this->attributes as $key => $value) { |
| |
| if (is_numeric($key)) { |
| $parts[] = $value; |
| } else { |
| $parts[] = sprintf('%s=%s', $key, $value); |
| } |
| } |
|
|
| $header_value .= '; ' . implode('; ', $parts); |
| } |
|
|
| return $header_value; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static function parse($cookie_header, $name = '', $reference_time = null) { |
| if (is_string($cookie_header) === false) { |
| throw InvalidArgument::create(1, '$cookie_header', 'string', gettype($cookie_header)); |
| } |
|
|
| if (is_string($name) === false) { |
| throw InvalidArgument::create(2, '$name', 'string', gettype($name)); |
| } |
|
|
| $parts = explode(';', $cookie_header); |
| $kvparts = array_shift($parts); |
|
|
| if (!empty($name)) { |
| $value = $cookie_header; |
| } elseif (strpos($kvparts, '=') === false) { |
| |
| |
| |
| |
| |
| $name = ''; |
| $value = $kvparts; |
| } else { |
| list($name, $value) = explode('=', $kvparts, 2); |
| } |
|
|
| $name = trim($name); |
| $value = trim($value); |
|
|
| |
| $attributes = new CaseInsensitiveDictionary(); |
|
|
| if (!empty($parts)) { |
| foreach ($parts as $part) { |
| if (strpos($part, '=') === false) { |
| $part_key = $part; |
| $part_value = true; |
| } else { |
| list($part_key, $part_value) = explode('=', $part, 2); |
| $part_value = trim($part_value); |
| } |
|
|
| $part_key = trim($part_key); |
| $attributes[$part_key] = $part_value; |
| } |
| } |
|
|
| return new static($name, $value, $attributes, [], $reference_time); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static function parse_from_headers(Headers $headers, $origin = null, $time = null) { |
| $cookie_headers = $headers->getValues('Set-Cookie'); |
| if (empty($cookie_headers)) { |
| return []; |
| } |
|
|
| if ($origin !== null && !($origin instanceof Iri)) { |
| throw InvalidArgument::create(2, '$origin', Iri::class . ' or null', gettype($origin)); |
| } |
|
|
| $cookies = []; |
| foreach ($cookie_headers as $header) { |
| $parsed = self::parse($header, '', $time); |
|
|
| |
| if (empty($parsed->attributes['domain']) && !empty($origin)) { |
| $parsed->attributes['domain'] = $origin->host; |
| $parsed->flags['host-only'] = true; |
| } else { |
| $parsed->flags['host-only'] = false; |
| } |
|
|
| $path_is_valid = (!empty($parsed->attributes['path']) && $parsed->attributes['path'][0] === '/'); |
| if (!$path_is_valid && !empty($origin)) { |
| $path = $origin->path; |
|
|
| |
| if (substr($path, 0, 1) !== '/') { |
| |
| |
| |
| $path = '/'; |
| } elseif (substr_count($path, '/') === 1) { |
| |
| |
| |
| $path = '/'; |
| } else { |
| |
| |
| |
| $path = substr($path, 0, strrpos($path, '/')); |
| } |
|
|
| $parsed->attributes['path'] = $path; |
| } |
|
|
| |
| if (!empty($origin) && !$parsed->domain_matches($origin->host)) { |
| continue; |
| } |
|
|
| $cookies[$parsed->name] = $parsed; |
| } |
|
|
| return $cookies; |
| } |
| } |
|
|