Skip to content

Commit fc709d3

Browse files
authored
Merge pull request #207 from phpDocumentor/feature/object-shape
Add support for object and list shape types
2 parents f237fbd + b6f5321 commit fc709d3

14 files changed

+273
-60
lines changed

composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"require": {
1313
"php": "^7.3 || ^8.0",
1414
"phpdocumentor/reflection-common": "^2.0",
15-
"phpstan/phpdoc-parser": "^1.13",
15+
"phpstan/phpdoc-parser": "^1.18",
1616
"doctrine/deprecations": "^1.0"
1717
},
1818
"require-dev": {

composer.lock

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/PseudoTypes/ArrayShapeItem.php

+1-48
Original file line numberDiff line numberDiff line change
@@ -13,53 +13,6 @@
1313

1414
namespace phpDocumentor\Reflection\PseudoTypes;
1515

16-
use phpDocumentor\Reflection\Type;
17-
use phpDocumentor\Reflection\Types\Mixed_;
18-
19-
use function sprintf;
20-
21-
final class ArrayShapeItem
16+
class ArrayShapeItem extends ShapeItem
2217
{
23-
/** @var string|null */
24-
private $key;
25-
/** @var Type */
26-
private $value;
27-
/** @var bool */
28-
private $optional;
29-
30-
public function __construct(?string $key, ?Type $value, bool $optional)
31-
{
32-
$this->key = $key;
33-
$this->value = $value ?? new Mixed_();
34-
$this->optional = $optional;
35-
}
36-
37-
public function getKey(): ?string
38-
{
39-
return $this->key;
40-
}
41-
42-
public function getValue(): Type
43-
{
44-
return $this->value;
45-
}
46-
47-
public function isOptional(): bool
48-
{
49-
return $this->optional;
50-
}
51-
52-
public function __toString(): string
53-
{
54-
if ($this->key !== null) {
55-
return sprintf(
56-
'%s%s: %s',
57-
$this->key,
58-
$this->optional ? '?' : '',
59-
(string) $this->value
60-
);
61-
}
62-
63-
return (string) $this->value;
64-
}
6518
}

src/PseudoTypes/ListShape.php

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\PseudoTypes;
6+
7+
use function implode;
8+
9+
/** @psalm-immutable */
10+
final class ListShape extends ArrayShape
11+
{
12+
public function __toString(): string
13+
{
14+
return 'list{' . implode(', ', $this->getItems()) . '}';
15+
}
16+
}

src/PseudoTypes/ListShapeItem.php

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\PseudoTypes;
6+
7+
final class ListShapeItem extends ArrayShapeItem
8+
{
9+
}

src/PseudoTypes/ObjectShape.php

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\PseudoTypes;
6+
7+
use phpDocumentor\Reflection\PseudoType;
8+
use phpDocumentor\Reflection\Type;
9+
use phpDocumentor\Reflection\Types\Object_;
10+
11+
use function implode;
12+
13+
/** @psalm-immutable */
14+
final class ObjectShape implements PseudoType
15+
{
16+
/** @var ObjectShapeItem[] */
17+
private $items;
18+
19+
public function __construct(ObjectShapeItem ...$items)
20+
{
21+
$this->items = $items;
22+
}
23+
24+
/**
25+
* @return ObjectShapeItem[]
26+
*/
27+
public function getItems(): array
28+
{
29+
return $this->items;
30+
}
31+
32+
public function underlyingType(): Type
33+
{
34+
return new Object_();
35+
}
36+
37+
public function __toString(): string
38+
{
39+
return 'object{' . implode(', ', $this->items) . '}';
40+
}
41+
}

src/PseudoTypes/ObjectShapeItem.php

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\PseudoTypes;
6+
7+
final class ObjectShapeItem extends ShapeItem
8+
{
9+
}

src/PseudoTypes/ShapeItem.php

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\PseudoTypes;
6+
7+
use phpDocumentor\Reflection\Type;
8+
use phpDocumentor\Reflection\Types\Mixed_;
9+
10+
use function sprintf;
11+
12+
abstract class ShapeItem
13+
{
14+
/** @var string|null */
15+
private $key;
16+
/** @var Type */
17+
private $value;
18+
/** @var bool */
19+
private $optional;
20+
21+
public function __construct(?string $key, ?Type $value, bool $optional)
22+
{
23+
$this->key = $key;
24+
$this->value = $value ?? new Mixed_();
25+
$this->optional = $optional;
26+
}
27+
28+
public function getKey(): ?string
29+
{
30+
return $this->key;
31+
}
32+
33+
public function getValue(): Type
34+
{
35+
return $this->value;
36+
}
37+
38+
public function isOptional(): bool
39+
{
40+
return $this->optional;
41+
}
42+
43+
public function __toString(): string
44+
{
45+
if ($this->key !== null) {
46+
return sprintf(
47+
'%s%s: %s',
48+
$this->key,
49+
$this->optional ? '?' : '',
50+
(string) $this->value
51+
);
52+
}
53+
54+
return (string) $this->value;
55+
}
56+
}

src/TypeResolver.php

+42-3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
use phpDocumentor\Reflection\PseudoTypes\IntegerRange;
2626
use phpDocumentor\Reflection\PseudoTypes\IntegerValue;
2727
use phpDocumentor\Reflection\PseudoTypes\List_;
28+
use phpDocumentor\Reflection\PseudoTypes\ListShape;
29+
use phpDocumentor\Reflection\PseudoTypes\ListShapeItem;
2830
use phpDocumentor\Reflection\PseudoTypes\LiteralString;
2931
use phpDocumentor\Reflection\PseudoTypes\LowercaseString;
3032
use phpDocumentor\Reflection\PseudoTypes\NegativeInteger;
@@ -34,6 +36,8 @@
3436
use phpDocumentor\Reflection\PseudoTypes\NonEmptyString;
3537
use phpDocumentor\Reflection\PseudoTypes\Numeric_;
3638
use phpDocumentor\Reflection\PseudoTypes\NumericString;
39+
use phpDocumentor\Reflection\PseudoTypes\ObjectShape;
40+
use phpDocumentor\Reflection\PseudoTypes\ObjectShapeItem;
3741
use phpDocumentor\Reflection\PseudoTypes\PositiveInteger;
3842
use phpDocumentor\Reflection\PseudoTypes\StringValue;
3943
use phpDocumentor\Reflection\PseudoTypes\TraitString;
@@ -83,6 +87,8 @@
8387
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
8488
use PHPStan\PhpDocParser\Ast\Type\IntersectionTypeNode;
8589
use PHPStan\PhpDocParser\Ast\Type\NullableTypeNode;
90+
use PHPStan\PhpDocParser\Ast\Type\ObjectShapeItemNode;
91+
use PHPStan\PhpDocParser\Ast\Type\ObjectShapeNode;
8692
use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
8793
use PHPStan\PhpDocParser\Ast\Type\ThisTypeNode;
8894
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
@@ -236,10 +242,43 @@ public function createType(?TypeNode $type, Context $context): Type
236242
);
237243

