8000 feature #44451 [PropertyInfo] Add support for phpDocumentor and PHPSt… · symfony/symfony@53e49a8 · GitHub
[go: up one dir, main page]

Skip to content

Commit 53e49a8

Browse files
feature #44451 [PropertyInfo] Add support for phpDocumentor and PHPStan pseudo-types (EmilMassey)
This PR was squashed before being merged into the 6.1 branch. Discussion ---------- [PropertyInfo] Add support for phpDocumentor and PHPStan pseudo-types | Q | A | ------------- | --- | Branch? | 6.1 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | - | License | MIT | Doc PR | - More and more apps are using pseudo-types like `non-empty-string`, `positive-int` which are understood by static analysis tools but are not a part of the language. This PR adds support for these types to `PhpDocExtractor` and `PhpStanExtractor`. Pseudo-type is mapped to built-in type(s) (e.g. `non-empty-string` => `string`, `positive-int` => `int`, `number` => `int|float`). This PR adds support for all pseudo-types defined by the [phpDocumentor](https://github.com/phpDocumentor/TypeResolver/tree/f8ec4ab631de5a97769e66b13418c3b8b24e81f4/src/PseudoTypes) (some of them like `list` or `true`, `false` are already supported) and [PHPStan's](https://phpstan.org/writing-php-code/phpdoc-types) basic types. Commits ------- 3e49b0f [PropertyInfo] Add support for phpDocumentor and PHPStan pseudo-types
2 parents a976d27 + 3e49b0f commit 53e49a8

File tree

9 files changed

+196
-2
lines changed

9 files changed

+196
-2
lines changed

composer.json

-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@
157157
"egulias/email-validator": "~3.0.0",
158158
"masterminds/html5": "<2.6",
159159
"phpdocumentor/reflection-docblock": "<5.2",
160-
"phpdocumentor/type-resolver": "<1.4.0",
160+
"phpdocumentor/type-resolver": "<1.5.1",
161161
"ocramius/proxy-manager": "<2.1",
162162
"phpunit/phpunit": "<5.4.3"
163163
},

src/Symfony/Component/PropertyInfo/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
6.1
5+
---
6+
7+
* Add support for phpDocumentor and PHPStan pseudo-types
8+
49
6.0
510
---
611

