Passed
Pull Request — master (#2608)
by Kévin
04:09
created

Response::getStatusCode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
eloc 1
nc 1
nop 0
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 ApiPlatform\Core\Bridge\Symfony\Bundle\Test\Exception\ClientException;
17
use Symfony\Component\BrowserKit\Response as BrowserKitResponse;
18
use Symfony\Component\HttpClient\Exception\JsonException;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, ApiPlatform\Core\Bridge\...ndle\Test\JsonException. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
19
use Symfony\Component\HttpFoundation\Response as HttpFoundationResponse;
20
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
21
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
22
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
23
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
24
use Symfony\Contracts\HttpClient\ResponseInterface;
25
26
/**
27
 * HTTP Response.
28
 *
29
 * @internal
30
 *
31
 * Partially copied from \Symfony\Component\HttpClient\Response\ResponseTrait
32
 *
33
 * @author Kévin Dunglas <[email protected]>
34
 */
35
final class Response implements ResponseInterface
36
{
37
    private $httpFoundationResponse;
38
    private $browserKitResponse;
39
    private $info;
40
    private $content;
41
    private $jsonData;
42
43
    public function __construct(HttpFoundationResponse $httpFoundationResponse, BrowserKitResponse $browserKitResponse, array $info)
44
    {
45
        $this->httpFoundationResponse = $httpFoundationResponse;
46
        $this->browserKitResponse = $browserKitResponse;
47
48
        $this->headers = $httpFoundationResponse->headers->all();
49
        $this->content = $httpFoundationResponse->getContent();
50
        $this->info = $info + [
51
            'http_code' => $httpFoundationResponse->getStatusCode(),
52
            'error' => null,
53
            '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...
54
        ];
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60
    public function getInfo(string $type = null)
61
    {
62
        if ($type) {
63
            return $this->info[$type] ?? null;
64
        }
65
66
        return $this->info;
67
    }
68
69
    /**
70
     * Checks the status, and try to extract message if appropriate.
71
     */
72
    private function checkStatusCode()
73
    {
74
        // TODO: use https://github.com/symfony/symfony/pull/30559 instead
75
        $message = 'An error '.$this->info['http_code'].' occured.';
76
        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...
77
            if ($json = json_decode($this->content, true)) {
78
                // Try to extract the error message from Hydra or RFC 7807 error structures
79
                $message = $json['hydra:description'] ?? $json['hydra:title'] ?? $json['detail'] ?? $json['title'] ?? $message;
80
            }
81
        }
82
83
        if (500 <= $this->info['http_code']) {
84
            throw new ServerException($message, $this->info['http_code']);
85
        }
86
87
        if (400 <= $this->info['http_code']) {
88
            throw new ClientException($message, $this->info['http_code']);
89
        }
90
91
        if (300 <= $this->info['http_code']) {
92
            throw new RedirectionException($message, $this->info['http_code']);
93
        }
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99
    public function getContent(bool $throw = true): string
100
    {
101
        if ($throw) {
102
            $this->checkStatusCode();
103
        }
104
105
        return $this->content;
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111
    public function getStatusCode(): int
112
    {
113
        return $this->info['http_code'];
114
    }
115
116
    /**
117
     * {@inheritdoc}
118
     */
119
    public function getHeaders(bool $throw = true): array
120
    {
121
        if ($throw) {
122
            $this->checkStatusCode();
123
        }
124
125
        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...
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131
    public function toArray(bool $throw = true): array
132
    {
133
        if (null !== $this->jsonData) {
134
            return $this->jsonData;
135
        }
136
137
        if ('' === $content = $this->getContent($throw)) {
138
            throw new class(sprintf('Response body is empty.', $contentType)) extends \Exception implements TransportExceptionInterface {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $contentType does not exist. Did you maybe mean $content?
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));
0 ignored issues
show
Bug introduced by
The constant ApiPlatform\Core\Bridge\...est\JSON_THROW_ON_ERROR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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