From 370845d924dfd2610c67c502efeb0963c0556038 Mon Sep 17 00:00:00 2001 From: Dan Hemberger Date: Wed, 27 Jul 2022 20:29:17 -0700 Subject: [PATCH 1/2] Fix parsing nullable collections Fixes #163. Fixes phpDocumentor/phpDocumentor#3290. Shorthand nullable type syntax is now supported for collection types (e.g. `?array`). This also prevents a misuse of the shorthand nullable type syntax with compound types, which is illegal with PHP types. This is documented in the Union Types 2.0 RFC (https://wiki.php.net/rfc/union_types_v2) by N. Popov: > Union types and the ?T nullable type notation cannot be mixed. > Writing ?T1|T2, T1|?T2 or ?(T1|T2) is not supported and T1|T2|null > needs to be used instead. --- src/TypeResolver.php | 7 +------ tests/unit/CollectionResolverTest.php | 17 +++++++++++++++++ tests/unit/TypeResolverTest.php | 4 ++-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/TypeResolver.php b/src/TypeResolver.php index ca36026..57785d6 100644 --- a/src/TypeResolver.php +++ b/src/TypeResolver.php @@ -293,13 +293,8 @@ private function parseTypes(ArrayIterator $tokens, Context $context, int $parser $tokens->next(); } else { - $type = $this->resolveSingleType($token, $context); + $types[] = $this->resolveSingleType($token, $context); $tokens->next(); - if ($parserContext === self::PARSER_IN_NULLABLE) { - return $type; - } - - $types[] = $type; } } diff --git a/tests/unit/CollectionResolverTest.php b/tests/unit/CollectionResolverTest.php index 379aafe..40c337e 100644 --- a/tests/unit/CollectionResolverTest.php +++ b/tests/unit/CollectionResolverTest.php @@ -328,4 +328,21 @@ public function testResolvingList(): void $this->assertInstanceOf(Types\String_::class, $valueType); $this->assertInstanceOf(Types\Integer::class, $keyType); } + + /** + * @uses \phpDocumentor\Reflection\Types\Context + * @uses \phpDocumentor\Reflection\Types\Nullable + * + * @covers ::__construct + * @covers ::resolve + */ + public function testResolvingNullableArray(): void + { + $fixture = new TypeResolver(); + + $resolvedType = $fixture->resolve('?array', new Context('')); + + $this->assertInstanceOf(Types\Nullable::class, $resolvedType); + $this->assertSame('?int[]', (string) $resolvedType); + } } diff --git a/tests/unit/TypeResolverTest.php b/tests/unit/TypeResolverTest.php index 6fd282f..6b3a4f2 100644 --- a/tests/unit/TypeResolverTest.php +++ b/tests/unit/TypeResolverTest.php @@ -407,9 +407,9 @@ public function testResolvingNullableCompoundTypes(): void { $fixture = new TypeResolver(); + $this->expectException('RuntimeException'); + $this->expectExceptionMessage('Unexpected type separator'); $resolvedType = $fixture->resolve('?string|null|?boolean'); - - $this->assertSame('?string|null|?bool', (string) $resolvedType); } /** From daf6ab446d412399277f695b5d975836403026a0 Mon Sep 17 00:00:00 2001 From: Dan Hemberger Date: Fri, 29 Jul 2022 00:55:17 -0700 Subject: [PATCH 2/2] Restore support for shorthand nullable compounds While PHP types do not support shorthand nullable syntax in compound types (e.g. `?string|int`), it has been requested to retain support for this in phpdoc types for backwards compatibility reasons. --- src/TypeResolver.php | 2 ++ tests/unit/TypeResolverTest.php | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/TypeResolver.php b/src/TypeResolver.php index 57785d6..f8bbae8 100644 --- a/src/TypeResolver.php +++ b/src/TypeResolver.php @@ -210,6 +210,7 @@ private function parseTypes(ArrayIterator $tokens, Context $context, int $parser self::PARSER_IN_COMPOUND, self::PARSER_IN_ARRAY_EXPRESSION, self::PARSER_IN_COLLECTION_EXPRESSION, + self::PARSER_IN_NULLABLE, ], true) ) { throw new RuntimeException( @@ -225,6 +226,7 @@ private function parseTypes(ArrayIterator $tokens, Context $context, int $parser self::PARSER_IN_COMPOUND, self::PARSER_IN_ARRAY_EXPRESSION, self::PARSER_IN_COLLECTION_EXPRESSION, + self::PARSER_IN_NULLABLE, ], true) ) { throw new RuntimeException( diff --git a/tests/unit/TypeResolverTest.php b/tests/unit/TypeResolverTest.php index 6b3a4f2..47557a6 100644 --- a/tests/unit/TypeResolverTest.php +++ b/tests/unit/TypeResolverTest.php @@ -407,9 +407,11 @@ public function testResolvingNullableCompoundTypes(): void { $fixture = new TypeResolver(); - $this->expectException('RuntimeException'); - $this->expectExceptionMessage('Unexpected type separator'); + // Note that in PHP types it is illegal to use shorthand nullable + // syntax with unions. This would be 'string|boolean|null' instead. $resolvedType = $fixture->resolve('?string|null|?boolean'); + + $this->assertSame('?string|null|?bool', (string) $resolvedType); } /**