Skip to content

Commit 2dc9aff

Browse files
authored
Merge pull request #176 from phpDocumentor/fix_support_legacy_formats
Fix regression in main branch
2 parents 662b17f + ebe0a19 commit 2dc9aff

File tree

4 files changed

+117
-42
lines changed

4 files changed

+117
-42
lines changed

phpunit.xml.dist

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
bootstrap="vendor/autoload.php"
55
colors="true"
66
verbose="true"
7-
convertDeprecationsToExceptions="true"
7+
convertDeprecationsToExceptions="false"
88
forceCoversAnnotation="true"
99
>
1010
<testsuites>

src/PseudoTypes/ConstExpression.php

+3-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
namespace phpDocumentor\Reflection\PseudoTypes;
1515

16-
use phpDocumentor\Reflection\Fqsen;
1716
use phpDocumentor\Reflection\PseudoType;
1817
use phpDocumentor\Reflection\Type;
1918
use phpDocumentor\Reflection\Types\Mixed_;
@@ -23,16 +22,16 @@
2322
/** @psalm-immutable */
2423
final class ConstExpression implements PseudoType
2524
{
26-
private Fqsen $owner;
25+
private Type $owner;
2726
private string $expression;
2827

29-
public function __construct(Fqsen $owner, string $expression)
28+
public function __construct(Type $owner, string $expression)
3029
{
3130
$this->owner = $owner;
3231
$this->expression = $expression;
3332
}
3433

35-
public function getOwner(): Fqsen
34+
public function getOwner(): Type
3635
{
3736
return $this->owner;
3837
}

src/TypeResolver.php

+55-9
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,11 @@
102102
use function sprintf;
103103
use function strpos;
104104
use function strtolower;
105+
use function trigger_error;
105106
use function trim;
106107

108+
use const E_USER_DEPRECATED;
109+
107110
final class TypeResolver
108111
{
109112
/** @var string Definition of the NAMESPACE operator in PHP */
@@ -200,15 +203,13 @@ public function resolve(string $type, ?Context $context = null): Type
200203
$context = new Context('');
201204
}
202205

203-
try {
204-
$tokens = $this->lexer->tokenize($type);
205-
$tokenIterator = new TokenIterator($tokens);
206-
$ast = $this->typeParser->parse($tokenIterator);
207-
} catch (ParserException $e) {
208-
throw new RuntimeException($e->getMessage(), 0, $e);
209-
}
206+
$tokens = $this->lexer->tokenize($type);
207+
$tokenIterator = new TokenIterator($tokens);
208+
209+
$ast = $this->parse($tokenIterator);
210+
$type = $this->createType($ast, $context);
210211

211-
return $this->createType($ast, $context);
212+
return $this->tryParseRemainingCompoundTypes($tokenIterator, $context, $type);
212213
}
213214

214215
public function createType(?TypeNode $type, Context $context): Type
@@ -403,7 +404,7 @@ private function createFromConst(ConstTypeNode $type, Context $context): Type
403404

404405
case $type->constExpr instanceof ConstFetchNode:
405406
return new ConstExpression(
406-
$this->fqsenResolver->resolve($type->constExpr->className, $context),
407+
$this->resolve($type->constExpr->className, $context),
407408
$type->constExpr->name
408409
);
409410

@@ -551,4 +552,49 @@ private function createArray(array $typeNodes, Context $context): Array_
551552

552553
throw new RuntimeException('An array can have only integers or strings as keys');
553554
}
555+
556+
private function parse(TokenIterator $tokenIterator): TypeNode
557+
{
558+
try {
559+
$ast = $this->typeParser->parse($tokenIterator);
560+
} catch (ParserException $e) {
561+
throw new RuntimeException($e->getMessage(), 0, $e);
562+
}
563+
564+
return $ast;
565+
}
566+
567+
/**
568+
* Will try to parse unsupported type notations by phpstan
569+
*
570+
* The phpstan parser doesn't support the illegal nullable combinations like this library does.
571+
* This method will warn the user about those notations but for bc purposes we will still have it here.
572+
*/
573+
private function tryParseRemainingCompoundTypes(TokenIterator $tokenIterator, Context $context, Type $type): Type
574+
{
575+
trigger_error(
576+
'Legacy nullable type detected, please update your code as
577+
you are using nullable types in a docblock. support will be removed in v2.0.0',
578+
E_USER_DEPRECATED
579+
);
580+
$continue = true;
581+
while ($continue) {
582+
$continue = false;
583+
while ($tokenIterator->tryConsumeTokenType(Lexer::TOKEN_UNION)) {
584+
$ast = $this->parse($tokenIterator);
585+
$type2 = $this->createType($ast, $context);
586+
$type = new Compound([$type, $type2]);
587+
$continue = true;
588+
}
589+
590+
while ($tokenIterator->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) {
591+
$ast = $this->typeParser->parse($tokenIterator);
592+
$type2 = $this->createType($ast, $context);
593+
$type = new Intersection([$type, $type2]);
594+
$continue = true;
595+
}
596+
}
597+
598+
return $type;
599+
}
554600
}