238244
case ArrayShapeNode::class:
239-
return new ArrayShape(
245+
switch ($type->kind) {
246+
case ArrayShapeNode::KIND_ARRAY:
247+
return new ArrayShape(
248+
...array_map(
249+
function (ArrayShapeItemNode $item) use ($context): ArrayShapeItem {
250+
return new ArrayShapeItem(
251+
(string) $item->keyName,
252+
$this->createType($item->valueType, $context),
253+
$item->optional
254+
);
255+
},
256+
$type->items
257+
)
258+
);
259+
260+
case ArrayShapeNode::KIND_LIST:
261+
return new ListShape(
262+
...array_map(
263+
function (ArrayShapeItemNode $item) use ($context): ListShapeItem {
264+
return new ListShapeItem(
265+
null,
266+
$this->createType($item->valueType, $context),
267+
$item->optional
268+
);
269+
},
270+
$type->items
271+
)
272+
);
273+
274+
default:
275+
throw new RuntimeException('Unsupported array shape kind');
276+
}
277+
case ObjectShapeNode::class:
278+
return new ObjectShape(
240279
...array_map(
241-
function (ArrayShapeItemNode $item) use ($context): ArrayShapeItem {
242-
return new ArrayShapeItem(
280+
function (ObjectShapeItemNode $item) use ($context): ObjectShapeItem {
281+
return new ObjectShapeItem(
243282
(string) $item->keyName,
244283
$this->createType($item->valueType, $context),
245284
$item->optional

0 commit comments

Comments
 (0)