Passed
Pull Request — master (#3418)
by
unknown
04:22
created

Client::getCookieJar()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
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\Bundle\FrameworkBundle\KernelBrowser;
17
use Symfony\Component\BrowserKit\CookieJar;
18
use Symfony\Component\DependencyInjection\ContainerInterface;
19
use Symfony\Component\HttpClient\HttpClientTrait;
20
use Symfony\Component\HttpKernel\KernelInterface;
21
use Symfony\Component\HttpKernel\Profiler\Profile;
22
use Symfony\Contracts\HttpClient\HttpClientInterface;
23
use Symfony\Contracts\HttpClient\ResponseInterface;
24
use Symfony\Contracts\HttpClient\ResponseStreamInterface;
25
26
/**
27
 * Convenient test client that makes requests to a Kernel object.
28
 *
29
 * @experimental
30
 *
31
 * @author Kévin Dunglas <[email protected]>
32
 */
33
final class Client implements HttpClientInterface
34
{
35
    use HttpClientTrait;
36
37
    /**
38
     * @see HttpClientInterface::OPTIONS_DEFAULTS
39
     */
40
    public const API_OPTIONS_DEFAULTS = [
41
        'auth_basic' => null,
42
        'auth_bearer' => null,
43
        'query' => [],
44
        'headers' => ['accept' => ['application/ld+json']],
45
        'body' => '',
46
        'json' => null,
47
        'base_uri' => 'http://example.com',
48
    ];
49
50
    private $kernelBrowser;
51
52
    private $defaultOptions = self::API_OPTIONS_DEFAULTS;
53
54
    /**
55
     * @var Response
56
     */
57
    private $response;
58
59
    /**
60
     * @param array $defaultOptions Default options for the requests
61
     *
62
     * @see HttpClientInterface::OPTIONS_DEFAULTS for available options
63
     */
64
    public function __construct(KernelBrowser $kernelBrowser, array $defaultOptions = [])
65
    {
66
        $this->kernelBrowser = $kernelBrowser;
67
        $kernelBrowser->followRedirects(false);
68
        if ($defaultOptions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $defaultOptions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
69
            $this->setDefaultOptions($defaultOptions);
70
        }
71
    }
72
73
    /**
74
     * Sets the default options for the requests.
75
     *
76
     * @see HttpClientInterface::OPTIONS_DEFAULTS for available options
77
     */
78
    public function setDefaultOptions(array $defaultOptions): void
79
    {
80
        [, $this->defaultOptions] = self::prepareRequest(null, null, $defaultOptions, self::API_OPTIONS_DEFAULTS);
81
    }
82
83
    /**
84
     * {@inheritdoc}
85
     *
86
     * @return Response
87
     */
88
    public function request(string $method, string $url, array $options = []): ResponseInterface
89
    {
90
        $basic = $options['auth_basic'] ?? null;
91
        [$url, $options] = self::prepareRequest($method, $url, $options, $this->defaultOptions);
92
        $resolvedUrl = implode('', $url);
93
        $server = [];
94
95
        // Convert headers to a $_SERVER-like array
96
        foreach (self::extractHeaders($options) as $key => $value) {
97
            if ('content-type' === $key) {
98
                $server['CONTENT_TYPE'] = $value[0] ?? '';
99
100
                continue;
101
            }
102
103
            // BrowserKit doesn't support setting several headers with the same name
104
            $server['HTTP_'.strtoupper(str_replace('-', '_', $key))] = $value[0] ?? '';
105
        }
106
107
        if ($basic) {
108
            $credentials = \is_array($basic) ? $basic : explode(':', $basic, 2);
109
            $server['PHP_AUTH_USER'] = $credentials[0];
110
            $server['PHP_AUTH_PW'] = $credentials[1] ?? '';
111
        }
112
113
        $info = [
114
            'response_headers' => [],
115
            'redirect_count' => 0,
116
            'redirect_url' => null,
117
            'start_time' => 0.0,
118
            'http_method' => $method,
119
            'http_code' => 0,
120
            'error' => null,
121
            'user_data' => $options['user_data'] ?? null,
122
            'url' => $resolvedUrl,
123
            'primary_port' => 'http:' === $url['scheme'] ? 80 : 443,
124
        ];
125
        $this->kernelBrowser->request($method, $resolvedUrl, [], [], $server, $options['body'] ?? null);
126
127
        return $this->response = new Response($this->kernelBrowser->getResponse(), $this->kernelBrowser->getInternalResponse(), $info);
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function stream($responses, float $timeout = null): ResponseStreamInterface
134
    {
135
        throw new \LogicException('Not implemented yet');
136
    }
137
138
    /**
139
     * Gets the latest response.
140
     *
141
     * @internal
142
     */
143
    public function getResponse(): ?Response
144
    {
145
        return $this->response;
146
    }
147
148
    /**
149
     * Gets the underlying test client.
150
     *
151
     * @internal
152
     */
153
    public function getKernelBrowser(): KernelBrowser
154
    {
155
        return $this->kernelBrowser;
156
    }
157
158
    // The following methods are proxy methods for KernelBrowser's ones
159
160
    /**
161
     * Returns the container.
162
     *
163
     * @return ContainerInterface|null Returns null when the Kernel has been shutdown or not started yet
164
     */
165
    public function getContainer(): ?ContainerInterface
166
    {
167
        return $this->kernelBrowser->getContainer();
168
    }
169
170
    /**
171
     * Returns the CookieJar instance.
172
     *
173
     * @return CookieJar A CookieJar instance
174
     */
175
    public function getCookieJar(): CookieJar
176
    {
177
        return $this->kernelBrowser->getCookieJar();
178
    }
179
180
    /**
181
     * Returns the kernel.
182
     */
183
    public function getKernel(): KernelInterface
184
    {
185
        return $this->kernelBrowser->getKernel();
186
    }
187
188
    /**
189
     * Gets the profile associated with the current Response.
190
     *
191
     * @return Profile|false A Profile instance
192
     */
193
    public function getProfile()
194
    {
195
        return $this->kernelBrowser->getProfile();
196
    }
197
198
    /**
199
     * Enables the profiler for the very next request.
200
     *
201
     * If the profiler is not enabled, the call to this method does nothing.
202
     */
203
    public function enableProfiler(): void
204
    {
205
        $this->kernelBrowser->enableProfiler();
206
    }
207
208
    /**
209
     * Disables kernel reboot between requests.
210
     *
211
     * By default, the Client reboots the Kernel for each request. This method
212
     * allows to keep the same kernel across requests.
213
     */
214
    public function disableReboot(): void
215
    {
216
        $this->kernelBrowser->disableReboot();
217
    }
218
219
    /**
220
     * Enables kernel reboot between requests.
221
     */
222
    public function enableReboot(): void
223
    {
224
        $this->kernelBrowser->enableReboot();
225
    }
226
227
    /**
228
     * Extracts headers depending on the symfony/http-client version being used.
229
     *
230
     * @return array<string, string[]>
231
     */
232
    private static function extractHeaders(array $options): array
233
    {
234
        if (!isset($options['normalized_headers'])) {
235
            return $options['headers'];
236
        }
237
238
        $headers = [];
239
240
        /** @var string $key */
241
        foreach ($options['normalized_headers'] as $key => $values) {
242
            foreach ($values as $value) {
243
                [, $value] = explode(': ', $value, 2);
244
                $headers[$key][] = $value;
245
            }
246
        }
247
248
        return $headers;
249
    }
250
}
251