src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,29 @@ public function constructorTypesProvider()
406406
['ddd', null],
407407
];
408408
}
409+
410+
/**
411+
* @dataProvider pseudoTypesProvider
412+
*/
413+
public function testPseudoTypes($property, array $type)
414+
{
415+
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\PseudoTypesDummy', $property));
416+
}
417+
418+
public function pseudoTypesProvider(): array
419+
{
420+
return [
421+
['classString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
422+
['classStringGeneric', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
423+
['htmlEscapedString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
424+
['lowercaseString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
425+
['nonEmptyLowercaseString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
426+
['nonEmptyString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
427+
['numericString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
428+
['traitString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
429+
['positiveInt', [new Type(Type::BUILTIN_TYPE_INT, false, null)]],
430+
];
431+
}
409432
}
410433

411434
class EmptyDocBlock

src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,39 @@ public function unionTypesProvider(): array
371371
];
372372
}
373373

374+
/**
375+
* @dataProvider pseudoTypesProvider
376+
*/
377+
public function testPseudoTypes($property, array $type)
378+
{
379+
$this->assertEquals($type, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\PhpStanPseudoTypesDummy', $property));
380+
}
381+
382+
public function pseudoTypesProvider(): array
383+
{
384+
return [
385+
['classString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
386+
['classStringGeneric', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
387+
['htmlEscapedString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
388+
['lowercaseString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
389+
['nonEmptyLowercaseString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
390+
['nonEmptyString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
391+
['numericString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
392+
['traitString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
393+
['interfaceString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
394+
['literalString', [new Type(Type::BUILTIN_TYPE_STRING, false, null)]],
395+
['positiveInt', [new Type(Type::BUILTIN_TYPE_INT, false, null)]],
396+
['negativeInt', [new Type(Type::BUILTIN_TYPE_INT, false, null)]],
397+
['nonEmptyArray', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)]],
398+
['nonEmptyList', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT))]],
399+
['scalar', [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_FLOAT), new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_BOOL)]],
400+
['number', [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_FLOAT)]],
401+
['numeric', [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_FLOAT), new Type(Type::BUILTIN_TYPE_STRING)]],
402+
['arrayKey', [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)]],
403+
['double', [new Type(Type::BUILTIN_TYPE_FLOAT)]],
404+
];
405+
}
406+
374407
public function testDummyNamespace()
375408
{
376409
$this->assertEquals(
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
13+
14+
/**
15+
* @author Emil Masiakowski <emil.masiakowski@gmail.com>
16+
*/
17+
class PhpStanPseudoTypesDummy extends PseudoTypesDummy
18+
{
19+
/** @var negative-int */
20+
public $negativeInt;
21+
22+
/** @var non-empty-array */
23+
public $nonEmptyArray;
24+
25+
/** @var non-empty-list */
26+
public $nonEmptyList;
27+
28+
/** @var interface-string */
29+
public $interfaceString;
30+
31+
/** @var scalar */
32+
public $scalar;
33+
34+
/** @var array-key */
35+
public $arrayKey;
36+
37+
/** @var number */
38+
public $number;
39+
40+
/** @var numeric */
41+
public $numeric;
42+
43+
/** @var double */
44+
public $double;
45+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
13+
14+
/**
15+
* @author Emil Masiakowski <emil.masiakowski@gmail.com>
16+
*/
17+
class PseudoTypesDummy
18+
{
19+
/** @var class-string */
20+
public $classString;
21+
22+
/** @var class-string<\stdClass> */
23+
public $classStringGeneric;
24+
25+
/** @var html-escaped-string */
26+
public $htmlEscapedString;
27+
28+
/** @var lowercase-string */
29+
public $lowercaseString;
30+
31+
/** @var non-empty-lowercase-string */
32+
public $nonEmptyLowercaseString;
33+
34+
/** @var non-empty-string */
35+
public $nonEmptyString;
36+
37+
/** @var numeric-string */
38+
public $numericString;
39+
40+
/** @var trait-string */
41+
public $traitString;
42+
43+
/** @var positive-int */
44+
public $positiveInt;
45+
46+
/** @var literal-string */
47+
public $literalString;
48+
}

src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@
1111

1212
namespace Symfony\Component\PropertyInfo\Util;
1313

14+
use phpDocumentor\Reflection\PseudoType;
1415
use phpDocumentor\Reflection\PseudoTypes\List_;
1516
use phpDocumentor\Reflection\Type as DocType;
1617
use phpDocumentor\Reflection\Types\Array_;
1718
use phpDocumentor\Reflection\Types\Collection;
1819
use phpDocumentor\Reflection\Types\Compound;
20+
use phpDocumentor\Reflection\Types\Integer;
1921
use phpDocumentor\Reflection\Types\Null_;
2022
use phpDocumentor\Reflection\Types\Nullable;
23+
use phpDocumentor\Reflection\Types\String_;
2124
use Symfony\Component\PropertyInfo\Type;
2225

2326
// Workaround for phpdocumentor/type-resolver < 1.6
@@ -143,6 +146,14 @@ private function createType(DocType $type, bool $nullable, string $docType = nul
143146
return new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, $collectionKeyType, $collectionValueType);
144147
}
145148

149+
if ($type instanceof PseudoType) {
150+
if ($type->underlyingType() instanceof Integer) {
151+
return new Type(Type::BUILTIN_TYPE_INT, $nullable, null);
152+
} elseif ($type->underlyingType() instanceof String_) {
153+
return new Type(Type::BUILTIN_TYPE_STRING, $nullable, null);
154+
}
155+
}
156+
146157
$docType = $this->normalizeType($docType);
147158
[$phpType, $class] = $this->getPhpTypeAndClass($docType);
148159

src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ private function extractTypes(TypeNode $node, NameScope $nameScope): array
110110
return $this->compressNullableType($types);
111111
}
112112
if ($node instanceof GenericTypeNode) {
113+
if ('class-string' === $node->type->name) {
114+
return [new Type(Type::BUILTIN_TYPE_STRING)];
115+
}
116+
113117
[$mainType] = $this->extractTypes($node->type, $nameScope);
114118

115119
$collectionKeyTypes = $mainType->getCollectionKeyTypes();
@@ -158,18 +162,43 @@ private function extractTypes(TypeNode $node, NameScope $nameScope): array
158162

159163
switch ($node->name) {
160164
case 'integer':
165+
case 'positive-int':
166+
case 'negative-int':
161167
return [new Type(Type::BUILTIN_TYPE_INT)];
168+
case 'double':
169+
return [new Type(Type::BUILTIN_TYPE_FLOAT)];
162170
case 'list':
171+
case 'non-empty-list':
163172
return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT))];
173+
case 'non-empty-array':
174+
return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)];
164175
case 'mixed':
165176
return []; // mixed seems to be ignored in all other extractors
166177
case 'parent':
167178
return [new Type(Type::BUILTIN_TYPE_OBJECT, false, $node->name)];
168179
case 'static':
169180
case 'self':
170181
return [new Type(Type::BUILTIN_TYPE_OBJECT, false, $nameScope->resolveRootClass())];
182+
case 'class-string':
183+
case 'html-escaped-string':
184+
case 'lowercase-string':
185+
case 'non-empty-lowercase-string':
186+
case 'non-empty-string':
187+
case 'numeric-string':
188+
case 'trait-string':
189+
case 'interface-string':
190+
case 'literal-string':
191+
return [new Type(Type::BUILTIN_TYPE_STRING)];
171192
case 'void':
172193
return [new Type(Type::BUILTIN_TYPE_NULL)];
194+
case 'scalar':
195+
return [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_FLOAT), new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_BOOL)];
196+
case 'number':
197+
return [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_FLOAT)];
198+
case 'numeric':
199+
return [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_FLOAT), new Type(Type::BUILTIN_TYPE_STRING)];
200+
case 'array-key':
201+
return [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)];
173202
}
174203

175204
return [new Type(Type::BUILTIN_TYPE_OBJECT, false, $nameScope->resolveStringName($node->name))];

src/Symfony/Component/PropertyInfo/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
},
3737
"conflict": {
3838
"phpdocumentor/reflection-docblock": "<5.2",
39-
"phpdocumentor/type-resolver": "<1.4.0",
39+
"phpdocumentor/type-resolver": "<1.5.1",
4040
"symfony/dependency-injection": "<5.4"
4141
},
4242
"suggest": {

0 commit comments

Comments
 (0)
0