serp-spider/core

View on GitHub
src/Core/Cookie/Cookie.php

Summary

Maintainability
A
55 mins
Test Coverage
<?php
/**
 * @license see LICENSE
 */

namespace Serps\Core\Cookie;

class Cookie
{

    protected $name;
    protected $value;
    protected $flags;
    /**
     * @param $name
     * @param $value
     * @param $flags
     */
    public function __construct($name, $value, $flags)
    {
        $this->name = $name;
        $this->value = $value;
        $this->flags = $flags;
    }

    protected function getFlag($flag, $default = null)
    {
        return isset($this->flags[$flag]) ?
            $this->flags[$flag]
            : $default;
    }


    /**
     * @return mixed
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @return mixed
     */
    public function getValue()
    {
        return $this->value;
    }



    /**
     * @return mixed
     */
    public function getPath()
    {
        return $this->getFlag('path', '/');
    }

    public function getDomain()
    {
        return $this->getFlag('domain');
    }

    public function getExpires()
    {
        return $this->getFlag('expires');
    }

    public function matchesPath($path)
    {

        $cookiePath = $this->getPath();

        // RFC6265 http://tools.ietf.org/search/rfc6265#section-5.1.4
        // A request-path path-matches a given cookie-path if at least one of
        // the following conditions holds:
        // o  The cookie-path and the request-path are identical.
        if ($path == $cookiePath) {
            return true;
        }
        $pos = stripos($path, $cookiePath);
        if ($pos === 0) {
            // o  The cookie-path is a prefix of the request-path, and the last
            // character of the cookie-path is %x2F ("/").
            if (substr($cookiePath, -1, 1) === '/') {
                return true;
            }
            // o  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.
            if (substr($path, strlen($cookiePath), 1) === '/') {
                return true;
            }
        }
        return false;
    }

    /**
     * Check if the cookie matches a domain value
     *
     * @param string $domain Domain to check against
     *
     * @return bool
     */
    public function matchesDomain($domain)
    {
        // Remove the leading '.' as per spec in RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.2.3
        $cookieDomain = ltrim($this->getDomain(), '.');
        // Domain not set or exact match.
        if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) {
            return true;
        }
        // Matching the subdomain according to RFC 6265: http://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, '/') . '$/i', $domain);
    }

    /**
     * Check if the cookie is expired
     *
     * @return bool
     */
    public function isExpired()
    {
        return $this->getExpires() && 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()
    {
        // Names must not be empty, but can be 0
        $name = $this->getName();
        if (empty($name) && !is_numeric($name)) {
            return 'The cookie name must not be empty';
        }
        // Check if any of the invalid characters are present in the cookie name
        if (strpbrk($name, self::getInvalidCharacters()) !== false) {
            return 'The cookie name must not contain invalid characters: ' . $name;
        }
        // Value must not be empty, but can be 0
        $value = $this->getValue();
        if (empty($value) && !is_numeric($value)) {
            return 'The cookie value must not be empty';
        }
        // Domains must not be empty, but can be 0
        // A "0" is not a valid internet domain, but may be used as server name in a private network
        $domain = $this->getDomain();
        if (empty($domain) && !is_numeric($domain)) {
            return 'The cookie domain must not be empty';
        }
        return true;
    }

    /**
     * @var string ASCII codes not valid for for use in a cookie name
     *
     * Cookie names are defined as 'token', according to RFC 2616, Section 2.2
     * A valid token may contain any CHAR except CTLs (ASCII 0 - 31 or 127)
     * or any of the following separators
     */
    protected static $invalidCharString;

    /**
     * Gets an array of invalid cookie characters
     *
     * @return array
     */
    protected static function getInvalidCharacters()
    {
        if (!self::$invalidCharString) {
            self::$invalidCharString = implode('', array_map('chr', array_merge(
                range(0, 32),
                [34, 40, 41, 44, 47],
                [58, 59, 60, 61, 62, 63, 64, 91, 92, 93, 123, 125, 127]
            )));
        }
        return self::$invalidCharString;
    }

    public function getDiscard()
    {
        return $this->getFlag('discard', false);
    }

    public function getSecure()
    {
        return $this->getFlag('secure', false);
    }

    public function getHttpOnly()
    {
        return $this->getFlag('http_only', false);
    }

    /**
     * Formats the cookies to be set in the Cookie header
     *
     * @return string the formatted cookie with this format: name=value
     */
    public function formatForCookieHeader()
    {
        return $this->getName() . '=' . $this->getValue();
    }


    /**
     * Formats the cookie into a json string to make it exportable.
     * @return string a json representation of the cookie
     */
    public function export()
    {
        return [
            'name' => $this->getName(),
            'value' => $this->getValue(),
            'flags' => [
                'path' => $this->getPath(),
                'domain' => $this->getDomain(),
                'expires' => $this->getExpires(),
                'discard' => $this->getDiscard(),
                'secure'  => $this->getSecure(),
                'http_only' => $this->getHttpOnly(),
            ]
        ];
    }
}