Passed
Pull Request — master (#2608)
by Kévin
03:14
created

Response::getInfo()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 7
rs 10
1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace ApiPlatform\Core\Bridge\Symfony\Bundle\Test;
15
16
use Symfony\Component\BrowserKit\Response as BrowserKitResponse;
17
use Symfony\Component\HttpClient\Exception\ClientException;
18
use Symfony\Component\HttpClient\Exception\JsonException;
19
use Symfony\Component\HttpClient\Exception\RedirectionException;
20
use Symfony\Component\HttpClient\Exception\ServerException;
21
use Symfony\Component\HttpFoundation\Response as HttpFoundationResponse;
22
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
23
use Symfony\Contracts\HttpClient\ResponseInterface;
24
25
/**
26
 * HTTP Response.
27
 *
28
 * @internal
29
 *
30
 * Partially copied from \Symfony\Component\HttpClient\Response\ResponseTrait
31
 *
32
 * @author Kévin Dunglas <[email protected]>
33
 */
34
final class Response implements ResponseInterface
35
{
36
    private $httpFoundationResponse;
37
    private $browserKitResponse;
38
    private $info;
39
    private $content;
40
    private $jsonData;
41
42
    public function __construct(HttpFoundationResponse $httpFoundationResponse, BrowserKitResponse $browserKitResponse, array $info)
43
    {
44
        $this->httpFoundationResponse = $httpFoundationResponse;
45
        $this->browserKitResponse = $browserKitResponse;
46
47
        $this->headers = $httpFoundationResponse->headers->all();
48
        $this->content = $httpFoundationResponse->getContent();
49
        $this->info = $info + [
50
            'http_code' => $httpFoundationResponse->getStatusCode(),
51
            'error' => null,
52
            'raw_headers' => $this->headers,
0 ignored issues
show
Bug Best Practice introduced by
The property headers does not exist on ApiPlatform\Core\Bridge\...ny\Bundle\Test\Response. Did you maybe forget to declare it?
Loading history...
53
        ];
54
    }
55
56
    /**
57
     * {@inheritdoc}
58
     */
59
    public function getInfo(string $type = null)
60
    {
61
        if ($type) {
62
            return $this->info[$type] ?? null;
63
        }
64
65
        return $this->info;
66
    }
67
68
    /**
69
     * Checks the status, and try to extract message if appropriate.
70
     */
71
    private function checkStatusCode()
72
    {
73
        // TODO: use https://github.com/symfony/symfony/pull/30559 instead
74
        $message = 'An error '.$this->info['http_code'].' occured.';
75
        if (isset($this->headers['content-type'][0]) && preg_match('/\bjson\b/i', $this->headers['content-type'][0])) {
0 ignored issues
show
Bug Best Practice introduced by
The property headers does not exist on ApiPlatform\Core\Bridge\...ny\Bundle\Test\Response. Did you maybe forget to declare it?
Loading history...
76
            if ($json = json_decode($this->content, true)) {
77
                // Try to extract the error message from Hydra or RFC 7807 error structures
78
                $message = $json['hydra:description'] ?? $json['hydra:title'] ?? $json['detail'] ?? $json['title'] ?? $message;
0 ignored issues
show
Unused Code introduced by
The assignment to $message is dead and can be removed.
Loading history...
79
            }
80
        }
81
82
        if (500 <= $this->info['http_code']) {
83
            throw new ServerException($this);
84
        }
85
86
        if (400 <= $this->info['http_code']) {
87
            throw new ClientException($this);
88
        }
89
90
        if (300 <= $this->info['http_code']) {
91
            throw new RedirectionException($this);
92
        }
93
    }
94
95
    /**
96
     * {@inheritdoc}
97
     */
98
    public function getContent(bool $throw = true): string
99
    {
100
        if ($throw) {
101
            $this->checkStatusCode();
102
        }
103
104
        return $this->content;
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110
    public function getStatusCode(): int
111
    {
112
        return $this->info['http_code'];
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118
    public function getHeaders(bool $throw = true): array
119
    {
120
        if ($throw) {
121
            $this->checkStatusCode();
122
        }
123
124
        return $this->headers;
0 ignored issues
show
Bug Best Practice introduced by
The property headers does not exist on ApiPlatform\Core\Bridge\...ny\Bundle\Test\Response. Did you maybe forget to declare it?
Loading history...
125
    }
126
127
    /**
128
     * {@inheritdoc}
129
     */
130
    public function toArray(bool $throw = true): array
131
    {
132
        if (null !== $this->jsonData) {
133
            return $this->jsonData;
134
        }
135
136
        if ('' === $content = $this->getContent($throw)) {
137
            throw new TransportExceptionInterface($this);
0 ignored issues
show
Unused Code introduced by
The call to Symfony\Contracts\HttpCl...nterface::__construct() has too many arguments starting with $this. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

137
            throw /** @scrutinizer ignore-call */ new TransportExceptionInterface($this);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
138
            throw new class(sprintf('Response body is empty.', $contentType)) extends \Exception implements TransportExceptionInterface {
0 ignored issues
show
Unused Code introduced by
ThrowNode is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
139
            };
140
        }
141
142
        $contentType = $this->headers['content-type'][0] ?? 'application/json';
0 ignored issues
show
Bug Best Practice introduced by
The property headers does not exist on ApiPlatform\Core\Bridge\...ny\Bundle\Test\Response. Did you maybe forget to declare it?
Loading history...
143
144
        if (!preg_match('/\bjson\b/i', $contentType)) {
145
            throw new class(sprintf('Response content-type is "%s" while a JSON-compatible one was expected.', $contentType)) extends \JsonException implements TransportExceptionInterface {
146
            };
147
        }
148
149
        try {
150
            $content = json_decode($content, true, 512, JSON_BIGINT_AS_STRING | (\PHP_VERSION_ID >= 70300 ? JSON_THROW_ON_ERROR : 0));
151
        } catch (\JsonException $e) {
152
            throw new class($e->getMessage(), $e->getCode()) extends \JsonException implements TransportExceptionInterface {
153
            };
154
        }
155
156
        if (\PHP_VERSION_ID < 70300 && JSON_ERROR_NONE !== json_last_error()) {
157
            throw new JsonException(json_last_error_msg(), json_last_error());
158
        }
159
160
        if (!\is_array($content)) {
161
            throw new class(sprintf('JSON content was expected to decode to an array, %s returned.', \gettype($content))) extends \JsonException implements TransportExceptionInterface {
162
            };
163
        }
164
165
        return $this->jsonData = $content;
166
    }
167
168
    /**
169
     * Returns the internal HttpKernel response.
170
     */
171
    public function getKernelResponse(): HttpFoundationResponse
172
    {
173
        return $this->httpFoundationResponse;
174
    }
175
176
    /**
177
     * Returns the internal BrowserKit reponse.
178
     */
179
    public function getBrowserKitResponse(): BrowserKitResponse
180
    {
181
        return $this->browserKitResponse;
182
    }
183
}
184