testAppendVaryHeaderInNormalRequest()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
c 0
b 0
f 0
rs 9.7998
cc 1
nc 1
nop 0
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license.
17
 */
18
19
namespace ZfrCorsTest\Service;
20
21
use PHPUnit\Framework\TestCase as TestCase;
22
use Zend\Http\Response as HttpResponse;
23
use Zend\Http\Request as HttpRequest;
24
use Zend\Mvc\MvcEvent;
25
use Zend\Mvc\Router\Http\RouteMatch as DeprecatedRouteMatch;
26
use Zend\Router\Http\RouteMatch;
27
use ZfrCors\Options\CorsOptions;
28
use ZfrCors\Service\CorsService;
29
30
/**
31
 * Integration tests for {@see \ZfrCors\Service\CorsService}
32
 *
33
 * @author Florent Blaison <[email protected]>
34
 *
35
 * @covers \ZfrCors\Service\CorsService
36
 * @group Coverage
37
 */
38
class CorsServiceTest extends TestCase
39
{
40
    /**
41
     * @var CorsService
42
     */
43
    protected $corsService;
44
45
    /**
46
     * @var HttpResponse
47
     */
48
    protected $response;
49
50
    /**
51
     * @var HttpRequest
52
     */
53
    protected $request;
54
55
    /**
56
     * @var MvcEvent
57
     */
58
    protected $event;
59
60
    /**
61
     * @var CorsOptions
62
     */
63
    protected $corsOptions;
64
65
    /**
66
     * Set up
67
     */
68
    public function setUp(): void
69
    {
70
        parent::setUp();
71
72
        $this->corsOptions = new CorsOptions(
73
            [
74
                'allowed_origins'     => ['http://example.com'],
75
                'allowed_methods'     => ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
76
                'allowed_headers'     => ['Content-Type', 'Accept'],
77
                'exposed_headers'     => ['Location'],
78
                'max_age'             => 10,
79
                'allowed_credentials' => true,
80
            ]
81
        );
82
83
        $this->corsService = new CorsService($this->corsOptions);
84
    }
85
86
    public function testCanDetectCorsRequest()
87
    {
88
        $request = new HttpRequest();
89
90
        $this->assertFalse($this->corsService->isCorsRequest($request));
91
92
        $request->getHeaders()->addHeaderLine('Origin', 'http://example.com');
93
        $this->assertEquals(true, $this->corsService->isCorsRequest($request));
94
    }
95
96
    public function testIsNotCorsRequestIfNotACrossRequest()
97
    {
98
        $request = new HttpRequest();
99
        $request->setUri('http://example.com');
100
101
        $request->getHeaders()->addHeaderLine('Origin', 'http://example.com');
102
        $this->assertEquals(false, $this->corsService->isCorsRequest($request));
103
    }
104
105
    public function testCanDetectPreflightRequest()
106
    {
107
        $request = new HttpRequest();
108
109
        $this->assertFalse($this->corsService->isPreflightRequest($request));
110
111
        $request->setMethod('OPTIONS');
112
        $this->assertFalse($this->corsService->isPreflightRequest($request));
113
114
        $request->getHeaders()->addHeaderLine('Origin', 'http://example.com');
115
        $this->assertFalse($this->corsService->isPreflightRequest($request));
116
117
        $request->getHeaders()->addHeaderLine('Access-Control-Request-Method', 'POST');
118
        $this->assertTrue($this->corsService->isPreflightRequest($request));
119
    }
120
121
    public function testProperlyCreatePreflightResponse()
122
    {
123
        $request  = new HttpRequest();
124
        $request->getHeaders()->addHeaderLine('Origin', 'http://example.com');
125
126
        $response = $this->corsService->createPreflightCorsResponse($request);
127
128
        $headers = $response->getHeaders();
129
130
        $this->assertEquals(200, $response->getStatusCode());
131
        $this->assertEquals('', $response->getContent());
132
        $this->assertEquals('http://example.com', $headers->get('Access-Control-Allow-Origin')->getFieldValue());
0 ignored issues
show
Bug introduced by
The method getFieldValue does only exist in Zend\Http\Header\HeaderInterface, but not in ArrayIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
133
        $this->assertEquals(
134
            'GET, POST, PUT, DELETE, OPTIONS',
135
            $headers->get('Access-Control-Allow-Methods')->getFieldValue()
136
        );
137
        $this->assertEquals('Content-Type, Accept', $headers->get('Access-Control-Allow-Headers')->getFieldValue());
138
        $this->assertEquals(10, $headers->get('Access-Control-Max-Age')->getFieldValue());
139
        $this->assertEquals(0, $headers->get('Content-Length')->getFieldValue());
140
141
        $this->assertEquals('true', $headers->get('Access-Control-Allow-Credentials')->getFieldValue());
142
    }
143
144
    public function testDoesNotAddAllowCredentialsHeadersIfAsked()
145
    {
146
        $request  = new HttpRequest();
147
        $request->getHeaders()->addHeaderLine('Origin', 'http://example.com');
148
        $this->corsOptions->setAllowedCredentials(false);
149
150
        $response = $this->corsService->createPreflightCorsResponse($request);
151
152
        $headers = $response->getHeaders();
153
        $this->assertFalse($headers->has('Access-Control-Allow-Credentials'));
154
    }
155
156
    public function testCanReturnWildCardAllowOrigin()
157
    {
158
        $request  = new HttpRequest();
159
        $request->getHeaders()->addHeaderLine('Origin', 'http://funny-origin.com');
160
        $this->corsOptions->setAllowedOrigins(['*']);
161
162
        $response = $this->corsService->createPreflightCorsResponse($request);
163
164
        $headers = $response->getHeaders();
165
        $this->assertEquals('http://funny-origin.com', $headers->get('Access-Control-Allow-Origin')->getFieldValue());
0 ignored issues
show
Bug introduced by
The method getFieldValue does only exist in Zend\Http\Header\HeaderInterface, but not in ArrayIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
166
    }
167
168
    public function testCanReturnWildCardSubDomainAllowOrigin()
169
    {
170
        $request  = new HttpRequest();
171
        $request->getHeaders()->addHeaderLine('Origin', 'http://subdomain.example.com');
172
        $this->corsOptions->setAllowedOrigins(['*.example.com']);
173
174
        $response = $this->corsService->createPreflightCorsResponse($request);
175
176
        $headers = $response->getHeaders();
177
        $headerValue = $headers->get('Access-Control-Allow-Origin')->getFieldValue();
0 ignored issues
show
Bug introduced by
The method getFieldValue does only exist in Zend\Http\Header\HeaderInterface, but not in ArrayIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
178
        $this->assertEquals('http://subdomain.example.com', $headerValue);
179
    }
180
181
    public function testCanReturnWildCardSubDomainWithSchemeAllowOrigin()
182
    {
183
        $request  = new HttpRequest();
184
        $request->getHeaders()->addHeaderLine('Origin', 'https://subdomain.example.com');
185
        $this->corsOptions->setAllowedOrigins(['https://*.example.com']);
186
187
        $response = $this->corsService->createPreflightCorsResponse($request);
188
189
        $headers = $response->getHeaders();
190
        $headerValue = $headers->get('Access-Control-Allow-Origin')->getFieldValue();
0 ignored issues
show
Bug introduced by
The method getFieldValue does only exist in Zend\Http\Header\HeaderInterface, but not in ArrayIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
191
        $this->assertEquals('https://subdomain.example.com', $headerValue);
192
    }
193
194
    public function testReturnNullForMissMatchedWildcardSubDomainOrigin()
195
    {
196
        $request  = new HttpRequest();
197
        $request->getHeaders()->addHeaderLine('Origin', 'http://subdomain.example.org');
198
        $this->corsOptions->setAllowedOrigins(['*.example.com']);
199
200
        $response = $this->corsService->createPreflightCorsResponse($request);
201
202
        $headers = $response->getHeaders();
203
        $this->assertEquals('null', $headers->get('Access-Control-Allow-Origin')->getFieldValue());
0 ignored issues
show
Bug introduced by
The method getFieldValue does only exist in Zend\Http\Header\HeaderInterface, but not in ArrayIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
204
    }
205
206
    public function testReturnNullForRootDomainOnWildcardSubDomainOrigin()
207
    {
208
        $request  = new HttpRequest();
209
        $request->getHeaders()->addHeaderLine('Origin', 'http://example.com');
210
        $this->corsOptions->setAllowedOrigins(['*.example.com']);
211
212
        $response = $this->corsService->createPreflightCorsResponse($request);
213
214
        $headers = $response->getHeaders();
215
        $this->assertEquals('null', $headers->get('Access-Control-Allow-Origin')->getFieldValue());
0 ignored issues
show
Bug introduced by
The method getFieldValue does only exist in Zend\Http\Header\HeaderInterface, but not in ArrayIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
216
    }
217
218
    public function testReturnNullForDifferentSchemeOnWildcardSubDomainOrigin()
219
    {
220
        $request  = new HttpRequest();
221
        $request->getHeaders()->addHeaderLine('Origin', 'https://example.com');
222
        $this->corsOptions->setAllowedOrigins(['http://*.example.com']);
223
224
        $response = $this->corsService->createPreflightCorsResponse($request);
225
226
        $headers = $response->getHeaders();
227
        $this->assertEquals('null', $headers->get('Access-Control-Allow-Origin')->getFieldValue());
0 ignored issues
show
Bug introduced by
The method getFieldValue does only exist in Zend\Http\Header\HeaderInterface, but not in ArrayIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
228
    }
229
230
    public function testReturnNullForUnknownOrigin()
231
    {
232
        $request  = new HttpRequest();
233
        $request->getHeaders()->addHeaderLine('Origin', 'http://unauthorized-origin.com');
234
235
        $response = $this->corsService->createPreflightCorsResponse($request);
236
237
        $headers = $response->getHeaders();
238
        $this->assertEquals('null', $headers->get('Access-Control-Allow-Origin')->getFieldValue());
0 ignored issues
show
Bug introduced by
The method getFieldValue does only exist in Zend\Http\Header\HeaderInterface, but not in ArrayIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
239
    }
240
241
    public function testEnsureVaryHeaderForNoOrigin()
242
    {
243
        $response = new HttpResponse();
244
245
        $this->corsService->ensureVaryHeader($response);
246
247
        $headers = $response->getHeaders();
248
249
        $this->assertFalse($headers->get('Origin'));
250
        $this->assertNotFalse($headers->get('Vary'));
251
        $this->assertContains('Origin', $headers->get('Vary')->getFieldValue());
0 ignored issues
show
Bug introduced by
The method getFieldValue does only exist in Zend\Http\Header\HeaderInterface, but not in ArrayIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
252
    }
253
254
    public function testEnsureNoVaryHeaderWhenAcceptsAnyOrigin()
255
    {
256
        $response = new HttpResponse();
257
        $corsService = new CorsService(new CorsOptions([
258
            'allowed_origins' => ['*']
259
        ]));
260
261
        $corsService->ensureVaryHeader($response);
262
263
        $headers = $response->getHeaders();
264
265
        $this->assertFalse($headers->get('Origin'));
266
        $this->assertFalse($headers->get('Vary'));
267
    }
268
269
    public function testCanPopulateNormalCorsRequest()
270
    {
271
        $request  = new HttpRequest();
272
        $response = new HttpResponse();
273
274
        $request->getHeaders()->addHeaderLine('Origin', 'http://example.com');
275
276
        $this->corsService->populateCorsResponse($request, $response);
277
278
        $headers = $response->getHeaders();
279
280
        $this->assertEquals('http://example.com', $headers->get('Access-Control-Allow-Origin')->getFieldValue());
0 ignored issues
show
Bug introduced by
The method getFieldValue does only exist in Zend\Http\Header\HeaderInterface, but not in ArrayIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
281
        $this->assertEquals('Location', $headers->get('Access-Control-Expose-Headers')->getFieldValue());
282
    }
283
284
    public function testRefuseNormalCorsRequestIfUnauthorized()
285
    {
286
        $request  = new HttpRequest();
287
        $response = new HttpResponse();
288
289
        $request->getHeaders()->addHeaderLine('Origin', 'http://unauthorized.com');
290
291
        $this->expectException(\ZfrCors\Exception\DisallowedOriginException::class);
292
        $this->expectExceptionMessage('The origin "http://unauthorized.com" is not authorized');
293
294
        $this->corsService->populateCorsResponse($request, $response);
295
    }
296
297
    public function testAddVaryHeaderInNormalRequest()
298
    {
299
        $request  = new HttpRequest();
300
        $response = new HttpResponse();
301
302
        $request->getHeaders()->addHeaderLine('Origin', 'http://example.com');
303
304
        $this->corsService->populateCorsResponse($request, $response);
305
306
        $headers = $response->getHeaders();
307
        $this->assertTrue($headers->has('Vary'));
308
    }
309
310
    public function testAppendVaryHeaderInNormalRequest()
311
    {
312
        $request  = new HttpRequest();
313
        $response = new HttpResponse();
314
315
        $request->getHeaders()->addHeaderLine('Origin', 'http://example.com');
316
        $response->getHeaders()->addHeaderLine('Vary', 'Foo');
317
318
        $this->corsService->populateCorsResponse($request, $response);
319
320
        $headers = $response->getHeaders();
321
        $this->assertTrue($headers->has('Vary'));
322
        $this->assertEquals('Foo, Origin', $headers->get('Vary')->getFieldValue());
0 ignored issues
show
Bug introduced by
The method getFieldValue does only exist in Zend\Http\Header\HeaderInterface, but not in ArrayIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
323
    }
324
325
    public function testPopulatesAllowCredentialsNormalCorsRequest()
326
    {
327
        $request  = new HttpRequest();
328
        $response = new HttpResponse();
329
330
        $request->getHeaders()->addHeaderLine('Origin', 'http://example.com');
331
332
        $this->corsService->populateCorsResponse($request, $response);
333
334
        $headers = $response->getHeaders();
335
336
        $this->assertEquals('true', $headers->get('Access-Control-Allow-Credentials')->getFieldValue());
0 ignored issues
show
Bug introduced by
The method getFieldValue does only exist in Zend\Http\Header\HeaderInterface, but not in ArrayIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
337
    }
338
339
    public function testCanDetectCorsRequestFromSameHostButDifferentPort()
340
    {
341
        $request = new HttpRequest();
342
        $request->setUri('http://example.com');
343
        $request->getHeaders()->addHeaderLine('Origin', 'http://example.com:9000');
344
        $this->assertTrue($this->corsService->isCorsRequest($request));
345
    }
346
347
    public function testCanDetectCorsRequestFromSameHostButDifferentScheme()
348
    {
349
        $request = new HttpRequest();
350
        $request->setUri('https://example.com');
351
        $request->getHeaders()->addHeaderLine('Origin', 'http://example.com');
352
        $this->assertTrue($this->corsService->isCorsRequest($request));
353
    }
354
355
    public function testCanHandleUnconfiguredRouteMatch()
356
    {
357
        $routeMatch = class_exists(DeprecatedRouteMatch::class) ? new DeprecatedRouteMatch([]) : new RouteMatch([]);
358
359
        $request = new HttpRequest();
360
        $request->getHeaders()->addHeaderLine('Origin', 'http://example.com');
361
        $response = $this->corsService->createPreflightCorsResponseWithRouteOptions($request, $routeMatch);
362
363
        $headers = $response->getHeaders();
364
365
        $this->assertEquals(200, $response->getStatusCode());
366
        $this->assertEquals('', $response->getContent());
367
        $this->assertEquals('http://example.com', $headers->get('Access-Control-Allow-Origin')->getFieldValue());
0 ignored issues
show
Bug introduced by
The method getFieldValue does only exist in Zend\Http\Header\HeaderInterface, but not in ArrayIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
368
        $this->assertEquals(
369
            'GET, POST, PUT, DELETE, OPTIONS',
370
            $headers->get('Access-Control-Allow-Methods')->getFieldValue()
371
        );
372
        $this->assertEquals('Content-Type, Accept', $headers->get('Access-Control-Allow-Headers')->getFieldValue());
373
        $this->assertEquals(10, $headers->get('Access-Control-Max-Age')->getFieldValue());
374
        $this->assertEquals(0, $headers->get('Content-Length')->getFieldValue());
375
376
        $this->assertEquals('true', $headers->get('Access-Control-Allow-Credentials')->getFieldValue());
377
    }
378
379
    public function testCanHandleConfiguredRouteMatch()
380
    {
381
382
        $routeMatchParameters = [
383
            CorsOptions::ROUTE_PARAM => [
384
                'allowed_origins'     => ['http://example.org'],
385
                'allowed_methods'     => ['POST', 'DELETE', 'OPTIONS'],
386
                'allowed_headers'     => ['Content-Type', 'Accept', 'Cookie'],
387
                'exposed_headers'     => ['Location'],
388
                'max_age'             => 5,
389
                'allowed_credentials' => false,
390
            ],
391
        ];
392
393
        $routeMatch = class_exists(DeprecatedRouteMatch::class) ? new DeprecatedRouteMatch($routeMatchParameters) :
394
            new RouteMatch($routeMatchParameters);
395
396
        $request = new HttpRequest();
397
        $request->getHeaders()->addHeaderLine('Origin', 'http://example.org');
398
        $response = $this->corsService->createPreflightCorsResponseWithRouteOptions($request, $routeMatch);
399
400
        $headers = $response->getHeaders();
401
        $this->assertEquals(200, $response->getStatusCode());
402
        $this->assertEquals('', $response->getContent());
403
        $this->assertEquals('http://example.org', $headers->get('Access-Control-Allow-Origin')->getFieldValue());
0 ignored issues
show
Bug introduced by
The method getFieldValue does only exist in Zend\Http\Header\HeaderInterface, but not in ArrayIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
404
        $this->assertEquals(
405
            'POST, DELETE, OPTIONS',
406
            $headers->get('Access-Control-Allow-Methods')->getFieldValue()
407
        );
408
        $this->assertEquals(
409
            'Content-Type, Accept, Cookie',
410
            $headers->get('Access-Control-Allow-Headers')->getFieldValue()
411
        );
412
        $this->assertEquals(5, $headers->get('Access-Control-Max-Age')->getFieldValue());
413
        $this->assertEquals(0, $headers->get('Content-Length')->getFieldValue());
414
415
        $this->assertFalse($headers->has('Access-Control-Allow-Credentials'));
416
    }
417
418
    /**
419
     * @see https://github.com/zf-fr/zfr-cors/issues/44
420
     */
421
    public function testDoesNotCrashApplicationOnInvalidOriginValue()
422
    {
423
        $request = new HttpRequest();
424
        $request->setUri('https://example.com');
425
        $request->getHeaders()->addHeaderLine('Origin', 'file:');
426
        $this->expectException(\ZfrCors\Exception\InvalidOriginException::class);
427
        $this->corsService->isCorsRequest($request);
428
    }
429
430
    /**
431
     * @see https://github.com/zf-fr/zfr-cors/issues/57
432
     */
433
    public function testCanPopulateNormalCorsRequestWithRouteMatch()
434
    {
435
        $request  = new HttpRequest();
436
        $response = new HttpResponse();
437
        $routeMatchParameters = [
438
            CorsOptions::ROUTE_PARAM => [
439
                'allowed_origins'     => ['http://example.org']
440
            ],
441
        ];
442
443
        $routeMatch = class_exists(DeprecatedRouteMatch::class) ? new DeprecatedRouteMatch($routeMatchParameters) :
444
            new RouteMatch($routeMatchParameters);
445
446
        $request->getHeaders()->addHeaderLine('Origin', 'http://example.org');
447
448
        $response = $this->corsService->populateCorsResponse($request, $response, $routeMatch);
0 ignored issues
show
Bug introduced by
It seems like $routeMatch defined by class_exists(\Zend\Mvc\R...($routeMatchParameters) on line 443 can also be of type object<Zend\Mvc\Router\Http\RouteMatch>; however, ZfrCors\Service\CorsServ...:populateCorsResponse() does only seem to accept null|object<Zend\Router\Http\RouteMatch>, 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...
449
        $this->assertInstanceOf(\Zend\Http\Response::class, $response);
450
        $this->assertEquals(
451
            'http://example.org',
452
            $response->getHeaders()->get('Access-Control-Allow-Origin')->getFieldValue()
0 ignored issues
show
Bug introduced by
The method getFieldValue does only exist in Zend\Http\Header\HeaderInterface, but not in ArrayIterator.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
453
        );
454
    }
455
456
    /**
457
     * @see https://github.com/zf-fr/zfr-cors/issues/57
458
     */
459
    public function testCanPopulateNormalCorsRequestWithRouteMatchRewriteException()
460
    {
461
        $request  = new HttpRequest();
462
        $response = new HttpResponse();
463
        $routeMatchParameters = [
464
            CorsOptions::ROUTE_PARAM => [
465
                'allowed_origins'     => ['http://example.org']
466
            ],
467
        ];
468
469
        $routeMatch = class_exists(DeprecatedRouteMatch::class) ? new DeprecatedRouteMatch($routeMatchParameters) :
470
            new RouteMatch($routeMatchParameters);
471
472
        $request->getHeaders()->addHeaderLine('Origin', 'http://example.com');
473
474
        $this->expectException(\ZfrCors\Exception\DisallowedOriginException::class);
475
        $this->corsService->populateCorsResponse($request, $response, $routeMatch);
0 ignored issues
show
Bug introduced by
It seems like $routeMatch defined by class_exists(\Zend\Mvc\R...($routeMatchParameters) on line 469 can also be of type object<Zend\Mvc\Router\Http\RouteMatch>; however, ZfrCors\Service\CorsServ...:populateCorsResponse() does only seem to accept null|object<Zend\Router\Http\RouteMatch>, 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...
476
    }
477
}
478