Skip to content

Commit 5ad886e

Browse files
authored
Merge pull request #80 from julienfalque/iterable-collection
Add collection syntax support for iterable
2 parents dbdf9d8 + b14efa6 commit 5ad886e

File tree

6 files changed

+188
-5
lines changed

6 files changed

+188
-5
lines changed

src/TypeResolver.php

+8-3
Original file line numberDiff line numberDiff line change
@@ -387,14 +387,15 @@ private function resolveTypedObject(string $type, ?Context $context = null) : Ob
387387
/**
388388
* Resolves the collection values and keys
389389
*
390-
* @return Array_|Collection
390+
* @return Array_|Iterable_|Collection
391391
*/
392392
private function resolveCollection(ArrayIterator $tokens, Type $classType, Context $context) : Type
393393
{
394394
$isArray = ((string) $classType === 'array');
395+
$isIterable = ((string) $classType === 'iterable');
395396

396-
// allow only "array" or class name before "<"
397-
if (!$isArray
397+
// allow only "array", "iterable" or class name before "<"
398+
if (!$isArray && !$isIterable
398399
&& (!$classType instanceof Object_ || $classType->getFqsen() === null)) {
399400
throw new RuntimeException(
400401
$classType . ' is not a collection'
@@ -455,6 +456,10 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte
455456
return new Array_($valueType, $keyType);
456457
}
457458

459+
if ($isIterable) {
460+
return new Iterable_($valueType, $keyType);
461+
}
462+
458463
/** @psalm-suppress RedundantCondition */
459464
if ($classType instanceof Object_) {
460465
return $this->makeCollectionFromObject($classType, $valueType, $keyType);

src/Types/Iterable_.php

+10-2
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,21 @@
1818
/**
1919
* Value Object representing iterable type
2020
*/
21-
final class Iterable_ implements Type
21+
final class Iterable_ extends AbstractList
2222
{
2323
/**
2424
* Returns a rendered output of the Type as it would be used in a DocBlock.
2525
*/
2626
public function __toString() : string
2727
{
28-
return 'iterable';
28+
if ($this->keyType) {
29+
return 'iterable<' . $this->keyType . ',' . $this->valueType . '>';
30+
}
31+
32+
if ($this->valueType instanceof Mixed_) {
33+
return 'iterable';
34+
}
35+
36+
return 'iterable<' . $this->valueType . '>';
2937
}
3038
}

tests/unit/TypeResolverTest.php

+41
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,47 @@ public function testResolvingArrayExpressionOrCompoundTypes() : void
485485
$this->assertInstanceOf(Object_::class, $secondArrayType);
486486
}
487487

488+
/**
489+
* @uses \phpDocumentor\Reflection\Types\Context
490+
* @uses \phpDocumentor\Reflection\Types\Compound
491+
* @uses \phpDocumentor\Reflection\Types\Iterable_
492+
* @uses \phpDocumentor\Reflection\Types\Object_
493+
* @uses \phpDocumentor\Reflection\Fqsen
494+
* @uses \phpDocumentor\Reflection\FqsenResolver
495+
*
496+
* @covers ::__construct
497+
* @covers ::resolve
498+
* @covers ::<private>
499+
*/
500+
public function testResolvingIterableExpressionSimpleTypes() : void
501+
{
502+
$fixture = new TypeResolver();
503+
504+
/** @var Iterable_ $resolvedType */
505+
$resolvedType = $fixture->resolve('iterable<string|\stdClass|boolean>', new Context(''));
506+
507+
$this->assertInstanceOf(Iterable_::class, $resolvedType);
508+
$this->assertSame('iterable<string|\stdClass|bool>', (string) $resolvedType);
509+
510+
/** @var Compound $valueType */
511+
$valueType = $resolvedType->getValueType();
512+
513+
$this->assertInstanceOf(Compound::class, $valueType);
514+
515+
/** @var String_ $firstType */
516+
$firstType = $valueType->get(0);
517+
518+
/** @var Object_ $secondType */
519+
$secondType = $valueType->get(1);
520+
521+
/** @var Boolean $thirdType */
522+
$thirdType = $valueType->get(2);
523+
524+
$this->assertInstanceOf(String_::class, $firstType);
525+
$this->assertInstanceOf(Object_::class, $secondType);
526+
$this->assertInstanceOf(Boolean::class, $thirdType);
527+
}
528+
488529
/**
489530
* This test asserts that the parameter order is correct.
490531
*

tests/unit/Types/ArrayTest.php

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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\Types;
15+
16+
use PHPUnit\Framework\TestCase;
17+
18+
/**
19+
* @coversDefaultClass \phpDocumentor\Reflection\Types\Array_
20+
*/
21+
class ArrayTest extends TestCase
22+
{
23+
/**
24+
* @covers ::__toString
25+
*
26+
* @dataProvider provideArrays
27+
*/
28+
public function testArrayStringifyCorrectly(Array_ $array, string $expectedString) : void
29+
{
30+
$this->assertSame($expectedString, (string) $array);
31+
}
32+
33+
public function provideArrays() : array
34+
{
35+
return [
36+
'simple array' => [new Array_(), 'array'],
37+
'array of mixed' => [new Array_(new Mixed_()), 'array'],
38+
'array of single type' => [new Array_(new String_()), 'string[]'],
39+
'array of compound type' => [new Array_(new Compound([new Integer(), new String_()])), '(int|string)[]'],
40+
'array with key type' => [new Array_(new String_(), new Integer()), 'array<int,string>'],
41+
];
42+
}
43+
}

tests/unit/Types/CollectionTest.php

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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\Types;
15+
16+
use phpDocumentor\Reflection\Fqsen;
17+
use PHPUnit\Framework\TestCase;
18+
19+
/**
20+
* @coversDefaultClass \phpDocumentor\Reflection\Types\Collection
21+
*/
22+
class CollectionTest extends TestCase
23+
{
24+
/**
25+
* @covers ::__toString
26+
*
27+
* @dataProvider provideCollections
28+
*/
29+
public function testCollectionStringifyCorrectly(Collection $collection, string $expectedString) : void
30+
{
31+
$this->assertSame($expectedString, (string) $collection);
32+
}
33+
34+
public function provideCollections() : array
35+
{
36+
return [
37+
'simple collection' => [new Collection(null, new Integer()), 'object<int>'],
38+
'simple collection with key type' => [new Collection(null, new Integer(), new String_()), 'object<string,int>'],
39+
'collection of single type using specific class' => [new Collection(new Fqsen('\Foo\Bar'), new Integer()), '\Foo\Bar<int>'],
40+
'collection of single type with key type and using specific class' => [new Collection(new Fqsen('\Foo\Bar'), new String_(), new Integer()), '\Foo\Bar<int,string>'],
41+
];
42+
}
43+
}

tests/unit/Types/IterableTest.php

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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\Types;
15+
16+
use PHPUnit\Framework\TestCase;
17+
18+
/**
19+
* @coversDefaultClass \phpDocumentor\Reflection\Types\Iterable_
20+
*/
21+
class IterableTest extends TestCase
22+
{
23+
/**
24+
* @covers ::__toString
25+
*
26+
* @dataProvider provideIterables
27+
*/
28+
public function testIterableStringifyCorrectly(Iterable_ $iterable, string $expectedString) : void
29+
{
30+
$this->assertSame($expectedString, (string) $iterable);
31+
}
32+
33+
public function provideIterables() : array
34+
{
35+
return [
36+
'simple iterable' => [new Iterable_(), 'iterable'],
37+
'iterable of mixed' => [new Iterable_(new Mixed_()), 'iterable'],
38+
'iterable of single type' => [new Iterable_(new String_()), 'iterable<string>'],
39+
'iterable of compound type' => [new Iterable_(new Compound([new Integer(), new String_()])), 'iterable<int|string>'],
40+
'iterable with key type' => [new Iterable_(new String_(), new Integer()), 'iterable<int,string>'],
41+
];
42+
}
43+
}

0 commit comments

Comments
 (0)