Skip to content

Add collection syntax support for iterable #80

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/TypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -387,14 +387,15 @@ private function resolveTypedObject(string $type, ?Context $context = null) : Ob
/**
* Resolves the collection values and keys
*
* @return Array_|Collection
* @return Array_|Iterable_|Collection
*/
private function resolveCollection(ArrayIterator $tokens, Type $classType, Context $context) : Type
{
$isArray = ((string) $classType === 'array');
$isIterable = ((string) $classType === 'iterable');

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

if ($isIterable) {
return new Iterable_($valueType, $keyType);
}

/** @psalm-suppress RedundantCondition */
if ($classType instanceof Object_) {
return $this->makeCollectionFromObject($classType, $valueType, $keyType);
Expand Down
12 changes: 10 additions & 2 deletions src/Types/Iterable_.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,21 @@
/**
* Value Object representing iterable type
*/
final class Iterable_ implements Type
final class Iterable_ extends AbstractList
{
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString() : string
{
return 'iterable';
if ($this->keyType) {
return 'iterable<' . $this->keyType . ',' . $this->valueType . '>';
}

if ($this->valueType instanceof Mixed_) {
return 'iterable';
}

return 'iterable<' . $this->valueType . '>';
}
}
41 changes: 41 additions & 0 deletions tests/unit/TypeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,47 @@ public function testResolvingArrayExpressionOrCompoundTypes() : void
$this->assertInstanceOf(Object_::class, $secondArrayType);
}

/**
* @uses \phpDocumentor\Reflection\Types\Context
* @uses \phpDocumentor\Reflection\Types\Compound
* @uses \phpDocumentor\Reflection\Types\Iterable_
* @uses \phpDocumentor\Reflection\Types\Object_
* @uses \phpDocumentor\Reflection\Fqsen
* @uses \phpDocumentor\Reflection\FqsenResolver
*
* @covers ::__construct
* @covers ::resolve
* @covers ::<private>
*/
public function testResolvingIterableExpressionSimpleTypes() : void
{
$fixture = new TypeResolver();

/** @var Iterable_ $resolvedType */
$resolvedType = $fixture->resolve('iterable<string|\stdClass|boolean>', new Context(''));

$this->assertInstanceOf(Iterable_::class, $resolvedType);
$this->assertSame('iterable<string|\stdClass|bool>', (string) $resolvedType);

/** @var Compound $valueType */
$valueType = $resolvedType->getValueType();

$this->assertInstanceOf(Compound::class, $valueType);

/** @var String_ $firstType */
$firstType = $valueType->get(0);

/** @var Object_ $secondType */
$secondType = $valueType->get(1);

/** @var Boolean $thirdType */
$thirdType = $valueType->get(2);

$this->assertInstanceOf(String_::class, $firstType);
$this->assertInstanceOf(Object_::class, $secondType);
$this->assertInstanceOf(Boolean::class, $thirdType);
}

/**
* This test asserts that the parameter order is correct.
*
Expand Down
43 changes: 43 additions & 0 deletions tests/unit/Types/ArrayTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/

namespace phpDocumentor\Reflection\Types;

use PHPUnit\Framework\TestCase;

/**
* @coversDefaultClass \phpDocumentor\Reflection\Types\Array_
*/
class ArrayTest extends TestCase
{
/**
* @covers ::__toString
*
* @dataProvider provideArrays
*/
public function testArrayStringifyCorrectly(Array_ $array, string $expectedString) : void
{
$this->assertSame($expectedString, (string) $array);
}

public function provideArrays() : array
{
return [
'simple array' => [new Array_(), 'array'],
'array of mixed' => [new Array_(new Mixed_()), 'array'],
'array of single type' => [new Array_(new String_()), 'string[]'],
'array of compound type' => [new Array_(new Compound([new Integer(), new String_()])), '(int|string)[]'],
'array with key type' => [new Array_(new String_(), new Integer()), 'array<int,string>'],
];
}
}
43 changes: 43 additions & 0 deletions tests/unit/Types/CollectionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/

namespace phpDocumentor\Reflection\Types;

use phpDocumentor\Reflection\Fqsen;
use PHPUnit\Framework\TestCase;

/**
* @coversDefaultClass \phpDocumentor\Reflection\Types\Collection
*/
class CollectionTest extends TestCase
{
/**
* @covers ::__toString
*
* @dataProvider provideCollections
*/
public function testCollectionStringifyCorrectly(Collection $collection, string $expectedString) : void
{
$this->assertSame($expectedString, (string) $collection);
}

public function provideCollections() : array
{
return [
'simple collection' => [new Collection(null, new Integer()), 'object<int>'],
'simple collection with key type' => [new Collection(null, new Integer(), new String_()), 'object<string,int>'],
'collection of single type using specific class' => [new Collection(new Fqsen('\Foo\Bar'), new Integer()), '\Foo\Bar<int>'],
'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>'],
];
}
}
43 changes: 43 additions & 0 deletions tests/unit/Types/IterableTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/

namespace phpDocumentor\Reflection\Types;

use PHPUnit\Framework\TestCase;

/**
* @coversDefaultClass \phpDocumentor\Reflection\Types\Iterable_
*/
class IterableTest extends TestCase
{
/**
* @covers ::__toString
*
* @dataProvider provideIterables
*/
public function testIterableStringifyCorrectly(Iterable_ $iterable, string $expectedString) : void
{
$this->assertSame($expectedString, (string) $iterable);
}

public function provideIterables() : array
{
return [
'simple iterable' => [new Iterable_(), 'iterable'],
'iterable of mixed' => [new Iterable_(new Mixed_()), 'iterable'],
'iterable of single type' => [new Iterable_(new String_()), 'iterable<string>'],
'iterable of compound type' => [new Iterable_(new Compound([new Integer(), new String_()])), 'iterable<int|string>'],
'iterable with key type' => [new Iterable_(new String_(), new Integer()), 'iterable<int,string>'],
];
}
}