diff --git a/psalm-baseline.xml b/psalm-baseline.xml index cfa7df05..1c9bade4 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,5 @@ - + $contents @@ -515,60 +515,7 @@ - - $name - $name - $value - $value - $value - $value - $value - $value - $value - $value - $version - - - headersWithInjectionVectors - invalidArrayHeaderValues - invalidGeneralHeaderValues - invalidHeaderValueTypes - invalidHeaderValues - invalidProtocolVersionProvider - numericHeaderValuesProvider - testAddHeaderAppendsToExistingHeader - testBodyMutatorReturnsCloneWithChanges - testCanRemoveHeaders - testDoesNotAllowCRLFInjectionWhenCallingWithAddedHeader - testDoesNotAllowCRLFInjectionWhenCallingWithHeader - testGetHeaderLineReturnsEmptyStringWhenHeaderDoesNotExist - testGetHeaderLineReturnsHeaderValueAsCommaConcatenatedString - testGetHeaderReturnsAnEmptyArrayWhenHeaderDoesNotExist - testGetHeaderReturnsHeaderValueAsArray - testGetHeadersKeepsHeaderCaseSensitivity - testGetHeadersReturnsCaseWithWhichHeaderFirstRegistered - testHasHeaderReturnsFalseIfHeaderIsNotPresent - testHasHeaderReturnsTrueIfHeaderIsPresent - testHeaderRemovalIsCaseInsensitive - testHeadersInitialization - testProtocolHasAcceptableDefault - testProtocolMutatorReturnsCloneWithChanges - testUsesStreamProvidedInConstructorAsBody - testWithAddedHeaderAllowsHeaderContinuations - testWithAddedHeaderRaisesExceptionForNonStringNonArrayValue - testWithHeaderAllowsHeaderContinuations - testWithHeaderRaisesExceptionForInvalidNestedHeaderValue - testWithHeaderRaisesExceptionForInvalidValueType - testWithHeaderReplacesDifferentCapitalization - testWithHeaderShouldAllowIntegersAndFloats - testWithHeaderShouldRaiseExceptionForInvalidHeaderScalarValues - testWithHeaderShouldRaiseExceptionForInvalidHeaderValuesInArrays - testWithProtocolVersionRaisesExceptionForInvalidVersion - testWithoutHeaderDoesNothingIfHeaderDoesNotExist - - - $name - $name + $value $value $value @@ -577,17 +524,11 @@ $value $version - + [ $value ] [ $value ] [ $value ] - - $values['array'] - - - $values - diff --git a/src/MessageTrait.php b/src/MessageTrait.php index e490bd3e..1c240d10 100644 --- a/src/MessageTrait.php +++ b/src/MessageTrait.php @@ -370,7 +370,7 @@ private function validateProtocolVersion($version) : void // HTTP/1 uses a "." numbering scheme to indicate // versions of the protocol, while HTTP/2 does not. - if (! preg_match('#^(1\.[01]|2)$#', $version)) { + if (! preg_match('#^(1\.[01]|2(\.0)?)$#', $version)) { throw new Exception\InvalidArgumentException(sprintf( 'Unsupported HTTP protocol version "%s" provided', $version diff --git a/test/MessageTraitTest.php b/test/MessageTraitTest.php index 38bc57ea..bbb39cef 100644 --- a/test/MessageTraitTest.php +++ b/test/MessageTraitTest.php @@ -24,20 +24,20 @@ protected function setUp() : void $this->message = new Request(null, null, $this->createMock(StreamInterface::class)); } - public function testProtocolHasAcceptableDefault() + public function testProtocolHasAcceptableDefault(): void { $this->assertSame('1.1', $this->message->getProtocolVersion()); } - public function testProtocolMutatorReturnsCloneWithChanges() + public function testProtocolMutatorReturnsCloneWithChanges(): void { $message = $this->message->withProtocolVersion('1.0'); $this->assertNotSame($this->message, $message); $this->assertSame('1.0', $message->getProtocolVersion()); } - - public function invalidProtocolVersionProvider() + /** @return non-empty-array */ + public function invalidProtocolVersionProvider(): array { return [ 'null' => [ null ], @@ -50,14 +50,15 @@ public function invalidProtocolVersionProvider() '1-without-minor' => [ '1' ], '1-with-invalid-minor' => [ '1.2' ], '1-with-hotfix' => [ '1.2.3' ], - '2-with-minor' => [ '2.0' ], ]; } /** * @dataProvider invalidProtocolVersionProvider + * + * @param mixed $version */ - public function testWithProtocolVersionRaisesExceptionForInvalidVersion($version) + public function testWithProtocolVersionRaisesExceptionForInvalidVersion($version): void { $request = new Request(); @@ -66,14 +67,34 @@ public function testWithProtocolVersionRaisesExceptionForInvalidVersion($version $request->withProtocolVersion($version); } - public function testUsesStreamProvidedInConstructorAsBody() + /** @return non-empty-array */ + public function validProtocolVersionProvider(): array + { + return [ + '1.0' => [ '1.0' ], + '1.1' => [ '1.1' ], + '2' => [ '2' ], + '2.0' => [ '2.0' ], + ]; + } + + /** + * @dataProvider validProtocolVersionProvider + */ + public function testWithProtocolVersionDoesntRaiseExceptionForValidVersion(string $version): void + { + $request = (new Request())->withProtocolVersion($version); + $this->assertEquals($version, $request->getProtocolVersion()); + } + + public function testUsesStreamProvidedInConstructorAsBody(): void { $stream = $this->createMock(StreamInterface::class); $message = new Request(null, null, $stream); $this->assertSame($stream, $message->getBody()); } - public function testBodyMutatorReturnsCloneWithChanges() + public function testBodyMutatorReturnsCloneWithChanges(): void { $stream = $this->createMock(StreamInterface::class); $message = $this->message->withBody($stream); @@ -81,28 +102,28 @@ public function testBodyMutatorReturnsCloneWithChanges() $this->assertSame($stream, $message->getBody()); } - public function testGetHeaderReturnsHeaderValueAsArray() + public function testGetHeaderReturnsHeaderValueAsArray(): void { $message = $this->message->withHeader('X-Foo', ['Foo', 'Bar']); $this->assertNotSame($this->message, $message); $this->assertSame(['Foo', 'Bar'], $message->getHeader('X-Foo')); } - public function testGetHeaderLineReturnsHeaderValueAsCommaConcatenatedString() + public function testGetHeaderLineReturnsHeaderValueAsCommaConcatenatedString(): void { $message = $this->message->withHeader('X-Foo', ['Foo', 'Bar']); $this->assertNotSame($this->message, $message); $this->assertSame('Foo,Bar', $message->getHeaderLine('X-Foo')); } - public function testGetHeadersKeepsHeaderCaseSensitivity() + public function testGetHeadersKeepsHeaderCaseSensitivity(): void { $message = $this->message->withHeader('X-Foo', ['Foo', 'Bar']); $this->assertNotSame($this->message, $message); $this->assertSame([ 'X-Foo' => [ 'Foo', 'Bar' ] ], $message->getHeaders()); } - public function testGetHeadersReturnsCaseWithWhichHeaderFirstRegistered() + public function testGetHeadersReturnsCaseWithWhichHeaderFirstRegistered(): void { $message = $this->message ->withHeader('X-Foo', 'Foo') @@ -111,19 +132,19 @@ public function testGetHeadersReturnsCaseWithWhichHeaderFirstRegistered() $this->assertSame([ 'X-Foo' => [ 'Foo', 'Bar' ] ], $message->getHeaders()); } - public function testHasHeaderReturnsFalseIfHeaderIsNotPresent() + public function testHasHeaderReturnsFalseIfHeaderIsNotPresent(): void { $this->assertFalse($this->message->hasHeader('X-Foo')); } - public function testHasHeaderReturnsTrueIfHeaderIsPresent() + public function testHasHeaderReturnsTrueIfHeaderIsPresent(): void { $message = $this->message->withHeader('X-Foo', 'Foo'); $this->assertNotSame($this->message, $message); $this->assertTrue($message->hasHeader('X-Foo')); } - public function testAddHeaderAppendsToExistingHeader() + public function testAddHeaderAppendsToExistingHeader(): void { $message = $this->message->withHeader('X-Foo', 'Foo'); $this->assertNotSame($this->message, $message); @@ -132,7 +153,7 @@ public function testAddHeaderAppendsToExistingHeader() $this->assertSame('Foo,Bar', $message2->getHeaderLine('X-Foo')); } - public function testCanRemoveHeaders() + public function testCanRemoveHeaders(): void { $message = $this->message->withHeader('X-Foo', 'Foo'); $this->assertNotSame($this->message, $message); @@ -143,7 +164,7 @@ public function testCanRemoveHeaders() $this->assertFalse($message2->hasHeader('X-Foo')); } - public function testHeaderRemovalIsCaseInsensitive() + public function testHeaderRemovalIsCaseInsensitive(): void { $message = $this->message ->withHeader('X-Foo', 'Foo') @@ -161,7 +182,8 @@ public function testHeaderRemovalIsCaseInsensitive() $this->assertSame(0, count($headers)); } - public function invalidGeneralHeaderValues() + /** @return non-empty-array */ + public function invalidGeneralHeaderValues(): array { return [ 'null' => [null], @@ -174,8 +196,10 @@ public function invalidGeneralHeaderValues() /** * @dataProvider invalidGeneralHeaderValues + * + * @param mixed $value */ - public function testWithHeaderRaisesExceptionForInvalidNestedHeaderValue($value) + public function testWithHeaderRaisesExceptionForInvalidNestedHeaderValue($value): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid header value'); @@ -183,7 +207,8 @@ public function testWithHeaderRaisesExceptionForInvalidNestedHeaderValue($value) $this->message->withHeader('X-Foo', [ $value ]); } - public function invalidHeaderValues() + /** @return non-empty-array */ + public function invalidHeaderValues(): array { return [ 'null' => [null], @@ -195,8 +220,10 @@ public function invalidHeaderValues() /** * @dataProvider invalidHeaderValues + * + * @param mixed $value */ - public function testWithHeaderRaisesExceptionForInvalidValueType($value) + public function testWithHeaderRaisesExceptionForInvalidValueType($value): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid header value'); @@ -204,7 +231,7 @@ public function testWithHeaderRaisesExceptionForInvalidValueType($value) $this->message->withHeader('X-Foo', $value); } - public function testWithHeaderReplacesDifferentCapitalization() + public function testWithHeaderReplacesDifferentCapitalization(): void { $this->message = $this->message->withHeader('X-Foo', ['foo']); $new = $this->message->withHeader('X-foo', ['bar']); @@ -214,8 +241,10 @@ public function testWithHeaderReplacesDifferentCapitalization() /** * @dataProvider invalidGeneralHeaderValues + * + * @param mixed $value */ - public function testWithAddedHeaderRaisesExceptionForNonStringNonArrayValue($value) + public function testWithAddedHeaderRaisesExceptionForNonStringNonArrayValue($value): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('must be a string'); @@ -223,7 +252,7 @@ public function testWithAddedHeaderRaisesExceptionForNonStringNonArrayValue($val $this->message->withAddedHeader('X-Foo', $value); } - public function testWithoutHeaderDoesNothingIfHeaderDoesNotExist() + public function testWithoutHeaderDoesNothingIfHeaderDoesNotExist(): void { $this->assertFalse($this->message->hasHeader('X-Foo')); $message = $this->message->withoutHeader('X-Foo'); @@ -231,24 +260,25 @@ public function testWithoutHeaderDoesNothingIfHeaderDoesNotExist() $this->assertFalse($message->hasHeader('X-Foo')); } - public function testHeadersInitialization() + public function testHeadersInitialization(): void { $headers = ['X-Foo' => ['bar']]; $message = new Request(null, null, 'php://temp', $headers); $this->assertSame($headers, $message->getHeaders()); } - public function testGetHeaderReturnsAnEmptyArrayWhenHeaderDoesNotExist() + public function testGetHeaderReturnsAnEmptyArrayWhenHeaderDoesNotExist(): void { $this->assertSame([], $this->message->getHeader('X-Foo-Bar')); } - public function testGetHeaderLineReturnsEmptyStringWhenHeaderDoesNotExist() + public function testGetHeaderLineReturnsEmptyStringWhenHeaderDoesNotExist(): void { $this->assertEmpty($this->message->getHeaderLine('X-Foo-Bar')); } - public function headersWithInjectionVectors() + /** @return non-empty-array */ + public function headersWithInjectionVectors(): array { return [ 'name-with-cr' => ["X-Foo\r-Bar", 'value'], @@ -269,8 +299,11 @@ public function headersWithInjectionVectors() /** * @dataProvider headersWithInjectionVectors * @group ZF2015-04 + * + * @param string $name + * @param string|array{string} $value */ - public function testDoesNotAllowCRLFInjectionWhenCallingWithHeader($name, $value) + public function testDoesNotAllowCRLFInjectionWhenCallingWithHeader($name, $value): void { $this->expectException(InvalidArgumentException::class); @@ -280,27 +313,31 @@ public function testDoesNotAllowCRLFInjectionWhenCallingWithHeader($name, $value /** * @dataProvider headersWithInjectionVectors * @group ZF2015-04 + * + * @param string $name + * @param string|array{string} $value */ - public function testDoesNotAllowCRLFInjectionWhenCallingWithAddedHeader($name, $value) + public function testDoesNotAllowCRLFInjectionWhenCallingWithAddedHeader($name, $value): void { $this->expectException(InvalidArgumentException::class); $this->message->withAddedHeader($name, $value); } - public function testWithHeaderAllowsHeaderContinuations() + public function testWithHeaderAllowsHeaderContinuations(): void { $message = $this->message->withHeader('X-Foo-Bar', "value,\r\n second value"); $this->assertSame("value,\r\n second value", $message->getHeaderLine('X-Foo-Bar')); } - public function testWithAddedHeaderAllowsHeaderContinuations() + public function testWithAddedHeaderAllowsHeaderContinuations(): void { $message = $this->message->withAddedHeader('X-Foo-Bar', "value,\r\n second value"); $this->assertSame("value,\r\n second value", $message->getHeaderLine('X-Foo-Bar')); } - public function numericHeaderValuesProvider() + /** @return non-empty-array */ + public function numericHeaderValuesProvider(): array { return [ 'integer' => [ 123 ], @@ -311,8 +348,11 @@ public function numericHeaderValuesProvider() /** * @dataProvider numericHeaderValuesProvider * @group 99 + * + * @psalm-suppress InvalidScalarArgument this test explicitly verifies that pre-type-declaration + * implicit type conversion semantics still apply, for BC Compliance */ - public function testWithHeaderShouldAllowIntegersAndFloats($value) + public function testWithHeaderShouldAllowIntegersAndFloats(float $value): void { $message = $this->message ->withHeader('X-Test-Array', [ $value ]) @@ -324,7 +364,8 @@ public function testWithHeaderShouldAllowIntegersAndFloats($value) ], $message->getHeaders()); } - public function invalidHeaderValueTypes() + /** @return non-empty-array */ + public function invalidHeaderValueTypes(): array { return [ 'null' => [null], @@ -334,7 +375,8 @@ public function invalidHeaderValueTypes() ]; } - public function invalidArrayHeaderValues() + /** @return non-empty-array */ + public function invalidArrayHeaderValues(): array { $values = $this->invalidHeaderValueTypes(); $values['array'] = [['INVALID']]; @@ -344,8 +386,10 @@ public function invalidArrayHeaderValues() /** * @dataProvider invalidArrayHeaderValues * @group 99 + * + * @param mixed $value */ - public function testWithHeaderShouldRaiseExceptionForInvalidHeaderValuesInArrays($value) + public function testWithHeaderShouldRaiseExceptionForInvalidHeaderValuesInArrays($value): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('header value type'); @@ -356,8 +400,10 @@ public function testWithHeaderShouldRaiseExceptionForInvalidHeaderValuesInArrays /** * @dataProvider invalidHeaderValueTypes * @group 99 + * + * @param mixed $value */ - public function testWithHeaderShouldRaiseExceptionForInvalidHeaderScalarValues($value) + public function testWithHeaderShouldRaiseExceptionForInvalidHeaderScalarValues($value): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('header value type');