Completed
Pull Request — master (#14)
by Márk
03:42
created

CookiePlugin   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 141
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 64.71%

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 24
c 1
b 0
f 1
lcom 1
cbo 5
dl 0
loc 141
ccs 44
cts 68
cp 0.6471
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
C handleRequest() 0 46 11
C createCookie() 0 49 10
A createValueKey() 0 8 2
1
<?php
2
3
namespace Http\Client\Common\Plugin;
4
5
use Http\Client\Common\Plugin;
6
use Http\Message\Cookie;
7
use Http\Message\CookieJar;
8
use Psr\Http\Message\RequestInterface;
9
use Psr\Http\Message\ResponseInterface;
10
11
/**
12
 * Handle request cookies.
13
 *
14
 * @author Joel Wurtz <[email protected]>
15
 *
16
 * TODO: make class final in version 2.0, once plugins does not extend it anymore.
17
 */
18
/*final*/ class CookiePlugin implements Plugin
19
{
20
    /**
21
     * Cookie storage.
22
     *
23
     * @var CookieJar
24
     */
25
    private $cookieJar;
26
27
    /**
28
     * @param CookieJar $cookieJar
29
     */
30 9
    public function __construct(CookieJar $cookieJar)
31
    {
32 9
        $this->cookieJar = $cookieJar;
33 9
    }
34
35
    /**
36
     * {@inheritdoc}
37
     */
38 7
    public function handleRequest(RequestInterface $request, callable $next, callable $first)
39
    {
40 7
        foreach ($this->cookieJar->getCookies() as $cookie) {
41 6
            if ($cookie->isExpired()) {
42 1
                continue;
43
            }
44
45 5
            if (!$cookie->matchDomain($request->getUri()->getHost())) {
46 1
                continue;
47
            }
48
49 4
            if (!$cookie->matchPath($request->getUri()->getPath())) {
50 1
                continue;
51
            }
52
53 3
            if ($cookie->isSecure() && ($request->getUri()->getScheme() !== 'https')) {
54 1
                continue;
55
            }
56
57 2
            $request = $request->withAddedHeader('Cookie', sprintf('%s=%s', $cookie->getName(), $cookie->getValue()));
58 7
        }
59
60 7
        return $next($request)->then(function (ResponseInterface $response) use ($request) {
61 1
            if ($response->hasHeader('Set-Cookie')) {
62 1
                $setCookies = $response->getHeader('Set-Cookie');
63
64 1
                foreach ($setCookies as $setCookie) {
65 1
                    $cookie = $this->createCookie($request, $setCookie);
66
67
                    // Cookie invalid do not use it
68 1
                    if (null === $cookie) {
69
                        continue;
70
                    }
71
72
                    // Restrict setting cookie from another domain
73 1
                    if (false === strpos($cookie->getDomain(), $request->getUri()->getHost())) {
74
                        continue;
75
                    }
76
77 1
                    $this->cookieJar->addCookie($cookie);
78 1
                }
79 1
            }
80
81 1
            return $response;
82 7
        });
83
    }
84
85
    /**
86
     * Creates a cookie from a string.
87
     *
88
     * @param RequestInterface $request
89
     * @param $setCookie
90
     *
91
     * @return Cookie|null
92
     */
93 1
    private function createCookie(RequestInterface $request, $setCookie)
94
    {
95 1
        $parts = array_map('trim', explode(';', $setCookie));
96
97 1
        if (empty($parts) || !strpos($parts[0], '=')) {
98
            return;
99
        }
100
101 1
        list($name, $cookieValue) = $this->createValueKey(array_shift($parts));
102
103 1
        $expires = 0;
104 1
        $domain = $request->getUri()->getHost();
105 1
        $path = $request->getUri()->getPath();
106 1
        $secure = false;
107 1
        $httpOnly = false;
108
109
        // Add the cookie pieces into the parsed data array
110 1
        foreach ($parts as $part) {
111
            list($key, $value) = $this->createValueKey($part);
112
113
            switch (strtolower($key)) {
114
                case 'expires':
115
                    $expires = \DateTime::createFromFormat(DATE_COOKIE, $value);
116
                    break;
117
118
                case 'max-age':
119
                    $expires = (new \DateTime())->add(new \DateInterval('PT'.(int) $value.'S'));
120
                    break;
121
122
                case 'domain':
123
                    $domain = $value;
124
                    break;
125
126
                case 'path':
127
                    $path = $value;
128
                    break;
129
130
                case 'secure':
131
                    $secure = true;
132
                    break;
133
134
                case 'httponly':
135
                    $httpOnly = true;
136
                    break;
137
            }
138 1
        }
139
140 1
        return new Cookie($name, $cookieValue, $expires, $domain, $path, $secure, $httpOnly);
0 ignored issues
show
Bug introduced by
It seems like $cookieValue defined by $this->createValueKey(array_shift($parts)) on line 101 can also be of type boolean; however, Http\Message\Cookie::__construct() does only seem to accept string|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug introduced by
It seems like $domain defined by $value on line 123 can also be of type boolean; however, Http\Message\Cookie::__construct() does only seem to accept string|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug introduced by
It seems like $path defined by $value on line 127 can also be of type boolean; however, Http\Message\Cookie::__construct() does only seem to accept string|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
141
    }
142
143
    /**
144
     * Separates key/value pair from cookie.
145
     *
146
     * @param $part
147
     *
148
     * @return array
149
     */
150 1
    private function createValueKey($part)
151
    {
152 1
        $parts = explode('=', $part, 2);
153 1
        $key = trim($parts[0]);
154 1
        $value = isset($parts[1]) ? trim($parts[1]) : true;
155
156 1
        return [$key, $value];
157
    }
158
}
159