Skip to content

Commit 528f51b

Browse files
authored
Merge pull request #136 from enumag/list-type
Add list pseudo-type
2 parents 9dc4bb0 + 1c7bc20 commit 528f51b

File tree

6 files changed

+134
-3
lines changed

6 files changed

+134
-3
lines changed

src/PseudoTypes/List_.php

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of phpDocumentor.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*
11+
* @link http://phpdoc.org
12+
*/
13+
14+
namespace phpDocumentor\Reflection\PseudoTypes;
15+
16+
use phpDocumentor\Reflection\PseudoType;
17+
use phpDocumentor\Reflection\Type;
18+
use phpDocumentor\Reflection\Types\Array_;
19+
use phpDocumentor\Reflection\Types\Integer;
20+
use phpDocumentor\Reflection\Types\Mixed_;
21+
22+
/**
23+
* Value Object representing the type 'list'.
24+
*
25+
* @psalm-immutable
26+
*/
27+
final class List_ extends Array_ implements PseudoType
28+
{
29+
public function underlyingType(): Type
30+
{
31+
return new Array_();
32+
}
33+
34+
public function __construct(?Type $valueType = null)
35+
{
36+
parent::__construct($valueType, new Integer());
37+
}
38+
39+
/**
40+
* Returns a rendered output of the Type as it would be used in a DocBlock.
41+
*/
42+
public function __toString(): string
43+
{
44+
if ($this->valueType instanceof Mixed_) {
45+
return 'list';
46+
}
47+
48+
return 'list<' . $this->valueType . '>';
49+
}
50+
}

src/TypeResolver.php

+9-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use phpDocumentor\Reflection\Types\InterfaceString;
2727
use phpDocumentor\Reflection\Types\Intersection;
2828
use phpDocumentor\Reflection\Types\Iterable_;
29+
use phpDocumentor\Reflection\PseudoTypes\List_;
2930
use phpDocumentor\Reflection\Types\Nullable;
3031
use phpDocumentor\Reflection\Types\Object_;
3132
use phpDocumentor\Reflection\Types\String_;
@@ -110,6 +111,7 @@ final class TypeResolver
110111
'parent' => Types\Parent_::class,
111112
'iterable' => Types\Iterable_::class,
112113
'never' => Types\Never_::class,
114+
'list' => PseudoTypes\List_::class,
113115
];
114116

115117
/**
@@ -521,10 +523,11 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte
521523
{
522524
$isArray = ((string) $classType === 'array');
523525
$isIterable = ((string) $classType === 'iterable');
526+
$isList = ((string) $classType === 'list');
524527

525528
// allow only "array", "iterable" or class name before "<"
526529
if (
527-
!$isArray && !$isIterable
530+
!$isArray && !$isIterable && !$isList
528531
&& (!$classType instanceof Object_ || $classType->getFqsen() === null)
529532
) {
530533
throw new RuntimeException(
@@ -538,7 +541,7 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte
538541
$keyType = null;
539542

540543
$token = $tokens->current();
541-
if ($token !== null && trim($token) === ',') {
544+
if ($token !== null && trim($token) === ',' && !$isList) {
542545
// if we have a comma, then we just parsed the key type, not the value type
543546
$keyType = $valueType;
544547
if ($isArray) {
@@ -596,6 +599,10 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte
596599
return new Iterable_($valueType, $keyType);
597600
}
598601

602+
if ($isList) {
603+
return new List_($valueType);
604+
}
605+
599606
if ($classType instanceof Object_) {
600607
return $this->makeCollectionFromObject($classType, $valueType, $keyType);
601608
}

src/Types/Array_.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@
2424
*
2525
* @psalm-immutable
2626
*/
27-
final class Array_ extends AbstractList
27+
class Array_ extends AbstractList
2828
{
2929
}

tests/unit/CollectionResolverTest.php

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

1414
namespace phpDocumentor\Reflection;
1515

16+
use phpDocumentor\Reflection\PseudoTypes\List_;
1617
use phpDocumentor\Reflection\Types\Array_;
1718
use phpDocumentor\Reflection\Types\Collection;
1819
use phpDocumentor\Reflection\Types\Context;
@@ -297,4 +298,28 @@ public function testResolvingCollectionAsArray(): void
297298
$this->assertInstanceOf(Types\Float_::class, $valueType);
298299
$this->assertInstanceOf(Types\String_::class, $keyType);
299300
}
301+
302+
/**
303+
* @uses \phpDocumentor\Reflection\Types\Context
304+
* @uses \phpDocumentor\Reflection\Types\String_
305+
*
306+
* @covers ::__construct
307+
* @covers ::resolve
308+
*/
309+
public function testResolvingList(): void
310+
{
311+
$fixture = new TypeResolver();
312+
313+
$resolvedType = $fixture->resolve('list<string>', new Context(''));
314+
315+
$this->assertInstanceOf(List_::class, $resolvedType);
316+
$this->assertSame('list<string>', (string) $resolvedType);
317+
318+
$valueType = $resolvedType->getValueType();
319+
320+
$keyType = $resolvedType->getKeyType();
321+
322+
$this->assertInstanceOf(Types\String_::class, $valueType);
323+
$this->assertInstanceOf(Types\Integer::class, $keyType);
324+
}
300325
}

tests/unit/PseudoTypes/ListTest.php

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of phpDocumentor.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*
11+
* @link http://phpdoc.org
12+
*/
13+
14+
namespace phpDocumentor\Reflection\PseudoTypes;
15+
16+
use phpDocumentor\Reflection\Types\Compound;
17+
use phpDocumentor\Reflection\Types\Integer;
18+
use phpDocumentor\Reflection\Types\Mixed_;
19+
use phpDocumentor\Reflection\Types\String_;
20+
use PHPUnit\Framework\TestCase;
21+
22+
/**
23+
* @coversDefaultClass \phpDocumentor\Reflection\PseudoTypes\List_
24+
*/
25+
class ListTest extends TestCase
26+
{
27+
/**
28+
* @dataProvider provideArrays
29+
* @covers ::__toString
30+
*/
31+
public function testArrayStringifyCorrectly(List_ $array, string $expectedString): void
32+
{
33+
$this->assertSame($expectedString, (string) $array);
34+
}
35+
36+
/**
37+
* @return mixed[]
38+
*/
39+
public function provideArrays(): array
40+
{
41+
return [
42+
'simple list' => [new List_(), 'list'],
43+
'list of mixed' => [new List_(new Mixed_()), 'list'],
44+
'list of single type' => [new List_(new String_()), 'list<string>'],
45+
'list of compound type' => [new List_(new Compound([new Integer(), new String_()])), 'list<int|string>'],
46+
];
47+
}
48+
}

tests/unit/TypeResolverTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,7 @@ public function provideKeywords(): array
759759
['iterable', Types\Iterable_::class],
760760
['never', Types\Never_::class],
761761
['literal-string', PseudoTypes\LiteralString::class],
762+
['list', PseudoTypes\List_::class],
762763
];
763764
}
764765

0 commit comments

Comments
 (0)