diff --git a/src/DocBlock/Annotation.php b/src/DocBlock/Annotation.php index 1c129034d1d..9c9db350dfc 100644 --- a/src/DocBlock/Annotation.php +++ b/src/DocBlock/Annotation.php @@ -208,7 +208,7 @@ public function setTypes(array $types): void { $pattern = '/'.preg_quote($this->getTypesContent(), '/').'/'; - $this->lines[0]->setContent(Preg::replace($pattern, implode('|', $types), $this->lines[0]->getContent(), 1)); + $this->lines[0]->setContent(Preg::replace($pattern, implode($this->getTypeExpression()->getTypesGlue(), $types), $this->lines[0]->getContent(), 1)); $this->clearCache(); } diff --git a/src/DocBlock/TypeExpression.php b/src/DocBlock/TypeExpression.php index 8e30470a27d..b3707cd0958 100644 --- a/src/DocBlock/TypeExpression.php +++ b/src/DocBlock/TypeExpression.php @@ -99,7 +99,7 @@ final class TypeExpression ) ) (?: - \h*[|&]\h* + \h*(?[|&])\h* (?&type) )* ) @@ -120,6 +120,8 @@ final class TypeExpression */ private $innerTypeExpressions = []; + private string $typesGlue = '|'; + /** * @var null|NamespaceAnalysis */ @@ -165,7 +167,7 @@ static function (array $type) { return $type['expression']->toString(); }, /** * @param callable(self $a, self $b): int $compareCallback */ - public function sortUnionTypes(callable $compareCallback): void + public function sortTypes(callable $compareCallback): void { foreach (array_reverse($this->innerTypeExpressions) as [ 'start_index' => $startIndex, @@ -173,7 +175,7 @@ public function sortUnionTypes(callable $compareCallback): void ]) { $initialValueLength = \strlen($inner->toString()); - $inner->sortUnionTypes($compareCallback); + $inner->sortTypes($compareCallback); $this->value = substr_replace( $this->value, @@ -190,10 +192,15 @@ static function (array $type): self { return $type['expression']; }, $compareCallback ); - $this->value = implode('|', $this->getTypes()); + $this->value = implode($this->getTypesGlue(), $this->getTypes()); } } + public function getTypesGlue(): string + { + return $this->typesGlue; + } + public function getCommonType(): ?string { $aliases = $this->getAliases(); @@ -254,6 +261,8 @@ private function parse(): void return; } + $this->typesGlue = $matches['glue'] ?? $this->typesGlue; + $index = '' !== $matches['nullable'] ? 1 : 0; if ($matches['type'] !== $matches['types']) { diff --git a/src/Fixer/Phpdoc/PhpdocTypesOrderFixer.php b/src/Fixer/Phpdoc/PhpdocTypesOrderFixer.php index 963dae3c731..6cff3474b82 100644 --- a/src/Fixer/Phpdoc/PhpdocTypesOrderFixer.php +++ b/src/Fixer/Phpdoc/PhpdocTypesOrderFixer.php @@ -170,7 +170,7 @@ private function sortTypes(TypeExpression $typeExpression): array return Preg::replace('/^\\??\\\?/', '', $type); }; - $typeExpression->sortUnionTypes( + $typeExpression->sortTypes( function (TypeExpression $a, TypeExpression $b) use ($normalizeType): int { $a = $normalizeType($a->toString()); $b = $normalizeType($b->toString()); diff --git a/tests/DocBlock/AnnotationTest.php b/tests/DocBlock/AnnotationTest.php index 11ee0a57500..236705b158d 100644 --- a/tests/DocBlock/AnnotationTest.php +++ b/tests/DocBlock/AnnotationTest.php @@ -473,6 +473,7 @@ public function provideTypesCases(): array [['RUNTIMEEEEeXCEPTION'], [\Throwable::class], "*\t@throws\t \t RUNTIMEEEEeXCEPTION\t\t\t\t\t\t\t\n\n\n", "*\t@throws\t \t Throwable\t\t\t\t\t\t\t\n\n\n"], [['RUNTIMEEEEeXCEPTION'], [\Throwable::class], "*@throws\t \t RUNTIMEEEEeXCEPTION\t\t\t\t\t\t\t\n\n\n", "*@throws\t \t Throwable\t\t\t\t\t\t\t\n\n\n"], [['string'], ['string', 'null'], ' * @method string getString()', ' * @method string|null getString()'], + [['Foo', 'Bar'], ['Bar', 'Foo'], ' * @param Foo&Bar $x', ' * @param Bar&Foo $x'], ]; } diff --git a/tests/DocBlock/TypeExpressionTest.php b/tests/DocBlock/TypeExpressionTest.php index 826e4a115fc..7d9e003d515 100644 --- a/tests/DocBlock/TypeExpressionTest.php +++ b/tests/DocBlock/TypeExpressionTest.php @@ -86,6 +86,22 @@ public function provideGetTypesCases(): \Generator yield ['array < int , callable ( string ) : bool >', ['array < int , callable ( string ) : bool >']]; } + /** + * @dataProvider provideGetTypesGlueCases + */ + public function testGetTypesGlue(string $expectedTypesGlue, string $typesExpression): void + { + $expression = new TypeExpression($typesExpression, null, []); + static::assertSame($expectedTypesGlue, $expression->getTypesGlue()); + } + + public static function provideGetTypesGlueCases(): iterable + { + yield ['|', 'string']; // for backward behaviour + yield ['|', 'bool|string']; + yield ['&', 'Foo&Bar']; + } + /** * @param NamespaceUseAnalysis[] $namespaceUses * @@ -181,20 +197,20 @@ public function provideAllowsNullCases(): \Generator } /** - * @dataProvider provideSortUnionTypesCases + * @dataProvider provideSortTypesCases */ - public function testSortUnionTypes(string $typesExpression, string $expectResult): void + public function testSortTypes(string $typesExpression, string $expectResult): void { $expression = new TypeExpression($typesExpression, null, []); - $expression->sortUnionTypes(static function (TypeExpression $a, TypeExpression $b): int { + $expression->sortTypes(static function (TypeExpression $a, TypeExpression $b): int { return strcasecmp($a->toString(), $b->toString()); }); static::assertSame($expectResult, $expression->toString()); } - public function provideSortUnionTypesCases(): iterable + public function provideSortTypesCases(): iterable { yield 'not a union type' => [ 'int', @@ -264,5 +280,13 @@ public function provideSortUnionTypesCases(): iterable '?array{0: Foo|Bar}', '?array{0: Bar|Foo}', ]; + yield 'simple types alternation' => [ + 'array', + 'array', + ]; + yield 'nesty stuff' => [ + 'array>>', + 'array|Level2>&Level11>', + ]; } } diff --git a/tests/Fixer/Phpdoc/PhpdocTypesOrderFixerTest.php b/tests/Fixer/Phpdoc/PhpdocTypesOrderFixerTest.php index 7ad3c3105c8..5f04734bde6 100644 --- a/tests/Fixer/Phpdoc/PhpdocTypesOrderFixerTest.php +++ b/tests/Fixer/Phpdoc/PhpdocTypesOrderFixerTest.php @@ -411,6 +411,22 @@ public function provideFixWithAlphaAlgorithmCases(): array [ ' , DateTime): bool> */', ], + [ + ' */', + ' */', + ], + [ + '|bool|string */', + '|string */', + ], + [ + '&C&D */', + '&C */', + ], ]; }