tests/unit/TypeResolverTest.php

+58-28
Original file line numberDiff line numberDiff line change
@@ -424,32 +424,6 @@ public function testResolvingCompoundTypedArrayTypes(): void
424424
$this->assertInstanceOf(Object_::class, $secondType->getValueType());
425425
}
426426

427-
/**
428-
* @uses \phpDocumentor\Reflection\Types\Context
429-
* @uses \phpDocumentor\Reflection\Types\Compound
430-
* @uses \phpDocumentor\Reflection\Types\String_
431-
* @uses \phpDocumentor\Reflection\Types\Nullable
432-
* @uses \phpDocumentor\Reflection\Types\Null_
433-
* @uses \phpDocumentor\Reflection\Types\Boolean
434-
* @uses \phpDocumentor\Reflection\Fqsen
435-
* @uses \phpDocumentor\Reflection\FqsenResolver
436-
*
437-
* @covers ::__construct
438-
* @covers ::resolve
439-
* @covers ::<private>
440-
*/
441-
public function testResolvingNullableCompoundTypes(): void
442-
{
443-
$this->markTestSkipped('Invalid type definition');
444-
$fixture = new TypeResolver();
445-
446-
// Note that in PHP types it is illegal to use shorthand nullable
447-
// syntax with unions. This would be 'string|boolean|null' instead.
448-
$resolvedType = $fixture->resolve('?string|null|?boolean');
449-
450-
$this->assertSame('?string|null|?bool', (string) $resolvedType);
451-
}
452-
453427
/**
454428
* @uses \phpDocumentor\Reflection\Types\Context
455429
* @uses \phpDocumentor\Reflection\Types\Compound
@@ -876,6 +850,7 @@ public function testArrayKeyValueSpecification(): void
876850
* @dataProvider genericsProvider
877851
* @dataProvider callableProvider
878852
* @dataProvider constExpressions
853+
* @dataProvider illegalLegacyFormatProvider
879854
* @testdox create type from $type
880855
*/
881856
public function testTypeBuilding(string $type, Type $expected): void
@@ -1093,11 +1068,66 @@ public function constExpressions(): array
10931068
],
10941069
[
10951070
'Foo::FOO_CONSTANT',
1096-
new ConstExpression(new Fqsen('\\phpDocumentor\\Foo'), 'FOO_CONSTANT'),
1071+
new ConstExpression(new Object_(new Fqsen('\\phpDocumentor\\Foo')), 'FOO_CONSTANT'),
10971072
],
10981073
[
10991074
'Foo::FOO_*',
1100-
new ConstExpression(new Fqsen('\\phpDocumentor\\Foo'), 'FOO_*'),
1075+
new ConstExpression(new Object_(new Fqsen('\\phpDocumentor\\Foo')), 'FOO_*'),
1076+
],
1077+
[
1078+
'self::*|null',
1079+
new Compound([new ConstExpression(new Self_(), '*'), new Null_()]),
1080+
],
1081+
];
1082+
}
1083+
1084+
/**
1085+
* @return array<array{0: string, 1: Type}>
1086+
*/
1087+
public function illegalLegacyFormatProvider(): array
1088+
{
1089+
return [
1090+
[
1091+
'?string|bool',
1092+
new Compound([new Nullable(new String_()), new Boolean()]),
1093+
],
1094+
[
1095+
'?string|?bool',
1096+
new Compound([new Nullable(new String_()), new Nullable(new Boolean())]),
1097+
],
1098+
[
1099+
'?string|?bool|null',
1100+
new Compound([new Nullable(new String_()), new Nullable(new Boolean()), new Null_()]),
1101+
],
1102+
[
1103+
'?string|bool|Foo',
1104+
new Compound([
1105+
new Nullable(new String_()),
1106+
new Boolean(),
1107+
new Object_(new Fqsen('\\phpDocumentor\\Foo')),
1108+
]),
1109+
],
1110+
[
1111+
'?string&bool',
1112+
new Intersection([new Nullable(new String_()), new Boolean()]),
1113+
],
1114+
[
1115+
'?string&bool|Foo',
1116+
new Intersection(
1117+
[
1118+
new Nullable(new String_()),
1119+
new Compound([new Boolean(), new Object_(new Fqsen('\\phpDocumentor\\Foo'))]),
1120+
]
1121+
),
1122+
],
1123+
[
1124+
'?string&?bool|null',
1125+
new Compound(
1126+
[
1127+
new Intersection([new Nullable(new String_()), new Nullable(new Boolean())]),
1128+
new Null_(),
1129+
]
1130+
),
11011131
],
11021132
];
11031133
}

0 commit comments

Comments
 (0)