| | <?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; |
| | } |
| | } |
| |
|