Completed
Push — master ( 0b9ce6...5496bf )
by David
03:14
created

CookiePlugin::handleRequest()   C

Complexity

Conditions 12
Paths 12

Size

Total Lines 51

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 12.0654

Importance

Changes 0
Metric Value
dl 0
loc 51
ccs 24
cts 26
cp 0.9231
rs 6.9666
c 0
b 0
f 0
cc 12
nc 12
nop 3
crap 12.0654

How to fix   Long Method    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\Client\Exception\TransferException;
7
use Http\Message\Cookie;
8
use Http\Message\CookieJar;
9
use Http\Message\CookieUtil;
10
use Http\Message\Exception\UnexpectedValueException;
11
use Psr\Http\Message\RequestInterface;
12
use Psr\Http\Message\ResponseInterface;
13
14
/**
15
 * Handle request cookies.
16
 *
17
 * @author Joel Wurtz <[email protected]>
18
 */
19
final class CookiePlugin implements Plugin
20
{
21
    /**
22
     * Cookie storage.
23
     *
24
     * @var CookieJar
25
     */
26
    private $cookieJar;
27
28
    /**
29
     * @param CookieJar $cookieJar
30
     */
31 13
    public function __construct(CookieJar $cookieJar)
32
    {
33 13
        $this->cookieJar = $cookieJar;
34 13
    }
35
36
    /**
37
     * {@inheritdoc}
38
     */
39 11
    public function handleRequest(RequestInterface $request, callable $next, callable $first)
40
    {
41 11
        $cookies = [];
42 11
        foreach ($this->cookieJar->getCookies() as $cookie) {
43 9
            if ($cookie->isExpired()) {
44 1
                continue;
45
            }
46
47 8
            if (!$cookie->matchDomain($request->getUri()->getHost())) {
48 2
                continue;
49
            }
50
51 6
            if (!$cookie->matchPath($request->getUri()->getPath())) {
52 1
                continue;
53
            }
54
55 5
            if ($cookie->isSecure() && ('https' !== $request->getUri()->getScheme())) {
56 1
                continue;
57
            }
58
59 4
            $cookies[] = sprintf('%s=%s', $cookie->getName(), $cookie->getValue());
60
        }
61
62 11
        if (!empty($cookies)) {
63 4
            $request = $request->withAddedHeader('Cookie', implode('; ', array_unique($cookies)));
64
        }
65
66 11
        return $next($request)->then(function (ResponseInterface $response) use ($request) {
67 2
            if ($response->hasHeader('Set-Cookie')) {
68 2
                $setCookies = $response->getHeader('Set-Cookie');
69
70 2
                foreach ($setCookies as $setCookie) {
71 2
                    $cookie = $this->createCookie($request, $setCookie);
72
73
                    // Cookie invalid do not use it
74 1
                    if (null === $cookie) {
75
                        continue;
76
                    }
77
78
                    // Restrict setting cookie from another domain
79 1
                    if (!preg_match("/\.{$cookie->getDomain()}$/", '.'.$request->getUri()->getHost())) {
80
                        continue;
81
                    }
82
83 1
                    $this->cookieJar->addCookie($cookie);
84
                }
85
            }
86
87 1
            return $response;
88 11
        });
89
    }
90
91
    /**
92
     * Creates a cookie from a string.
93
     *
94
     * @param RequestInterface $request
95
     * @param $setCookie
96
     *
97
     * @return Cookie|null
98
     *
99
     * @throws TransferException
100
     */
101 2
    private function createCookie(RequestInterface $request, $setCookie)
102
    {
103 2
        $parts = array_map('trim', explode(';', $setCookie));
104
105 2
        if (empty($parts) || !strpos($parts[0], '=')) {
106
            return;
107
        }
108
109 2
        list($name, $cookieValue) = $this->createValueKey(array_shift($parts));
110
111 2
        $maxAge = null;
112 2
        $expires = null;
113 2
        $domain = $request->getUri()->getHost();
114 2
        $path = $request->getUri()->getPath();
115 2
        $secure = false;
116 2
        $httpOnly = false;
117
118
        // Add the cookie pieces into the parsed data array
119 2
        foreach ($parts as $part) {
120 2
            list($key, $value) = $this->createValueKey($part);
121
122 2
            switch (strtolower($key)) {
123 2
                case 'expires':
124
                    try {
125 2
                        $expires = CookieUtil::parseDate($value);
0 ignored issues
show
Bug introduced by
It seems like $value defined by $this->createValueKey($part) on line 120 can also be of type boolean; however, Http\Message\CookieUtil::parseDate() does only seem to accept string, 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...
126 1
                    } catch (UnexpectedValueException $e) {
127 1
                        throw new TransferException(
128 1
                            sprintf(
129 1
                                'Cookie header `%s` expires value `%s` could not be converted to date',
130 1
                                $name,
131 1
                                $value
132
                            ),
133 1
                            null,
134 1
                            $e
135
                        );
136
                    }
137
138 1
                    break;
139
140 1
                case 'max-age':
141 1
                    $maxAge = (int) $value;
142
143 1
                    break;
144
145 1
                case 'domain':
146 1
                    $domain = $value;
147
148 1
                    break;
149
150 1
                case 'path':
151 1
                    $path = $value;
152
153 1
                    break;
154
155 1
                case 'secure':
156 1
                    $secure = true;
157
158 1
                    break;
159
160 1
                case 'httponly':
161 1
                    $httpOnly = true;
162
163 1
                    break;
164
            }
165
        }
166
167 1
        return new Cookie($name, $cookieValue, $maxAge, $domain, $path, $secure, $httpOnly, $expires);
0 ignored issues
show
Bug introduced by
It seems like $cookieValue defined by $this->createValueKey(array_shift($parts)) on line 109 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 146 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 151 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...
168
    }
169
170
    /**
171
     * Separates key/value pair from cookie.
172
     *
173
     * @param $part
174
     *
175
     * @return array
176
     */
177 2
    private function createValueKey($part)
178
    {
179 2
        $parts = explode('=', $part, 2);
180 2
        $key = trim($parts[0]);
181 2
        $value = isset($parts[1]) ? trim($parts[1]) : true;
182
183 2
        return [$key, $value];
184
    }
185
}
186