Completed
Pull Request — master (#14)
by Márk
06:15
created

CookiePlugin::handleRequest()   C

Complexity

Conditions 11
Paths 6

Size

Total Lines 46
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 46
rs 5.2653
cc 11
eloc 22
nc 6
nop 3

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
final class CookiePlugin implements Plugin
17
{
18
    /**
19
     * Cookie storage.
20
     *
21
     * @var CookieJar
22
     */
23
    private $cookieJar;
24
25
    /**
26
     * @param CookieJar $cookieJar
27
     */
28
    public function __construct(CookieJar $cookieJar)
29
    {
30
        $this->cookieJar = $cookieJar;
31
    }
32
33
    /**
34
     * {@inheritdoc}
35
     */
36
    public function handleRequest(RequestInterface $request, callable $next, callable $first)
37
    {
38
        foreach ($this->cookieJar->getCookies() as $cookie) {
39
            if ($cookie->isExpired()) {
40
                continue;
41
            }
42
43
            if (!$cookie->matchDomain($request->getUri()->getHost())) {
44
                continue;
45
            }
46
47
            if (!$cookie->matchPath($request->getUri()->getPath())) {
48
                continue;
49
            }
50
51
            if ($cookie->isSecure() && ($request->getUri()->getScheme() !== 'https')) {
52
                continue;
53
            }
54
55
            $request = $request->withAddedHeader('Cookie', sprintf('%s=%s', $cookie->getName(), $cookie->getValue()));
56
        }
57
58
        return $next($request)->then(function (ResponseInterface $response) use ($request) {
59
            if ($response->hasHeader('Set-Cookie')) {
60
                $setCookies = $response->getHeader('Set-Cookie');
61
62
                foreach ($setCookies as $setCookie) {
63
                    $cookie = $this->createCookie($request, $setCookie);
64
65
                    // Cookie invalid do not use it
66
                    if (null === $cookie) {
67
                        continue;
68
                    }
69
70
                    // Restrict setting cookie from another domain
71
                    if (false === strpos($cookie->getDomain(), $request->getUri()->getHost())) {
72
                        continue;
73
                    }
74
75
                    $this->cookieJar->addCookie($cookie);
76
                }
77
            }
78
79
            return $response;
80
        });
81
    }
82
83
    /**
84
     * Creates a cookie from a string.
85
     *
86
     * @param RequestInterface $request
87
     * @param $setCookie
88
     *
89
     * @return Cookie|null
90
     */
91
    private function createCookie(RequestInterface $request, $setCookie)
92
    {
93
        $parts = array_map('trim', explode(';', $setCookie));
94
95
        if (empty($parts) || !strpos($parts[0], '=')) {
96
            return;
97
        }
98
99
        list($name, $cookieValue) = $this->createValueKey(array_shift($parts));
100
101
        $expires = 0;
102
        $domain = $request->getUri()->getHost();
103
        $path = $request->getUri()->getPath();
104
        $secure = false;
105
        $httpOnly = false;
106
107
        // Add the cookie pieces into the parsed data array
108
        foreach ($parts as $part) {
109
            list($key, $value) = $this->createValueKey($part);
110
111
            switch (strtolower($key)) {
112
                case 'expires':
113
                    $expires = \DateTime::createFromFormat(DATE_COOKIE, $value);
114
                    break;
115
116
                case 'max-age':
117
                    $expires = (new \DateTime())->add(new \DateInterval('PT'.(int) $value.'S'));
118
                    break;
119
120
                case 'domain':
121
                    $domain = $value;
122
                    break;
123
124
                case 'path':
125
                    $path = $value;
126
                    break;
127
128
                case 'secure':
129
                    $secure = true;
130
                    break;
131
132
                case 'httponly':
133
                    $httpOnly = true;
134
                    break;
135
            }
136
        }
137
138
        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 99 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 121 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 125 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...
139
    }
140
141
    /**
142
     * Separates key/value pair from cookie.
143
     *
144
     * @param $part
145
     *
146
     * @return array
147
     */
148
    private function createValueKey($part)
149
    {
150
        $parts = explode('=', $part, 2);
151
        $key = trim($parts[0]);
152
        $value = isset($parts[1]) ? trim($parts[1]) : true;
153
154
        return [$key, $value];
155
    }
156
}
157